Completed
Push — master ( e709af...8070d4 )
by Guillaume
02:45
created

Time   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 340
Duplicated Lines 25.88 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 24
Bugs 0 Features 1
Metric Value
wmc 49
c 24
b 0
f 1
lcom 1
cbo 2
dl 88
loc 340
rs 8.5454

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A deleteServiceTimer() 0 12 3
B deleteTimer() 0 24 5
A getProjectName() 0 15 3
A getProjects() 16 16 3
A getRecentTimers() 12 12 3
A getServiceDataCache() 0 11 2
A getTags() 12 16 3
A setNotificationForService() 0 8 2
C startTimer() 0 38 8
A startTimerWithDefaultOptions() 0 14 1
A stopRunningTimer() 19 19 4
A syncOnlineDataToLocalCache() 12 12 3
A undoTimer() 17 17 4
A getRecentServiceTimers() 0 4 1
A saveServiceDataCache() 0 5 1
A syncServiceOnlineDataToLocalCache() 0 12 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Time often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Time, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace AlfredTime;
4
5
use AlfredTime\Toggl;
6
use AlfredTime\Config;
7
use AlfredTime\Harvest;
8
9
class Time
10
{
11
    /**
12
     * @var mixed
13
     */
14
    private $config;
15
16
    /**
17
     * @var mixed
18
     */
19
    private $harvest;
20
21
    /**
22
     * @var mixed
23
     */
24
    private $toggl;
25
26
    /**
27
     * @param Config $config
28
     */
29
    public function __construct(Config $config = null)
30
    {
31
        $this->config = $config;
32
        $this->harvest = new Harvest($this->config->get('harvest', 'domain'), $this->config->get('harvest', 'api_token'));
33
        $this->toggl = new Toggl($this->config->get('toggl', 'api_token'));
34
    }
35
36
    /**
37
     * @param  $service
38
     * @param  $timerId
39
     * @return boolean
40
     */
41
    public function deleteServiceTimer($service, $timerId)
42
    {
43
        if ($this->$service->deleteTimer($timerId) === false) {
44
            return false;
45
        }
46
47
        if ($timerId === $this->config->get('workflow', 'timer_' . $service . '_id')) {
48
            $this->config->update('workflow', 'timer_' . $service . '_id', null);
49
        }
50
51
        return true;
52
    }
53
54
    /**
55
     * @param  $timerId
56
     * @return string
57
     */
58
    public function deleteTimer($timerId)
59
    {
60
        $message = '';
61
        $oneTimerDeleted = false;
62
63
        /**
64
         * Currently only for Toggl
65
         *
66
         * Would have to be changed if adding Harvest
67
         */
68
        $togglId = $this->config->get('workflow', 'timer_toggl_id');
69
70
        foreach ($this->config->implementedServicesForFeature('delete') as $service) {
71
            $res = $this->deleteServiceTimer($service, $timerId);
72
            $message .= $this->setNotificationForService($service, 'delete', $res);
73
            $oneTimerDeleted = $oneTimerDeleted || $res;
74
        }
75
76
        if ($oneTimerDeleted === true && ($timerId === $togglId)) {
77
            $this->config->update('workflow', 'is_timer_running', false);
78
        }
79
80
        return $message;
81
    }
82
83
    /**
84
     * @param  $projectId
85
     * @return mixed
86
     */
87
    public function getProjectName($projectId)
88
    {
89
        $projectName = '';
90
91
        $projects = $this->getProjects();
92
93
        foreach ($projects as $project) {
94
            if ($project['id'] === $projectId) {
95
                $projectName = $project['name'];
96
                break;
97
            }
98
        }
99
100
        return $projectName;
101
    }
102
103
    /**
104
     * @return mixed
105
     */
106 View Code Duplication
    public function getProjects()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
    {
108
        $projects = [];
109
110
        /**
111
         * Temporary, only get the projects of Toggl
112
         * Later, we will get Harvest ones too
113
         */
114
        foreach ($this->config->implementedServicesForFeature('get_projects') as $service) {
115
            if ($this->config->isServiceActive($service) === true) {
116
                $projects = $this->$service->getProjects($this->getServiceDataCache($service));
117
            }
118
        }
119
120
        return $projects;
121
    }
122
123
    /**
124
     * @return mixed
125
     */
126 View Code Duplication
    public function getRecentTimers()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
127
    {
128
        $timers = [];
129
130
        foreach ($this->config->implementedServicesForFeature('get_timers') as $service) {
131
            if ($this->config->isServiceActive($service) === true) {
132
                $timers = array_merge($timers, $this->getRecentServiceTimers($service));
133
            }
134
        }
135
136
        return $timers;
137
    }
138
139
    /**
140
     * @param  $service
141
     * @return mixed
142
     */
143
    public function getServiceDataCache($service)
144
    {
145
        $data = [];
146
        $cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json';
147
148
        if (file_exists($cacheFile)) {
149
            $data = json_decode(file_get_contents($cacheFile), true);
150
        }
151
152
        return $data;
153
    }
154
155
    /**
156
     * @return mixed
157
     */
158 View Code Duplication
    public function getTags()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
    {
160
        $tags = [];
161
162
        /**
163
         * Temporary, only get the tags of Toggl
164
         * Later, we will get Harvest ones too
165
         */
166
        foreach ($this->config->implementedServicesForFeature('get_tags') as $service) {
167
            if ($this->config->isServiceActive($service) === true) {
168
                $tags = $this->$service->getTags($this->getServiceDataCache($service));
169
            }
170
        }
171
172
        return $tags;
173
    }
174
175
    /**
176
     * @param $service
177
     * @param null       $action
178
     * @param null       $success
179
     */
180
    public function setNotificationForService($service = null, $action = null, $success = null)
181
    {
182
        if (empty($success) === true) {
183
            return '- ' . ucfirst($service) . ': cannot ' . $action . ' [' . $this->$service->getLastMessage() . ']' . "\r\n";
184
        }
185
186
        return '- ' . ucfirst($service) . ': ' . $action . "\r\n";
187
    }
188
189
    /**
190
     * @param  $description
191
     * @param  $projectsDefault
192
     * @param  null               $tagsDefault
193
     * @param  string             $startType
194
     * @return string
195
     */
196
    public function startTimer($description = '', array $projectsDefault = [], array $tagsDefault = [], $startType = 'start')
197
    {
198
        $message = '';
199
        $oneServiceStarted = false;
200
        $implementedServices = $this->config->implementedServicesForFeature($startType);
201
202
        /**
203
         * When starting a new timer, all the services timer IDs have to be put to null
204
         * so that when the user uses the UNDO feature, it doesn't delete old previous
205
         * other services timers. The timer IDs are used for the UNDO feature and
206
         * should then contain the IDs of the last starts through the workflow, not
207
         * through each individual sefrvice
208
         */
209
        if (empty($implementedServices) === true) {
210
            return '';
211
        }
212
213
        foreach ($this->config->activatedServices() as $service) {
214
            $this->config->update('workflow', 'timer_' . $service . '_id', null);
215
        }
216
217
        foreach ($implementedServices as $service) {
218
            $defaultProjectId = isset($projectsDefault[$service]) ? $projectsDefault[$service] : null;
219
            $defaultTags = isset($tagsDefault[$service]) ? $tagsDefault[$service] : null;
220
221
            $timerId = $this->$service->startTimer($description, $defaultProjectId, $defaultTags);
222
            $this->config->update('workflow', 'timer_' . $service . '_id', $timerId);
223
            $message .= $this->setNotificationForService($service, 'start', $timerId);
224
            $oneServiceStarted = $oneServiceStarted || ($timerId !== null);
225
        }
226
227
        if ($oneServiceStarted === true) {
228
            $this->config->update('workflow', 'timer_description', $description);
229
            $this->config->update('workflow', 'is_timer_running', true);
230
        }
231
232
        return $message;
233
    }
234
235
    /**
236
     * @param  $description
237
     * @return string
238
     */
239
    public function startTimerWithDefaultOptions($description)
240
    {
241
        $projectsDefault = [
242
            'toggl'   => $this->config->get('toggl', 'default_project_id'),
243
            'harvest' => $this->config->get('harvest', 'default_project_id'),
244
        ];
245
246
        $tagsDefault = [
247
            'toggl'   => $this->config->get('toggl', 'default_tags'),
248
            'harvest' => $this->config->get('harvest', 'default_task_id'),
249
        ];
250
251
        return $this->startTimer($description, $projectsDefault, $tagsDefault, 'start_default');
252
    }
253
254
    /**
255
     * @return string
256
     */
257 View Code Duplication
    public function stopRunningTimer()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
258
    {
259
        $message = '';
260
        $oneServiceStopped = false;
261
262
        foreach ($this->config->activatedServices() as $service) {
263
            $timerId = $this->config->get('workflow', 'timer_' . $service . '_id');
264
265
            $res = $this->$service->stopTimer($timerId);
266
            $message .= $this->setNotificationForService($service, 'stop', $res);
267
            $oneServiceStopped = $oneServiceStopped || $res;
268
        }
269
270
        if ($oneServiceStopped === true) {
271
            $this->config->update('workflow', 'is_timer_running', false);
272
        }
273
274
        return $message;
275
    }
276
277
    /**
278
     * @return string
279
     */
280 View Code Duplication
    public function syncOnlineDataToLocalCache()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
    {
282
        $message = '';
283
284
        foreach ($this->config->implementedServicesForFeature('sync_data') as $service) {
285
            if ($this->config->isServiceActive($service) === true) {
286
                $message .= $this->syncServiceOnlineDataToLocalCache($service);
287
            }
288
        }
289
290
        return $message;
291
    }
292
293
    /**
294
     * @return string
295
     */
296 View Code Duplication
    public function undoTimer()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
297
    {
298
        $message = '';
299
        $oneTimerDeleted = false;
300
301
        foreach ($this->config->servicesToUndo() as $service) {
302
            $res = $this->deleteServiceTimer($service, $this->config->get('workflow', 'timer_' . $service . '_id'));
303
            $message .= $this->setNotificationForService($service, 'undo', $res);
304
            $oneTimerDeleted = $oneTimerDeleted || $res;
305
        }
306
307
        if ($oneTimerDeleted === true) {
308
            $this->config->update('workflow', 'is_timer_running', false);
309
        }
310
311
        return $message;
312
    }
313
314
    /**
315
     * @return mixed
316
     */
317
    private function getRecentServiceTimers($service)
318
    {
319
        return $this->$service->getRecentTimers();
320
    }
321
322
    /**
323
     * @param $data
324
     * @param string  $service
325
     */
326
    private function saveServiceDataCache($service, $data)
327
    {
328
        $cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json';
329
        file_put_contents($cacheFile, json_encode($data));
330
    }
331
332
    /**
333
     * @param  string   $service
334
     * @return string
335
     */
336
    private function syncServiceOnlineDataToLocalCache($service)
337
    {
338
        $data = $this->$service->getOnlineData();
339
340
        if (empty($data) === true) {
341
            return $this->setNotificationForService($service, 'data', false);
342
        }
343
344
        $this->saveServiceDataCache($service, $data);
345
346
        return $this->setNotificationForService($service, 'data', true);
347
    }
348
}
349