Completed
Push — master ( 386cc0...5fe2ea )
by Matthew
05:36
created

TrendsController::getDateFormat()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 0
cts 15
cp 0
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 12
nc 5
nop 1
crap 30
1
<?php
2
3
namespace Dtc\QueueBundle\Controller;
4
5
use Doctrine\ODM\MongoDB\DocumentManager;
6
use Doctrine\ORM\EntityManager;
7
use Dtc\QueueBundle\Doctrine\BaseJobTimingManager;
8
use Dtc\QueueBundle\Model\JobTiming;
9
use Dtc\QueueBundle\ODM\JobManager;
10
use Dtc\QueueBundle\ODM\JobTimingManager;
11
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
12
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
13
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
14
use Symfony\Component\HttpFoundation\JsonResponse;
15
use Symfony\Component\HttpFoundation\Request;
16
17
class TrendsController extends Controller
18
{
19
    use ControllerTrait;
20
21
    /**
22
     * Show a graph of job trends.
23
     *
24
     * @Route("/trends", name="dtc_queue_trends")
25
     * @Template("@DtcQueue/Queue/trends.html.twig")
26
     */
27
    public function trendsAction()
28
    {
29
        $recordTimings = $this->container->getParameter('dtc_queue.record_timings');
30
        $params = ['record_timings' => $recordTimings, 'states' => JobTiming::getStates()];
31
        $this->addCssJs($params);
32
33
        return $params;
34
    }
35
36
    /**
37
     * @Route("/timings", name="dtc_queue_timings")
38
     *
39
     * @param Request $request
0 ignored issues
show
Bug introduced by
There is no parameter named $request. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
40
     */
41
    public function getTimingsAction()
42
    {
43
        $request = $this->get('request_stack')->getMasterRequest();
44
        $begin = $request->query->get('begin');
45
        $end = $request->query->get('end');
46
        $type = $request->query->get('type', 'HOUR');
47
        $beginDate = \DateTime::createFromFormat('Y-m-d\TH:i:s.uO', $begin) ?: null;
48
        $endDate = \DateTime::createFromFormat('Y-m-d\TH:i:s.uO', $end) ?: new \DateTime();
49
50
        $recordTimings = $this->container->getParameter('dtc_queue.record_timings');
51
        $params = [];
52
        if ($recordTimings) {
53
            $params = $this->calculateTimings($type, $beginDate, $endDate);
54
        }
55
56
        return new JsonResponse($params);
57
    }
58
59
    protected function calculateTimings($type, $beginDate, $endDate)
60
    {
61
        $params = [];
62
        $this->validateJobTimingManager();
63
64
        /** @var BaseJobTimingManager $jobTimingManager */
65
        $jobTimingManager = $this->get('dtc_queue.job_timing_manager');
66
        if ($jobTimingManager instanceof JobTimingManager) {
67
            $timings = $this->getJobTimingsOdm($type, $endDate, $beginDate);
68
        } else {
69
            $timings = $this->getJobTimingsOrm($type, $endDate, $beginDate);
70
        }
71
72
        $timingStates = JobTiming::getStates();
73
        $timingsDates = [];
74
        foreach (array_keys($timingStates) as $state) {
75
            if (!isset($timings[$state])) {
76
                continue;
77
            }
78
            $timingsData = $timings[$state];
79
            $timingsDates = array_unique(array_merge(array_keys($timingsData), $timingsDates));
80
        }
81
82
        $format = $this->getDateFormat($type);
83
        usort($timingsDates, function ($date1str, $date2str) use ($format) {
84
            $date1 = \DateTime::createFromFormat($format, $date1str);
85
            $date2 = \DateTime::createFromFormat($format, $date2str);
86
            if (!$date2) {
87
                return false;
88
            }
89
            if (!$date1) {
90
                return false;
91
            }
92
93
            return $date1 > $date2;
94
        });
95
96
        $timezoneOffset = $this->container->getParameter('dtc_queue.record_timings_timezone_offset');
97
        $timingsDatesAdjusted = [];
98
        foreach ($timingsDates as $dateStr) {
99
            $date = \DateTime::createFromFormat($format, $dateStr);
100
            if (0 !== $timezoneOffset) {
101
                $date->setTimestamp($date->getTimestamp() + ($timezoneOffset * 3600));
102
            }
103
            $timingsDatesAdjusted[] = $date->format(DATE_RFC3339);
104
        }
105
106
        foreach (array_keys($timingStates) as $state) {
107
            if (!isset($timings[$state])) {
108
                continue;
109
            }
110
111
            $timingsData = $timings[$state];
112
            foreach ($timingsDates as $date) {
113
                $params['timings_data_'.$state][] = isset($timingsData[$date]) ? $timingsData[$date] : 0;
114
            }
115
        }
116
        $params['timings_dates'] = $timingsDates;
117
        $params['timings_dates_rfc3339'] = $timingsDatesAdjusted;
118
119
        return $params;
120
    }
121
122
    protected function getJobTimingsOdm($type, \DateTime $end, \DateTime $begin = null)
123
    {
124
        /** @var JobTimingManager $runManager */
125
        $jobTimingManager = $this->get('dtc_queue.job_timing_manager');
126
        $jobTimingClass = $jobTimingManager->getJobTimingClass();
127
128
        /** @var DocumentManager $documentManager */
129
        $documentManager = $jobTimingManager->getObjectManager();
130
131
        $regexInfo = $this->getRegexDate($type);
132
        if (!$begin) {
133
            $begin = clone $end;
134
            $begin->sub($regexInfo['interval']);
135
        }
136
137
        // Run a map reduce function get worker and status break down
138
        $mapFunc = "function() {
139
            var dateStr = this.finishedAt.toISOString();
140
            dateStr = dateStr.replace(/{$regexInfo['regex']}/,'{$regexInfo['replacement']}');
141
            var dateBegin = new Date('{$begin->format('c')}');
142
            var dateEnd = new Date('{$end->format('c')}');
143
            if (this.finishedAt >= dateBegin && this.finishedAt <= dateEnd) {
144
                var result = {};
145
                result[dateStr] = 1;
146
                emit(this.status, result);
147
            }
148
        }";
149
        $reduceFunc = JobManager::REDUCE_FUNCTION;
150
        $builder = $documentManager->createQueryBuilder($jobTimingClass);
151
        $builder->map($mapFunc)
152
            ->reduce($reduceFunc);
153
        $query = $builder->getQuery();
154
        $results = $query->execute();
155
        $resultHash = [];
156
        foreach ($results as $info) {
157
            $resultHash[$info['_id']] = $info['value'];
158
        }
159
160
        return $resultHash;
161
    }
162
163
    protected function getDateFormat($type)
164
    {
165
        switch ($type) {
166
            case 'YEAR':
167
                return 'Y';
168
            case 'MONTH':
169
                return 'Y-m';
170
            case 'DAY':
171
                return 'Y-m-d';
172
            case 'HOUR':
173
                return 'Y-m-d H';
174
            default:
175
                throw new \InvalidArgumentException("Invalid date format type '$type''");
176
        }
177
    }
178
179
    protected function getRegexDate($type)
180
    {
181
        switch ($type) {
182 View Code Duplication
            case 'YEAR':
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...
183
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1'],
184
                    'interval' => new \DateInterval('P10Y'), ];
185 View Code Duplication
            case 'MONTH':
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...
186
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2'],
187
                    'interval' => new \DateInterval('P12M'), ];
188 View Code Duplication
            case 'DAY':
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...
189
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3'],
190
                    'interval' => new \DateInterval('P31D'), ];
191 View Code Duplication
            case 'HOUR':
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...
192
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3 $4'],
193
                    'interval' => new \DateInterval('PT24H'), ];
194 View Code Duplication
            case 'MINUTE':
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...
195
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3 $4:$5'],
196
                    'interval' => new \DateInterval('PT3600S'), ];
197
        }
198
        throw new \InvalidArgumentException("Invalid type $type");
199
    }
200
201
    protected function getOrmGroupBy($type)
202
    {
203
        switch ($type) {
204
            case 'YEAR':
205
                return ['groupby' => 'YEAR(j.finishedAt)',
206
                    'interval' => new \DateInterval('P10Y'), ];
207
            case 'MONTH':
208
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt))',
209
                    'interval' => new \DateInterval('P12M'), ];
210
            case 'DAY':
211
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt))',
212
                    'interval' => new \DateInterval('P31D'), ];
213
            case 'HOUR':
214
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt),\' \',HOUR(j.finishedAt))',
215
                    'interval' => new \DateInterval('PT24H'), ];
216
            case 'MINUTE':
217
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt),\' \',HOUR(j.finishedAt),\':\',MINUTE(j.finishedAt))',
218
                    'interval' => new \DateInterval('PT3600S'), ];
219
        }
220
        throw new \InvalidArgumentException("Invalid type $type");
221
    }
222
223
    protected function getJobTimingsOrm($type, \DateTime $end, \DateTime $begin = null)
224
    {
225
        /** @var JobTimingManager $jobTimingManager */
226
        $jobTimingManager = $this->get('dtc_queue.job_timing_manager');
227
        $jobTimingClass = $jobTimingManager->getJobTimingClass();
228
        /** @var EntityManager $entityManager */
229
        $entityManager = $jobTimingManager->getObjectManager();
230
231
        $groupByInfo = $this->getOrmGroupBy($type);
232
233
        if (!$begin) {
234
            $begin = clone $end;
235
            $begin->sub($groupByInfo['interval']);
236
        }
237
238
        $queryBuilder = $entityManager->createQueryBuilder()->select("j.status as status, count(j.finishedAt) as thecount, {$groupByInfo['groupby']} as thedate")
239
            ->from($jobTimingClass, 'j')
240
            ->where('j.finishedAt <= :end')
241
            ->andWhere('j.finishedAt >= :begin')
242
            ->setParameter(':end', $end)
243
            ->setParameter(':begin', $begin)
244
            ->groupBy('status')
245
            ->addGroupBy('thedate');
246
247
        $result = $queryBuilder
248
            ->getQuery()->getArrayResult();
249
250
        $resultHash = [];
251
        foreach ($result as $row) {
252
            $resultHash[$row['status']][$row['thedate']] = intval($row['thecount']);
253
        }
254
255
        return $resultHash;
256
    }
257
258 View Code Duplication
    protected function validateJobTimingManager()
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...
259
    {
260
        if ($this->container->hasParameter('dtc_queue.job_timing_manager')) {
261
            $this->validateManagerType('dtc_queue.job_timing_manager');
262
        } elseif ($this->container->hasParameter('dtc_queue.job_timing_manager')) {
263
            $this->validateManagerType('dtc_queue.run_manager');
264
        } else {
265
            $this->validateManagerType('dtc_queue.default_manager');
266
        }
267
    }
268
}
269