Completed
Push — master ( 0176bc...60bcf6 )
by Guillaume
02:09
created

Time   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 345
Duplicated Lines 28.41 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 21
Bugs 0 Features 1
Metric Value
wmc 50
c 21
b 0
f 1
lcom 1
cbo 2
dl 98
loc 345
rs 8.6206

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A deleteServiceTimer() 0 12 3
A deleteTimer() 14 14 3
A getProjectName() 0 15 3
A getProjects() 16 16 3
A getRecentTimers() 12 12 3
A getServiceDataCache() 0 11 2
A getTags() 12 12 3
A getTimerDescription() 0 4 1
A hasTimerRunning() 0 4 2
D startTimer() 0 42 9
A startTimerWithDefaultOptions() 0 14 1
B stopRunningTimer() 10 22 4
A syncOnlineDataToLocalCache() 12 12 3
B undoTimer() 8 25 5
A getRecentServiceTimers() 0 4 1
A saveServiceDataCache() 0 5 1
A syncServiceOnlineDataToLocalCache() 14 14 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 View Code Duplication
    public function deleteTimer($timerId)
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...
59
    {
60
        $message = '';
61
62
        foreach ($this->config->implementedServicesForFeature('delete') as $service) {
63
            if ($this->deleteServiceTimer($service, $timerId) === true) {
64
                $message .= '- ' . ucfirst($service) . ': deleted' . "\r\n";
65
            } else {
66
                $message .= '- ' . ucfirst($service) . ': cannot delete' . "\r\n";
67
            }
68
        }
69
70
        return $message;
71
    }
72
73
    /**
74
     * @param  $projectId
75
     * @return mixed
76
     */
77
    public function getProjectName($projectId)
78
    {
79
        $projectName = '';
80
81
        $projects = $this->getProjects();
82
83
        foreach ($projects as $project) {
84
            if ($project['id'] === $projectId) {
85
                $projectName = $project['name'];
86
                break;
87
            }
88
        }
89
90
        return $projectName;
91
    }
92
93
    /**
94
     * @return mixed
95
     */
96 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...
97
    {
98
        $projects = [];
99
100
/*
101
 * Temporary, only get the projects of Toggl
102
 * Later, we will get Harvest ones too
103
 */
104
        foreach ($this->config->implementedServicesForFeature('get_projects') as $service) {
105
            if ($this->config->isServiceActive($service) === true) {
106
                $projects = $this->$service->getProjects($this->getServiceDataCache($service));
107
            }
108
        }
109
110
        return $projects;
111
    }
112
113
    /**
114
     * @return mixed
115
     */
116 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...
117
    {
118
        $timers = [];
119
120
        foreach ($this->config->implementedServicesForFeature('get_timers') as $service) {
121
            if ($this->config->isServiceActive($service) === true) {
122
                $timers = array_merge($timers, $this->getRecentServiceTimers($service));
123
            }
124
        }
125
126
        return $timers;
127
    }
128
129
    /**
130
     * @param  $service
131
     * @return mixed
132
     */
133
    public function getServiceDataCache($service)
134
    {
135
        $data = [];
136
        $cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json';
137
138
        if (file_exists($cacheFile)) {
139
            $data = json_decode(file_get_contents($cacheFile), true);
140
        }
141
142
        return $data;
143
    }
144
145
    /**
146
     * @return mixed
147
     */
148 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...
149
    {
150
        $tags = [];
151
152
        foreach ($this->config->implementedServicesForFeature('get_tags') as $service) {
153
            if ($this->config->isServiceActive($service) === true) {
154
                $tags = $this->$service->getTags($this->getServiceDataCache($service));
155
            }
156
        }
157
158
        return $tags;
159
    }
160
161
    /**
162
     * @return mixed
163
     */
164
    public function getTimerDescription()
165
    {
166
        return $this->config->get('workflow', 'timer_description');
167
    }
168
169
    /**
170
     * @return boolean
171
     */
172
    public function hasTimerRunning()
173
    {
174
        return $this->config->get('workflow', 'is_timer_running') === false ? false : true;
175
    }
176
177
    /**
178
     * @param  $description
179
     * @param  $projectsDefault
180
     * @param  null               $tagsDefault
181
     * @param  boolean            $startDefault
182
     * @return string
183
     */
184
    public function startTimer($description = '', $projectsDefault = null, $tagsDefault = null, $startDefault = false)
185
    {
186
        $message = '';
187
        $startType = $startDefault === true ? 'start_default' : 'start';
188
        $atLeastOneServiceStarted = false;
189
        $implementedServices = $this->config->implementedServicesForFeature($startType);
190
191
/*
192
 * When starting a new timer, all the services timer IDs have to be put to null
193
 * so that when the user uses the UNDO feature, it doesn't delete old previous
194
 * other services timers. The timer IDs are used for the UNDO feature and
195
 * should then contain the IDs of the last starts through the workflow, not
196
 * through each individual sefrvice
197
 */
198
        if (empty($implementedServices) === false) {
199
            foreach ($this->config->activatedServices() as $service) {
200
                $this->config->update('workflow', 'timer_' . $service . '_id', null);
201
            }
202
203
            foreach ($implementedServices as $service) {
204
                $defaultProjectId = isset($projectsDefault[$service]) ? $projectsDefault[$service] : null;
205
                $defaultTags = isset($tagsDefault[$service]) ? $tagsDefault[$service] : null;
206
207
                $timerId = $this->$service->startTimer($description, $defaultProjectId, $defaultTags);
208
                $this->config->update('workflow', 'timer_' . $service . '_id', $timerId);
209
210
                if ($timerId !== null) {
211
                    $atLeastOneServiceStarted = true;
212
                    $message .= '- ' . ucfirst($service) . ': started' . "\r\n";
213
                } else {
214
                    $message .= '- ' . ucfirst($service) . ': cannot start' . "\r\n";
215
                }
216
            }
217
        }
218
219
        if ($atLeastOneServiceStarted === true) {
220
            $this->config->update('workflow', 'timer_description', $description);
221
            $this->config->update('workflow', 'is_timer_running', true);
222
        }
223
224
        return $message;
225
    }
226
227
    /**
228
     * @param  $description
229
     * @return string
230
     */
231
    public function startTimerWithDefaultOptions($description)
232
    {
233
        $projectsDefault = [
234
            'toggl'   => $this->config->get('toggl', 'default_project_id'),
235
            'harvest' => $this->config->get('harvest', 'default_project_id'),
236
        ];
237
238
        $tagsDefault = [
239
            'toggl'   => $this->config->get('toggl', 'default_tags'),
240
            'harvest' => $this->config->get('harvest', 'default_task_id'),
241
        ];
242
243
        return $this->startTimer($description, $projectsDefault, $tagsDefault, true);
244
    }
245
246
    /**
247
     * @return string
248
     */
249
    public function stopRunningTimer()
250
    {
251
        $message = '';
252
        $atLeastOneServiceStopped = false;
253
254 View Code Duplication
        foreach ($this->config->activatedServices() as $service) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
255
            $timerId = $this->config->get('workflow', 'timer_' . $service . '_id');
256
257
            if ($this->$service->stopTimer($timerId) === true) {
258
                $atLeastOneServiceStopped = true;
259
                $message .= '- ' . ucfirst($service) . ': stopped' . "\r\n";
260
            } else {
261
                $message .= '- ' . ucfirst($service) . ': cannot stop' . "\r\n";
262
            }
263
        }
264
265
        if ($atLeastOneServiceStopped === true) {
266
            $this->config->update('workflow', 'is_timer_running', false);
267
        }
268
269
        return $message;
270
    }
271
272
    /**
273
     * @return string
274
     */
275 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...
276
    {
277
        $message = '';
278
279
        foreach ($this->config->implementedServicesForFeature('sync_data') as $service) {
280
            if ($this->config->isServiceActive($service) === true) {
281
                $message .= $this->syncServiceOnlineDataToLocalCache($service);
282
            }
283
        }
284
285
        return $message;
286
    }
287
288
    /**
289
     * @return string
290
     */
291
    public function undoTimer()
292
    {
293
        $message = '';
294
295
        if ($this->hasTimerRunning() === true) {
296
            $this->stopRunningTimer();
297
        }
298
299
        $atLeastOneTimerDeleted = false;
300
301 View Code Duplication
        foreach ($this->config->servicesToUndo() as $service) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
302
            if ($this->deleteServiceTimer($service, $this->config->get('workflow', 'timer_' . $service . '_id')) === true) {
303
                $atLeastOneTimerDeleted = true;
304
                $message .= '- ' . ucfirst($service) . ': undid' . "\r\n";
305
            } else {
306
                $message .= '- ' . ucfirst($service) . ': cannot undo' . "\r\n";
307
            }
308
        }
309
310
        if ($atLeastOneTimerDeleted === true) {
311
            $this->config->update('workflow', 'is_timer_running', false);
312
        }
313
314
        return $message;
315
    }
316
317
    /**
318
     * @return mixed
319
     */
320
    private function getRecentServiceTimers($service)
321
    {
322
        return $this->$service->getRecentTimers();
323
    }
324
325
    /**
326
     * @param $data
327
     * @param string  $service
328
     */
329
    private function saveServiceDataCache($service, $data)
330
    {
331
        $cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json';
332
        file_put_contents($cacheFile, json_encode($data));
333
    }
334
335
    /**
336
     * @param  string   $service
337
     * @return string
338
     */
339 View Code Duplication
    private function syncServiceOnlineDataToLocalCache($service)
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...
340
    {
341
        $message = '';
342
        $data = $this->$service->getOnlineData();
343
344
        if (empty($data) === false) {
345
            $this->saveServiceDataCache($service, $data);
346
            $message .= '- ' . ucfirst($service) . ': data cached' . "\r\n";
347
        } else {
348
            $message .= '- ' . ucfirst($service) . ': cannot cache data' . "\r\n";
349
        }
350
351
        return $message;
352
    }
353
}
354