Completed
Push — master ( 43a919...57d452 )
by Guillaume
02:08
created

Time   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 332
Duplicated Lines 21.08 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 24
Bugs 0 Features 1
Metric Value
wmc 46
c 24
b 0
f 1
lcom 1
cbo 2
dl 70
loc 332
rs 8.3999

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A deleteServiceTimer() 0 12 3
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 deleteTimer() 0 14 3
C startTimer() 0 43 8
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() 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
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
/*
153
 * Temporary, only get the tags of Toggl
154
 * Later, we will get Harvest ones too
155
 */
156
        foreach ($this->config->implementedServicesForFeature('get_tags') as $service) {
157
            if ($this->config->isServiceActive($service) === true) {
158
                $tags = $this->$service->getTags($this->getServiceDataCache($service));
159
            }
160
        }
161
162
        return $tags;
163
    }
164
165
    /**
166
     * @param  $description
167
     * @param  $projectsDefault
168
     * @param  null               $tagsDefault
169
     * @param  string             $startType
170
     * @return string
171
     */
172
    public function startTimer($description = '', array $projectsDefault = [], array $tagsDefault = [], $startType = 'start')
173
    {
174
        $message = '';
175
        $oneServiceStarted = false;
176
        $implementedServices = $this->config->implementedServicesForFeature($startType);
177
178
/*
179
 * When starting a new timer, all the services timer IDs have to be put to null
180
 * so that when the user uses the UNDO feature, it doesn't delete old previous
181
 * other services timers. The timer IDs are used for the UNDO feature and
182
 * should then contain the IDs of the last starts through the workflow, not
183
 * through each individual sefrvice
184
 */
185
        if (empty($implementedServices) === true) {
186
            return '';
187
        }
188
189
        foreach ($this->config->activatedServices() as $service) {
190
            $this->config->update('workflow', 'timer_' . $service . '_id', null);
191
        }
192
193
        foreach ($implementedServices as $service) {
194
            $defaultProjectId = isset($projectsDefault[$service]) ? $projectsDefault[$service] : null;
195
            $defaultTags = isset($tagsDefault[$service]) ? $tagsDefault[$service] : null;
196
197
            $timerId = $this->$service->startTimer($description, $defaultProjectId, $defaultTags);
198
            $this->config->update('workflow', 'timer_' . $service . '_id', $timerId);
199
200
            if ($timerId !== null) {
201
                $oneServiceStarted = true;
202
                $message .= '- ' . ucfirst($service) . ': started' . "\r\n";
203
            } else {
204
                $message .= '- ' . ucfirst($service) . ': cannot start' . "\r\n";
205
            }
206
        }
207
208
        if ($oneServiceStarted === true) {
209
            $this->config->update('workflow', 'timer_description', $description);
210
            $this->config->update('workflow', 'is_timer_running', true);
211
        }
212
213
        return $message;
214
    }
215
216
    /**
217
     * @param  $description
218
     * @return string
219
     */
220
    public function startTimerWithDefaultOptions($description)
221
    {
222
        $projectsDefault = [
223
            'toggl'   => $this->config->get('toggl', 'default_project_id'),
224
            'harvest' => $this->config->get('harvest', 'default_project_id'),
225
        ];
226
227
        $tagsDefault = [
228
            'toggl'   => $this->config->get('toggl', 'default_tags'),
229
            'harvest' => $this->config->get('harvest', 'default_task_id'),
230
        ];
231
232
        return $this->startTimer($description, $projectsDefault, $tagsDefault, 'start_default');
233
    }
234
235
    /**
236
     * @return string
237
     */
238
    public function stopRunningTimer()
239
    {
240
        $message = '';
241
        $oneServiceStopped = false;
242
243 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...
244
            $timerId = $this->config->get('workflow', 'timer_' . $service . '_id');
245
246
            if ($this->$service->stopTimer($timerId) === true) {
247
                $oneServiceStopped = true;
248
                $message .= '- ' . ucfirst($service) . ': stopped' . "\r\n";
249
            } else {
250
                $message .= '- ' . ucfirst($service) . ': cannot stop' . "\r\n";
251
            }
252
        }
253
254
        if ($oneServiceStopped === true) {
255
            $this->config->update('workflow', 'is_timer_running', false);
256
        }
257
258
        return $message;
259
    }
260
261
    /**
262
     * @return string
263
     */
264 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...
265
    {
266
        $message = '';
267
268
        foreach ($this->config->implementedServicesForFeature('sync_data') as $service) {
269
            if ($this->config->isServiceActive($service) === true) {
270
                $message .= $this->syncServiceOnlineDataToLocalCache($service);
271
            }
272
        }
273
274
        return $message;
275
    }
276
277
    /**
278
     * @return string
279
     */
280
    public function undoTimer()
281
    {
282
        $message = '';
283
284
        if ($this->config->hasTimerRunning() === true) {
285
            $this->stopRunningTimer();
286
        }
287
288
        $oneTimerDeleted = false;
289
290 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...
291
            if ($this->deleteServiceTimer($service, $this->config->get('workflow', 'timer_' . $service . '_id')) === true) {
292
                $oneTimerDeleted = true;
293
                $message .= '- ' . ucfirst($service) . ': undid' . "\r\n";
294
            } else {
295
                $message .= '- ' . ucfirst($service) . ': cannot undo' . "\r\n";
296
            }
297
        }
298
299
        if ($oneTimerDeleted === true) {
300
            $this->config->update('workflow', 'is_timer_running', false);
301
        }
302
303
        return $message;
304
    }
305
306
    /**
307
     * @return mixed
308
     */
309
    private function getRecentServiceTimers($service)
310
    {
311
        return $this->$service->getRecentTimers();
312
    }
313
314
    /**
315
     * @param $data
316
     * @param string  $service
317
     */
318
    private function saveServiceDataCache($service, $data)
319
    {
320
        $cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json';
321
        file_put_contents($cacheFile, json_encode($data));
322
    }
323
324
    /**
325
     * @param  string   $service
326
     * @return string
327
     */
328
    private function syncServiceOnlineDataToLocalCache($service)
329
    {
330
        $data = $this->$service->getOnlineData();
331
332
        if (empty($data) === true) {
333
            return '- ' . ucfirst($service) . ': cannot cache data' . "\r\n";
334
        }
335
336
        $this->saveServiceDataCache($service, $data);
337
338
        return '- ' . ucfirst($service) . ': data cached' . "\r\n";
339
    }
340
}
341