Completed
Push — master ( 9fa9e8...74ff85 )
by Matthew
04:52
created

QueueController::trendsAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 0
cts 7
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
crap 2
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\BaseRunManager;
8
use Dtc\QueueBundle\Exception\UnsupportedException;
9
use Dtc\QueueBundle\Model\Worker;
10
use Dtc\QueueBundle\ODM\RunManager;
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 Zend\Stdlib\Request;
16
17
class QueueController extends Controller
18
{
19
    /**
20
     * Summary stats.
21
     *
22
     * @Route("/")
23
     * @Route("/status/")
24
     * @Template()
25
     */
26
    public function statusAction()
27
    {
28
        $params = array();
29
        $jobManager = $this->get('dtc_queue.job_manager');
30
31
        $params['status'] = $jobManager->getStatus();
32
        $this->addCssJs($params);
33
34
        return $params;
35
    }
36
37
    /**
38
     * List jobs in system by default.
39
     *
40
     * @Route("/jobs_all", name="dtc_queue_jobs_all")
41
     */
42 View Code Duplication
    public function jobsAllAction()
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...
43
    {
44
        $this->validateManagerType('dtc_queue.default_manager');
45
        $class1 = $this->container->getParameter('dtc_queue.class_job');
46
        $class2 = $this->container->getParameter('dtc_queue.class_job_archive');
47
        $label1 = 'Non-Archived Jobs';
48
        $label2 = 'Archived Jobs';
49
50
        $params = $this->getDualGridParams($class1, $class2, $label1, $label2);
51
52
        return $this->render('@DtcQueue/Queue/grid.html.twig', $params);
53
    }
54
55
    /**
56
     * List jobs in system by default.
57
     *
58
     * @Template()
59
     * @Route("/jobs", name="dtc_queue_jobs")
60
     */
61
    public function jobsAction()
62
    {
63
        $this->validateManagerType('dtc_queue.default_manager');
64
        $managerType = $this->container->getParameter('dtc_queue.default_manager');
65
        $rendererFactory = $this->get('dtc_grid.renderer.factory');
66
        $renderer = $rendererFactory->create('datatables');
67
        $gridSource = $this->get('dtc_queue.grid_source.live_jobs.'.('mongodb' === $managerType ? 'odm' : $managerType));
68
        $renderer->bind($gridSource);
69
        $params = $renderer->getParams();
70
        $this->addCssJs($params);
71
72
        return $params;
73
    }
74
75
    protected function validateManagerType($type)
76
    {
77
        $managerType = $this->container->getParameter($type);
78
        if ('mongodb' !== $managerType && 'orm' != $managerType && 'odm' != $managerType) {
79
            throw new UnsupportedException("Unsupported manager type: $managerType");
80
        }
81
    }
82
83
    /**
84
     * @param string $class1
85
     * @param string $class2
86
     * @param string $label1
87
     * @param string $label2
88
     *
89
     * @return \Symfony\Component\HttpFoundation\Response
90
     *
91
     * @throws \Exception
92
     */
93
    protected function getDualGridParams($class1, $class2, $label1, $label2)
94
    {
95
        $rendererFactory = $this->get('dtc_grid.renderer.factory');
96
        $renderer = $rendererFactory->create('datatables');
97
        $gridSource = $this->get('dtc_grid.manager.source')->get($class1);
98
        $renderer->bind($gridSource);
99
        $params = $renderer->getParams();
100
101
        $renderer2 = $rendererFactory->create('datatables');
102
        $gridSource = $this->get('dtc_grid.manager.source')->get($class2);
103
        $renderer2->bind($gridSource);
104
        $params2 = $renderer2->getParams();
105
106
        $params['archive_grid'] = $params2['dtc_grid'];
107
108
        $params['dtc_queue_grid_label1'] = $label1;
109
        $params['dtc_queue_grid_label2'] = $label2;
110
        $this->addCssJs($params);
111
112
        return $params;
113
    }
114
115
    protected function addCssJs(array &$params)
116
    {
117
        $params['css'] = $this->container->getParameter('dtc_grid.theme.css');
118
        $params['js'] = $this->container->getParameter('dtc_grid.theme.js');
119
        $jQuery = $this->container->getParameter('dtc_grid.jquery');
120
        array_unshift($params['js'], $jQuery['url']);
121
        $params['chartjs'] = $this->container->getParameter('dtc_queue.admin.chartjs');
122
    }
123
124
    protected function validateRunManager()
125
    {
126
        if ($this->container->hasParameter('dtc_queue.run_manager')) {
127
            $this->validateManagerType('dtc_queue.run_manager');
128
        } else {
129
            $this->validateManagerType('dtc_queue.default_manager');
130
        }
131
    }
132
133
    /**
134
     * List jobs in system by default.
135
     *
136
     * @Route("/runs", name="dtc_queue_runs")
137
     */
138 View Code Duplication
    public function runsAction()
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...
139
    {
140
        $this->validateRunManager();
141
        $class1 = $this->container->getParameter('dtc_queue.class_run');
142
        $class2 = $this->container->getParameter('dtc_queue.class_run_archive');
143
        $label1 = 'Live Runs';
144
        $label2 = 'Archived Runs';
145
146
        $params = $this->getDualGridParams($class1, $class2, $label1, $label2);
147
148
        return $this->render('@DtcQueue/Queue/grid.html.twig', $params);
149
    }
150
151
    /**
152
     * List registered workers in the system.
153
     *
154
     * @Route("/workers", name="dtc_queue_workers")
155
     * @Template()
156
     */
157
    public function workersAction()
158
    {
159
        $workerManager = $this->get('dtc_queue.worker_manager');
160
        $workers = $workerManager->getWorkers();
161
162
        $workerList = [];
163
        foreach ($workers as $workerName => $worker) {
164
            /* @var Worker $worker */
165
            $workerList[$workerName] = get_class($worker);
166
        }
167
        $params = ['workers' => $workerList];
168
        $this->addCssJs($params);
169
170
        return $params;
171
    }
172
173
    /**
174
     * Show a graph of job trends.
175
     *
176
     * @Route("/trends", name="dtc_queue_trends")
177
     * @Template()
178
     */
179
    public function trendsAction()
180
    {
181
        $recordTimings = $this->container->getParameter('dtc_queue.record_timings');
182
        $params = ['record_timings' => $recordTimings];
183
        $this->addCssJs($params);
184
        return $params;
185
    }
186
187
    /**
188
     * @Route("/timings", name="dtc_queue_timings")
189
     *
190
     * @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...
191
     */
192
    public function getTimingsAction()
193
    {
194
        $request = $this->get('request_stack')->getMasterRequest();
195
        $begin = $request->query->get('begin');
196
        $end = $request->query->get('end');
197
        $type = $request->query->get('type', 'HOUR');
198
        $beginDate = \DateTime::createFromFormat(DATE_ISO8601, $begin) ?: null;
199
        $endDate = \DateTime::createFromFormat(DATE_ISO8601, $end) ?: new \DateTime();
200
201
        $recordTimings = $this->container->getParameter('dtc_queue.record_timings');
202
        $params = [];
203
        if ($recordTimings) {
204
            $this->validateRunManager();
205
206
            /** @var BaseRunManager $runManager */
207
            $runManager = $this->get('dtc_queue.run_manager');
208
            if ($runManager instanceof RunManager) {
209
                $timings = $this->getJobTimingsOdm($type, $endDate, $beginDate);
210
            } else {
211
                $timings = $this->getJobTimingsOrm($type, $endDate, $beginDate);
212
            }
213
            uksort($timings, function ($date1str, $date2str) {
214
                $date1 = \DateTime::createFromFormat('Y-m-d H', $date1str);
215
                $date2 = \DateTime::createFromFormat('Y-m-d H', $date2str);
216
                if (!$date2) {
217
                    return false;
218
                }
219
                if (!$date1) {
220
                    return false;
221
                }
222
223
                return $date1 > $date2;
224
            });
225
226
            $params['timings_dates'] = array_keys($timings);
227
            $params['timings_data'] = array_values($timings);
228
        }
229
230
        return new JsonResponse($params);
231
    }
232
233
    protected function getJobTimingsOdm($type, \DateTime $end, \DateTime $begin = null)
234
    {
235
        /** @var RunManager $runManager */
236
        $runManager = $this->get('dtc_queue.run_manager');
237
        $jobTimingClass = $runManager->getJobTimingClass();
238
        /** @var DocumentManager $documentManager */
239
        $documentManager = $runManager->getObjectManager();
240
241
        $regexInfo = $this->getRegexDate($type);
242
        if (!$begin) {
243
            $begin = clone $end;
244
            $begin->sub($regexInfo['interval']);
245
        }
246
247
        // Run a map reduce function get worker and status break down
248
        $mapFunc = "function() {
249
            var dateStr = this.finishedAt.toISOString();
250
            dateStr = dateStr.replace(/{$regexInfo['regex']}/,'{$regexInfo['replacement']}');
251
            var dateBegin = new Date('{$begin->format('c')}');
252
            var dateEnd = new Date('{$end->format('c')}');
253
            if (this.finishedAt >= dateBegin && this.finishedAt <= dateEnd) {
254
                emit(dateStr, 1);
255
            }
256
        }";
257
        $reduceFunc = 'function(k, vals) {
258
            return Array.sum(vals);
259
        }';
260
261
        $builder = $documentManager->createQueryBuilder($jobTimingClass);
262
        $builder->map($mapFunc)
263
            ->reduce($reduceFunc);
264
        $query = $builder->getQuery();
265
        $results = $query->execute();
266
        $resultHash = [];
267
        foreach ($results as $info) {
268
            $resultHash[$info['_id']] = $info['value'];
269
        }
270
271
        return $resultHash;
272
    }
273
274
    protected function getRegexDate($type)
275
    {
276
        switch ($type) {
277 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...
278
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1'],
279
                    'interval' => new \DateInterval('P10Y'), ];
280 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...
281
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2'],
282
                    'interval' => new \DateInterval('P12M'), ];
283 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...
284
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3'],
285
                    'interval' => new \DateInterval('P31D'), ];
286 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...
287
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3 $4'],
288
                    'interval' => new \DateInterval('PT24H'), ];
289 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...
290
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3 $4:$5'],
291
                    'interval' => new \DateInterval('PT3600S'), ];
292
        }
293
        throw new \InvalidArgumentException("Invalid type $type");
294
    }
295
296
    protected function getOrmGroupBy($type)
297
    {
298
        switch ($type) {
299
            case 'YEAR':
300
                return ['groupby' => 'YEAR(j.finishedAt)',
301
                        'interval' => new \DateInterval('P10Y'), ];
302
            case 'MONTH':
303
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt))',
304
                        'interval' => new \DateInterval('P12M'), ];
305
            case 'DAY':
306
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt))',
307
                        'interval' => new \DateInterval('P31D'), ];
308
            case 'HOUR':
309
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt),\' \',HOUR(j.finishedAt))',
310
                        'interval' => new \DateInterval('PT24H'), ];
311
            case 'MINUTE':
312
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt),\' \',HOUR(j.finishedAt),\':\',MINUTE(j.finishedAt))',
313
                        'interval' => new \DateInterval('PT3600S'), ];
314
        }
315
        throw new \InvalidArgumentException("Invalid type $type");
316
    }
317
318
    protected function getJobTimingsOrm($type, \DateTime $end, \DateTime $begin = null)
319
    {
320
        /** @var RunManager $runManager */
321
        $runManager = $this->get('dtc_queue.run_manager');
322
        $jobTimingClass = $runManager->getJobTimingClass();
323
        /** @var EntityManager $entityManager */
324
        $entityManager = $runManager->getObjectManager();
325
326
        $groupByInfo = $this->getOrmGroupBy($type);
327
328
        if (!$begin) {
329
            $begin = clone $end;
330
            $begin->sub($groupByInfo['interval']);
331
        }
332
333
        $queryBuilder = $entityManager->createQueryBuilder()->select("count(j.finishedAt) as thecount, {$groupByInfo['groupby']} as thedate")
334
            ->from($jobTimingClass, 'j')
335
            ->where('j.finishedAt <= :end')
336
            ->andWhere('j.finishedAt >= :begin')
337
            ->setParameter(':end', $end)
338
            ->setParameter(':begin', $begin)
339
            ->groupBy('thedate');
340
341
        $result = $queryBuilder
342
            ->getQuery()->getArrayResult();
343
344
        $resultHash = [];
345
        foreach ($result as $row) {
346
            $resultHash[$row['thedate']] = intval($row['thecount']);
347
        }
348
349
        return $resultHash;
350
    }
351
}
352