Test Setup Failed
Push — master ( 8dfa9d...c45cfd )
by Matthew
04:44
created

QueueController::validateRunManager()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 6
cp 0
cc 2
eloc 4
nc 2
nop 0
crap 6
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;
0 ignored issues
show
Bug introduced by
The type Zend\Stdlib\Request was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
17
class QueueController extends Controller
18
{
19
    /**
20
     * Summary stats.
21
     *
22
     * @Route("/")
23
     * @Route("/status/")
24
     * @Template("@DtcQueue/Queue/jobs.html.twig")
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);
0 ignored issues
show
Bug introduced by
$params of type Symfony\Component\HttpFoundation\Response is incompatible with the type array expected by parameter $parameters of Symfony\Bundle\Framework...er\Controller::render(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

52
        return $this->render('@DtcQueue/Queue/grid.html.twig', /** @scrutinizer ignore-type */ $params);
Loading history...
53
    }
54
55
    /**
56
     * List jobs in system by default.
57
     *
58
     * @Template("@DtcQueue/Queue/grid.html.twig")
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);
0 ignored issues
show
Bug introduced by
$params of type Symfony\Component\HttpFoundation\Response is incompatible with the type array expected by parameter $parameters of Symfony\Bundle\Framework...er\Controller::render(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

148
        return $this->render('@DtcQueue/Queue/grid.html.twig', /** @scrutinizer ignore-type */ $params);
Loading history...
149
    }
150
151
    /**
152
     * List registered workers in the system.
153
     *
154
     * @Route("/workers", name="dtc_queue_workers")
155
     * @Template("@DtcQueue/Queue/workers.html.twig")
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("@DtcQueue/Queue/trends.html.twig")
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
185
        return $params;
186
    }
187
188
    /**
189
     * @Route("/timings", name="dtc_queue_timings")
190
     *
191
     * @param Request $request
192
     */
193
    public function getTimingsAction()
194
    {
195
        $request = $this->get('request_stack')->getMasterRequest();
196
        $begin = $request->query->get('begin');
197
        $end = $request->query->get('end');
198
        $type = $request->query->get('type', 'HOUR');
199
        $beginDate = \DateTime::createFromFormat(DATE_ISO8601, $begin) ?: null;
200
        $endDate = \DateTime::createFromFormat(DATE_ISO8601, $end) ?: new \DateTime();
201
202
        $recordTimings = $this->container->getParameter('dtc_queue.record_timings');
203
        $params = [];
204
        if ($recordTimings) {
205
            $params = $this->calculateTimings($type, $beginDate, $endDate);
206
        }
207
        return new JsonResponse($params);
208
    }
209
210
    protected function calculateTimings($type, $beginDate, $endDate) {
211
        $params = [];
212
        $this->validateRunManager();
213
214
        /** @var BaseRunManager $runManager */
215
        $runManager = $this->get('dtc_queue.run_manager');
216
        if ($runManager instanceof RunManager) {
217
            $timings = $this->getJobTimingsOdm($type, $endDate, $beginDate);
218
        } else {
219
            $timings = $this->getJobTimingsOrm($type, $endDate, $beginDate);
220
        }
221
        uksort($timings, function ($date1str, $date2str) {
222
            $date1 = \DateTime::createFromFormat('Y-m-d H', $date1str);
223
            $date2 = \DateTime::createFromFormat('Y-m-d H', $date2str);
224
            if (!$date2) {
225
                return false;
226
            }
227
            if (!$date1) {
228
                return false;
229
            }
230
231
            return $date1 > $date2;
232
        });
233
234
        $params['timings_dates'] = array_keys($timings);
235
        $params['timings_data'] = array_values($timings);
236
        return $params;
237
    }
238
239
    protected function getJobTimingsOdm($type, \DateTime $end, \DateTime $begin = null)
240
    {
241
        /** @var RunManager $runManager */
242
        $runManager = $this->get('dtc_queue.run_manager');
243
        $jobTimingClass = $runManager->getJobTimingClass();
244
        /** @var DocumentManager $documentManager */
245
        $documentManager = $runManager->getObjectManager();
246
247
        $regexInfo = $this->getRegexDate($type);
248
        if (!$begin) {
249
            $begin = clone $end;
250
            $begin->sub($regexInfo['interval']);
251
        }
252
253
        // Run a map reduce function get worker and status break down
254
        $mapFunc = "function() {
255
            var dateStr = this.finishedAt.toISOString();
256
            dateStr = dateStr.replace(/{$regexInfo['regex']}/,'{$regexInfo['replacement']}');
257
            var dateBegin = new Date('{$begin->format('c')}');
258
            var dateEnd = new Date('{$end->format('c')}');
259
            if (this.finishedAt >= dateBegin && this.finishedAt <= dateEnd) {
260
                emit(dateStr, 1);
261
            }
262
        }";
263
        $reduceFunc = 'function(k, vals) {
264
            return Array.sum(vals);
265
        }';
266
267
        $builder = $documentManager->createQueryBuilder($jobTimingClass);
268
        $builder->map($mapFunc)
269
            ->reduce($reduceFunc);
270
        $query = $builder->getQuery();
271
        $results = $query->execute();
272
        $resultHash = [];
273
        foreach ($results as $info) {
274
            $resultHash[$info['_id']] = $info['value'];
275
        }
276
277
        return $resultHash;
278
    }
279
280
    protected function getRegexDate($type)
281
    {
282
        switch ($type) {
283 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...
284
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1'],
285
                    'interval' => new \DateInterval('P10Y'), ];
286 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...
287
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2'],
288
                    'interval' => new \DateInterval('P12M'), ];
289 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...
290
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3'],
291
                    'interval' => new \DateInterval('P31D'), ];
292 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...
293
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3 $4'],
294
                    'interval' => new \DateInterval('PT24H'), ];
295 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...
296
                return ['replace' => ['regex' => '(\d+)\-(\d+)\-(\d+)T(\d+):(\d+):(\d+).+$', 'replacement' => '$1-$2-$3 $4:$5'],
297
                    'interval' => new \DateInterval('PT3600S'), ];
298
        }
299
        throw new \InvalidArgumentException("Invalid type $type");
300
    }
301
302
    protected function getOrmGroupBy($type)
303
    {
304
        switch ($type) {
305
            case 'YEAR':
306
                return ['groupby' => 'YEAR(j.finishedAt)',
307
                        'interval' => new \DateInterval('P10Y'), ];
308
            case 'MONTH':
309
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt))',
310
                        'interval' => new \DateInterval('P12M'), ];
311
            case 'DAY':
312
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt))',
313
                        'interval' => new \DateInterval('P31D'), ];
314
            case 'HOUR':
315
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt),\' \',HOUR(j.finishedAt))',
316
                        'interval' => new \DateInterval('PT24H'), ];
317
            case 'MINUTE':
318
                return ['groupby' => 'CONCAT(YEAR(j.finishedAt),\'-\',MONTH(j.finishedAt),\'-\',DAY(j.finishedAt),\' \',HOUR(j.finishedAt),\':\',MINUTE(j.finishedAt))',
319
                        'interval' => new \DateInterval('PT3600S'), ];
320
        }
321
        throw new \InvalidArgumentException("Invalid type $type");
322
    }
323
324
    protected function getJobTimingsOrm($type, \DateTime $end, \DateTime $begin = null)
325
    {
326
        /** @var RunManager $runManager */
327
        $runManager = $this->get('dtc_queue.run_manager');
328
        $jobTimingClass = $runManager->getJobTimingClass();
329
        /** @var EntityManager $entityManager */
330
        $entityManager = $runManager->getObjectManager();
331
332
        $groupByInfo = $this->getOrmGroupBy($type);
333
334
        if (!$begin) {
335
            $begin = clone $end;
336
            $begin->sub($groupByInfo['interval']);
337
        }
338
339
        $queryBuilder = $entityManager->createQueryBuilder()->select("count(j.finishedAt) as thecount, {$groupByInfo['groupby']} as thedate")
340
            ->from($jobTimingClass, 'j')
341
            ->where('j.finishedAt <= :end')
342
            ->andWhere('j.finishedAt >= :begin')
343
            ->setParameter(':end', $end)
344
            ->setParameter(':begin', $begin)
345
            ->groupBy('thedate');
346
347
        $result = $queryBuilder
348
            ->getQuery()->getArrayResult();
349
350
        $resultHash = [];
351
        foreach ($result as $row) {
352
            $resultHash[$row['thedate']] = intval($row['thecount']);
353
        }
354
355
        return $resultHash;
356
    }
357
}
358