StatisticsController::intervals()   B
last analyzed

Complexity

Conditions 9
Paths 11

Size

Total Lines 61
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 44
c 1
b 0
f 0
nc 11
nop 1
dl 0
loc 61
rs 7.6604

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * BEdita, API-first content management framework
6
 * Copyright 2024 Atlas Srl, Chialab Srl
7
 *
8
 * This file is part of BEdita: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published
10
 * by the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
14
 */
15
namespace App\Controller\Admin;
16
17
use App\Controller\Model\ModelBaseController;
18
use App\Utility\CacheTools;
19
use BEdita\SDK\BEditaClientException;
20
use Cake\Cache\Cache;
21
use Cake\Http\Response;
22
use Cake\I18n\FrozenDate;
23
use Cake\Utility\Hash;
24
use Cake\Utility\Inflector;
25
26
/**
27
 * Statistics Controller
28
 */
29
class StatisticsController extends ModelBaseController
30
{
31
    /**
32
     * Resource type currently used
33
     *
34
     * @var string
35
     */
36
    protected $resourceType = 'object_types';
37
38
    /**
39
     * @inheritDoc
40
     */
41
    public function index(): ?Response
42
    {
43
        if ($this->getRequest()->getQuery('year') !== null) {
44
            return $this->fetch();
45
        }
46
        parent::index();
47
        $resources = $this->viewBuilder()->getVar('resources');
48
        $resources = (array)Hash::extract($resources, '{n}.attributes.name');
49
        $modules = $this->viewBuilder()->getVar('modules');
50
        $data = [];
51
        foreach ($resources as $objectType) {
52
            $color = $modules[$objectType]['color'] ?? '#123123';
53
            $label = $modules[$objectType]['label'] ?? Inflector::humanize($objectType);
54
            $shortLabel = $modules[$objectType]['shortLabel'] ?? substr($label, 0, 3);
55
            $data[$objectType] = [
56
                'color' => $color,
57
                'label' => $label,
58
                'shortLabel' => $shortLabel,
59
            ];
60
        }
61
62
        $this->set('resources', $data);
63
64
        return null;
65
    }
66
67
    /**
68
     * Fetch counts for a specific object type and interval
69
     *
70
     * @return \Cake\Http\Response|null
71
     */
72
    protected function fetch(): ?Response
73
    {
74
        $objectType = $this->getRequest()->getQuery('objectType');
75
        $year = $this->getRequest()->getQuery('year');
76
        $month = $this->getRequest()->getQuery('month');
77
        $week = $this->getRequest()->getQuery('week');
78
        $day = $this->getRequest()->getQuery('day');
79
        $params = compact('year', 'month', 'week', 'day');
80
        $data = [];
81
        $intervals = $this->intervals($params);
82
        foreach ($intervals as $item) {
83
            $data[] = $this->fetchCount($objectType, $item['start'], $item['end']);
84
        }
85
        $this->viewBuilder()->setClassName('Json');
86
        $this->set('data', $data);
87
        $this->setSerialize(['data']);
88
89
        return null;
90
    }
91
92
    /**
93
     * Fetch count for a specific object type and interval
94
     *
95
     * @param string $objectType Object type name
96
     * @param string $from Start date
97
     * @param string $to End date
98
     * @return int
99
     */
100
    protected function fetchCount(string $objectType, string $from, string $to): int
101
    {
102
        // if from is in the future, return 0
103
        if (new FrozenDate($from) > new FrozenDate('today')) {
104
            return 0;
105
        }
106
        $key = CacheTools::cacheKey(sprintf('statistics-%s-%s-%s', $objectType, $from, $to));
107
        try {
108
            $count = Cache::remember(
109
                $key,
110
                function () use ($objectType, $from, $to) {
111
                    $response = $this->apiClient->get(
112
                        '/objects',
113
                        [
114
                            'filter' => [
115
                                'type' => $objectType,
116
                                'created' => [
117
                                    'gte' => $from,
118
                                    'lte' => $to,
119
                                ],
120
                            ],
121
                            'page' => 1,
122
                            'page_size' => 1,
123
                        ]
124
                    );
125
126
                    return (int)Hash::get($response, 'meta.pagination.count', 0);
127
                }
128
            );
129
        } catch (BEditaClientException $e) {
130
            $count = 0;
131
        }
132
133
        return $count;
134
    }
135
136
    /**
137
     * Get intervals for a specific interval
138
     *
139
     * @param array $params Interval parameters
140
     * @return array
141
     */
142
    protected function intervals(array $params): array
143
    {
144
        $year = Hash::get($params, 'year');
145
        $month = Hash::get($params, 'month');
146
        $week = Hash::get($params, 'week');
147
        $day = Hash::get($params, 'day');
148
        // case day: return interval with just one day
149
        if ($day !== null) {
150
            $start = new FrozenDate($day);
151
            $end = $start->addDays(1);
152
153
            return [['start' => $start->format('Y-m-d'), 'end' => $end->format('Y-m-d')]];
154
        }
155
        // case week: return interval with all days of the week, ~ 7 days
156
        if ($week !== null) {
157
            $firstWeek = intval($week);
158
            $lastWeek = intval($week);
159
            $start = new FrozenDate(sprintf('first day of %s', $month));
160
            $start = $start->addWeeks($firstWeek - 1);
161
            $end = $start->addWeeks(1)->subDays(1);
162
            $intervals = [];
163
            while ($start->lessThanOrEquals($end)) {
164
                $next = $start->addDays(1);
165
                $intervals[] = ['start' => $start->format('Y-m-d'), 'end' => $next->format('Y-m-d')];
166
                $start = $next;
167
            }
168
169
            return $intervals;
170
        }
171
        // case month: return interval with 4/5 weeks
172
        if ($month !== null) {
173
            $firstWeek = 1;
174
            $defaultLastWeek = $month === 'february' ? 4 : 5;
175
            $lastWeek = $defaultLastWeek;
176
            $start = new FrozenDate(sprintf('first day of %s %s', $month, $year));
177
            $start = $start->addWeeks($firstWeek - 1);
178
            $end = $start->addWeeks($lastWeek)->subDays(1);
179
            $intervals = [];
180
            while ($start->lessThanOrEquals($end)) {
181
                $next = $start->addWeeks(1);
182
                if ($next->format('m') !== $start->format('m')) {
183
                    $end = new FrozenDate(sprintf('last day of %s %s', $month, $year));
184
                    $next = $end->addDays(1);
185
                }
186
                $intervals[] = ['start' => $start->format('Y-m-d'), 'end' => $next->format('Y-m-d')];
187
                $start = $next;
188
            }
189
190
            return $intervals;
191
        }
192
        // case year: return interval with 12 months
193
        $start = new FrozenDate(sprintf('first day of january %s', $year));
194
        $end = new FrozenDate(sprintf('last day of december %s', $year));
195
        $intervals = [];
196
        while ($start->lessThanOrEquals($end)) {
197
            $next = $start->addMonths(1);
198
            $intervals[] = ['start' => $start->format('Y-m-d'), 'end' => $next->format('Y-m-d')];
199
            $start = $next;
200
        }
201
202
        return $intervals;
203
    }
204
}
205