Passed
Push — master ( 9df72b...a7b6af )
by Marcel
02:33
created

ReportService::create()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 14
c 2
b 0
f 0
nc 16
nop 12
dl 0
loc 23
rs 8.8333

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * Analytics
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the LICENSE.md file.
7
 *
8
 * @author Marcel Scherello <[email protected]>
9
 * @copyright 2019-2022 Marcel Scherello
10
 */
11
12
namespace OCA\Analytics\Service;
13
14
use OCA\Analytics\Activity\ActivityManager;
15
use OCA\Analytics\Controller\DatasourceController;
16
use OCA\Analytics\Db\DataloadMapper;
17
use OCA\Analytics\Db\ReportMapper;
18
use OCA\Analytics\Db\StorageMapper;
19
use OCA\Analytics\Db\ThresholdMapper;
20
use OCP\AppFramework\Http\DataDownloadResponse;
21
use OCP\DB\Exception;
22
use OCP\Files\IRootFolder;
23
use OCP\ITagManager;
24
use OCP\IConfig;
25
use OCP\PreConditionNotMetException;
26
use Psr\Log\LoggerInterface;
27
use OCP\IL10N;
28
29
class ReportService
30
{
31
    /** @var IConfig */
32
    protected $config;
33
    private $userId;
34
    private $logger;
35
    private $tagManager;
36
    private $ShareService;
37
    private $DatasetService;
38
    private $StorageMapper;
39
    private $ReportMapper;
40
    private $ThresholdMapper;
41
    private $DataloadMapper;
42
    private $ActivityManager;
43
    private $rootFolder;
44
    private $VariableService;
45
    private $l10n;
46
47
    public function __construct(
48
        $userId,
49
        IL10N $l10n,
50
        LoggerInterface $logger,
51
        ITagManager $tagManager,
52
        ShareService $ShareService,
53
        DatasetService $DatasetService,
54
        StorageMapper $StorageMapper,
55
        ReportMapper $ReportMapper,
56
        ThresholdMapper $ThresholdMapper,
57
        DataloadMapper $DataloadMapper,
58
        ActivityManager $ActivityManager,
59
        IRootFolder $rootFolder,
60
        IConfig $config,
61
        VariableService $VariableService
62
    )
63
    {
64
        $this->userId = $userId;
65
        $this->logger = $logger;
66
        $this->tagManager = $tagManager;
67
        $this->ShareService = $ShareService;
68
        $this->DatasetService = $DatasetService;
69
        $this->ThresholdMapper = $ThresholdMapper;
70
        $this->StorageMapper = $StorageMapper;
71
        $this->ReportMapper = $ReportMapper;
72
        $this->DataloadMapper = $DataloadMapper;
73
        $this->ActivityManager = $ActivityManager;
74
        $this->rootFolder = $rootFolder;
75
        $this->VariableService = $VariableService;
76
        $this->config = $config;
77
        $this->l10n = $l10n;
78
    }
79
80
    /**
81
     * get all reports
82
     *
83
     * @return array
84
     * @throws PreConditionNotMetException
85
     */
86
    public function index(): array
87
    {
88
        $ownReports = $this->ReportMapper->index();
89
90
        // get shared reports and remove duplicates
91
        $sharedReports = $this->ShareService->getSharedReports();
92
        foreach ($sharedReports as $sharedReport) {
93
            $this->logger->info('reportservice: '. $sharedReport['name']);
94
95
            if (!array_search($sharedReport['id'], array_column($ownReports, 'id'))) {
96
                $sharedReport['type'] = '99';
97
                $sharedReport['parrent'] = '0';
98
                array_push($ownReports, $sharedReport);
99
            }
100
        }
101
        if (count($ownReports) === 0) return $ownReports;
102
103
        // get data load indicators for icons shown in the advanced screen
104
        $dataloads = $this->DataloadMapper->getAllDataloadMetadata();
105
        foreach ($dataloads as $dataload) {
106
            $key = array_search($dataload['dataset'], array_column($ownReports, 'dataset'));
107
            if ($key !== '') {
108
                if ($dataload['schedules'] !== '' and $dataload['schedules'] !== null) {
109
                    $dataload['schedules'] = 1;
110
                } else {
111
                    $dataload['schedules'] = 0;
112
                }
113
                $ownReports[$key]['dataloads'] = $dataload['dataloads'];
114
                $ownReports[$key]['schedules'] = $dataload['schedules'];
115
            }
116
        }
117
118
        $favoriteMigration = $this->config->getUserValue($this->userId, 'analytics', 'favMig', '0');
119
        if ($favoriteMigration === '0') {
120
            $this->logger->info('Favorite migration being performed');
121
            $this->favoriteMigration($ownReports);
122
            $this->config->setUserValue($this->userId, 'analytics', 'favMig', 3.7);
123
        }
124
125
        $favorites = $this->tagManager->load('analytics')->getFavorites();
126
        foreach ($ownReports as &$ownReport) {
127
            $hasTag = 0;
128
            if (is_array($favorites) and in_array($ownReport['id'], $favorites)) {
129
                $hasTag = 1;
130
            }
131
            $ownReport['favorite'] = $hasTag;
132
            $ownReport = $this->VariableService->replaceTextVariables($ownReport);
133
        }
134
135
        return $ownReports;
136
    }
137
138
    /**
139
     * get own report details
140
     *
141
     * @param int $reportId
142
     * @return array
143
     * @throws Exception
144
     */
145
    public function read(int $reportId, $replace = true)
146
    {
147
        $ownReport = $this->ReportMapper->readOwn($reportId);
148
        if (!empty($ownReport)) {
149
            $ownReport['permissions'] = \OCP\Constants::PERMISSION_UPDATE;
150
            if ($replace) $ownReport = $this->VariableService->replaceTextVariables($ownReport);
151
152
            if ($ownReport['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB && $ownReport['dataset'] !== 0) {
153
                $dataset = $this->DatasetService->readOwn($ownReport['dataset']);
154
                $ownReport['dimension1'] = $dataset['dimension1'];
155
                $ownReport['dimension2'] = $dataset['dimension2'];
156
                $ownReport['value'] = $dataset['value'];
157
            }
158
159
        }
160
        return $ownReport;
161
    }
162
163
    /**
164
     * check if own report
165
     *
166
     * @param int $reportId
167
     * @return bool
168
     */
169
    public function isOwn(int $reportId)
170
    {
171
        $ownReport = $this->ReportMapper->readOwn($reportId);
172
        if (!empty($ownReport)) {
173
            return true;
174
        } else {
175
            return false;
176
        }
177
    }
178
179
    /**
180
     * create new blank report
181
     *
182
     * @return int
183
     */
184
    public function create($name, $subheader, $parent, $type, int $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value, $addReport = null): int
185
    {
186
        $array = json_decode($link, true);
187
        if (is_array($array)){
188
            foreach ($array as $key => $value) {
0 ignored issues
show
introduced by
$value is overwriting one of the parameters of this function.
Loading history...
189
                $array[$key] = htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
190
            }
191
        }
192
        $link = json_encode($array);
193
194
        if ($type === DatasourceController::DATASET_TYPE_GROUP) {
195
            $parent = 0;
196
        }
197
        if ($type === DatasourceController::DATASET_TYPE_INTERNAL_DB && $dataset === 0) { // New dataset
198
            $dataset = $this->DatasetService->create($name, $dimension1, $dimension2, $value);
199
        }
200
        $reportId = $this->ReportMapper->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value);
201
        $this->ActivityManager->triggerEvent($reportId, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_ADD);
202
203
        if ($addReport !== null) {
204
            $this->updateGroup($addReport, $reportId);
205
        }
206
        return $reportId;
207
    }
208
209
    /**
210
     * copy an existing report with the current navigation status
211
     *
212
     * @NoAdminRequired
213
     * @param int $reportId
214
     * @param $chartoptions
215
     * @param $dataoptions
216
     * @param $filteroptions
217
     * @return int
218
     * @throws Exception
219
     */
220
    public function createCopy(int $reportId, $chartoptions, $dataoptions, $filteroptions)
221
    {
222
223
        $template = $this->ReportMapper->readOwn($reportId);
224
        $newId = $this->ReportMapper->create(
225
        // TRANSLATORS Noun
226
            $template['name'] . ' ' . $this->l10n->t('copy'),
227
            $template['subheader'],
228
            $template['parent'],
229
            $template['type'],
230
            $template['dataset'],
231
            $template['link'],
232
            $template['visualization'],
233
            $template['chart'],
234
            $template['dimension1'],
235
            $template['dimension2'],
236
            $template['value']);
237
        $this->ReportMapper->updateOptions($newId, $chartoptions, $dataoptions, $filteroptions);
238
        return $newId;
239
    }
240
241
    /**
242
     * create new report
243
     *
244
     * @param string $file
245
     * @return int
246
     */
247
    public function createFromDataFile($file = '')
248
    {
249
        $this->ActivityManager->triggerEvent(0, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_ADD);
250
251
        if ($file !== '') {
252
            $name = explode('.', end(explode('/', $file)))[0];
253
            $subheader = $file;
254
            $parent = 0;
255
            $dataset = 0;
256
            $type = DatasourceController::DATASET_TYPE_FILE;
257
            $link = $file;
258
            $visualization = 'table';
259
            $chart = 'line';
260
            $reportId = $this->ReportMapper->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, '', '', '');
261
        }
262
        return $reportId;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $reportId does not seem to be defined for all execution paths leading up to this point.
Loading history...
263
    }
264
265
    /**
266
     * update report details
267
     *
268
     * @param int $reportId
269
     * @param $name
270
     * @param $subheader
271
     * @param int $parent
272
     * @param $link
273
     * @param $visualization
274
     * @param $chart
275
     * @param $chartoptions
276
     * @param $dataoptions
277
     * @param $dimension1
278
     * @param $dimension2
279
     * @param $value
280
     * @return bool
281
     */
282
    public function update(int $reportId, $name, $subheader, int $parent, $link, $visualization, $chart, $chartoptions, $dataoptions, $dimension1 = null, $dimension2 = null, $value = null)
283
    {
284
        $array = json_decode($link, true);
285
        foreach ($array as $key => $value) {
286
            $array[$key] = htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
287
        }
288
        $link = json_encode($array);
289
290
        return $this->ReportMapper->update($reportId, $name, $subheader, $parent, $link, $visualization, $chart, $chartoptions, $dataoptions, $dimension1, $dimension2, $value);
291
    }
292
293
    /**
294
     * Delete Dataset and all depending objects
295
     *
296
     * @param int $reportId
297
     * @return string
298
     * @throws Exception
299
     */
300
    public function delete(int $reportId)
301
    {
302
        $metadata = $this->ReportMapper->readOwn($reportId);
303
        //$this->ActivityManager->triggerEvent($reportId, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_DELETE);
304
        $this->ShareService->deleteShareByReport($reportId);
305
        $this->ThresholdMapper->deleteThresholdByReport($reportId);
306
        $this->setFavorite($reportId, 'false');
307
        $this->ReportMapper->delete($reportId);
308
309
        $report = $this->reportsForDataset((int)$metadata['dataset']);
310
        if (empty($report) && (int)$metadata['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB) {
311
            return $metadata['dataset'];
312
        } else {
313
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type string.
Loading history...
314
        }
315
    }
316
317
    /**
318
     * get dataset by user
319
     *
320
     * @param string $userId
321
     * @return array|bool
322
     * @throws Exception
323
     */
324
    public function deleteByUser(string $userId)
325
    {
326
        $reports = $this->ReportMapper->indexByUser($userId);
327
        foreach ($reports as $report) {
328
            $this->ShareService->deleteShareByReport($report['id']);
329
            $this->ThresholdMapper->deleteThresholdByReport($report['id']);
330
            $this->setFavorite($report['id'], 'false');
331
            $this->ReportMapper->delete($report['id']);
332
        }
333
        return true;
334
    }
335
336
    /**
337
     * get own reports which are marked as favorites
338
     *
339
     * @return array|bool
340
     */
341
    public function getOwnFavoriteReports()
342
    {
343
        $ownReports = $this->ReportMapper->index();
344
        $favorites = $this->tagManager->load('analytics')->getFavorites();
345
        $sharedReports = $this->ShareService->getSharedReports();
346
347
        foreach ($favorites as $favorite) {
348
            if (array_search($favorite, array_column($ownReports, 'id')) === false
349
                && array_search($favorite, array_column($sharedReports, 'id')) === false) {
350
                unset($favorites[$favorite]);
351
                $this->tagManager->load('analytics')->removeFromFavorites($favorite);
352
            }
353
        }
354
355
        return $favorites;
356
    }
357
358
    /**
359
     * set/remove the favorite flag for a report
360
     *
361
     * @param int $reportId
362
     * @param string $favorite
363
     * @return bool
364
     */
365
    public function setFavorite(int $reportId, string $favorite)
366
    {
367
        if ($favorite === 'true') {
368
            $return = $this->tagManager->load('analytics')->addToFavorites($reportId);
369
        } else {
370
            $return = $this->tagManager->load('analytics')->removeFromFavorites($reportId);
371
        }
372
        return $return;
373
    }
374
375
    /**
376
     * Import Report from File
377
     *
378
     * @param string|null $path
379
     * @param string|null $raw
380
     * @return int
381
     * @throws \OCP\Files\NotFoundException
382
     * @throws \OCP\Files\NotPermittedException
383
     */
384
    public function import(string $path = null, string $raw = null)
385
    {
386
        if ($path !== '') {
387
            $file = $this->rootFolder->getUserFolder($this->userId)->get($path);
388
            $data = $file->getContent();
0 ignored issues
show
Bug introduced by
The method getContent() does not exist on OCP\Files\Node. It seems like you code against a sub-type of OCP\Files\Node such as OCP\Files\File. ( Ignorable by Annotation )

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

388
            /** @scrutinizer ignore-call */ 
389
            $data = $file->getContent();
Loading history...
389
        } else if ($raw !== null) {
390
            $data = $raw;
391
        } else {
392
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
393
        }
394
        $data = json_decode($data, true);
395
396
        $report = $data['report'];
397
        isset($report['name']) ? $name = $report['name'] : $name = '';
398
        isset($report['subheader']) ? $subheader = $report['subheader'] : $subheader = '';
399
        $parent = 0;
400
        $dataset = 0;
401
        isset($report['type']) ? $type = $report['type'] : $type = null;
402
        isset($report['link']) ? $link = $report['link'] : $link = null;
403
        isset($report['visualization']) ? $visualization = $report['visualization'] : $visualization = null;
404
        isset($report['chart']) ? $chart = $report['chart'] : $chart = null;
405
        isset($report['chartoptions']) ? $chartoptions = $report['chartoptions'] : $chartoptions = null;
406
        isset($report['dataoptions']) ? $dataoptions = $report['dataoptions'] : $dataoptions = null;
407
        isset($report['filteroptions']) ? $filteroptions = $report['filteroptions'] : $filteroptions = null;
408
        isset($report['dimension1']) ? $dimension1 = $report['dimension1'] : $dimension1 = null;
409
        isset($report['dimension2']) ? $dimension2 = $report['dimension2'] : $dimension2 = null;
410
        isset($report['value']) ? $value = $report['value'] : $value = null;
411
412
        $reportId = $this->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value);
413
        $this->updateOptions($reportId, $chartoptions, $dataoptions, $filteroptions);
414
        $report = $this->ReportMapper->readOwn($reportId);
415
        $datasetId = $report['dataset'];
416
417
        foreach ($data['dataload'] as $dataload) {
418
            isset($dataload['datasource']) ? $datasource = $dataload['datasource'] : $datasource = null;
419
            isset($dataload['name']) ? $name = $dataload['name'] : $name = null;
420
            isset($dataload['option']) ? $option = $dataload['option'] : $option = null;
421
            $schedule = null;
422
423
            $dataloadId = $this->DataloadMapper->create($datasetId, $datasource);
424
            $this->DataloadMapper->update($dataloadId, $name, $option, $schedule);
425
        }
426
427
        foreach ($data['threshold'] as $threshold) {
428
            isset($threshold['dimension1']) ? $dimension1 = $threshold['dimension1'] : $dimension1 = null;
429
            isset($threshold['value']) ? $value = $threshold['value'] : $value = null;
430
            isset($threshold['option']) ? $option = $threshold['option'] : $option = null;
431
            isset($threshold['severity']) ? $severity = $threshold['severity'] : $severity = null;
432
            $value = $this->floatvalue($value);
433
            $this->ThresholdMapper->create($reportId, $dimension1, $value, $option, $severity);
434
        }
435
436
        foreach ($data['data'] as $dData) {
437
            isset($dData[0]) ? $dimension1 = $dData[0] : $dimension1 = null;
438
            isset($dData[1]) ? $dimension2 = $dData[1] : $dimension2 = null;
439
            isset($dData[2]) ? $value = $dData[2] : $value = null;
440
            $this->StorageMapper->create($datasetId, $dimension1, $dimension2, $value);
441
        }
442
443
        if (isset($data['favorite'])) {
444
            $this->setFavorite($reportId, $data['favorite']);
445
        }
446
447
        return $reportId;
448
    }
449
450
    /**
451
     * Export Report
452
     *
453
     * @param int $reportId
454
     * @return DataDownloadResponse
455
     */
456
    public function export(int $reportId)
457
    {
458
        $result = array();
459
        $result['report'] = $this->ReportMapper->readOwn($reportId);
460
        $datasetId = $result['report']['dataset'];
461
        $result['dataload'] = $this->DataloadMapper->read($datasetId);
462
        $result['threshold'] = $this->ThresholdMapper->getThresholdsByReport($reportId);
463
        $result['favorite'] = '';
464
465
        if ($result['report']['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB) {
466
            $result['data'] = $this->StorageMapper->read($datasetId);
467
        }
468
469
        unset($result['report']['id'], $result['report']['user_id'], $result['report']['user_id'], $result['report']['parent'], $result['report']['dataset']);
470
        $data = json_encode($result);
471
        return new DataDownloadResponse($data, $result['report']['name'] . '.export.txt', 'text/plain; charset=utf-8');
472
    }
473
474
    /**
475
     * Update report options
476
     *
477
     * @param int $reportId
478
     * @param $chartoptions
479
     * @param $dataoptions
480
     * @param $filteroptions
481
     * @return bool
482
     */
483
    public function updateOptions(int $reportId, $chartoptions, $dataoptions, $filteroptions)
484
    {
485
        return $this->ReportMapper->updateOptions($reportId, $chartoptions, $dataoptions, $filteroptions);
486
    }
487
488
    /**
489
     * get report refresh options
490
     *
491
     * @NoAdminRequired
492
     * @param int $reportId
493
     * @param $refresh
494
     * @return bool
495
     */
496
    public function updateRefresh(int $reportId, $refresh)
497
    {
498
        return $this->ReportMapper->updateRefresh($reportId, $refresh);
499
    }
500
501
    /**
502
     * update report group assignment (from drag & drop)
503
     *
504
     * @NoAdminRequired
505
     * @param int $reportId
506
     * @param $groupId
507
     * @return bool
508
     */
509
    public function updateGroup(int $reportId, $groupId)
510
    {
511
        return $this->ReportMapper->updateGroup($reportId, $groupId);
512
    }
513
514
    /**
515
     * search for reports
516
     *
517
     * @param string $searchString
518
     * @return array
519
     */
520
    public function search(string $searchString)
521
    {
522
        return $this->ReportMapper->search($searchString);
523
    }
524
525
    /**
526
     * @throws Exception
527
     */
528
    public function reportsForDataset($datasetId) {
529
        return $this->ReportMapper->reportsForDataset($datasetId);
530
    }
531
532
    /**
533
     * migrate old favorite ids
534
     *
535
     * @param $ownReports
536
     * @return bool
537
     */
538
    private function favoriteMigration($ownReports) {
539
        $favorites = $this->tagManager->load('analytics')->getFavorites();
540
        foreach ($favorites as $favorite) {
541
            $key = array_search($favorite, array_column($ownReports, 'dataset'));
542
            if ($key) {
543
                $this->logger->info('Favorite was migrated from '. $ownReports[$key]['dataset'] . ' to new report ' . $ownReports[$key]['id']);
544
                $this->tagManager->load('analytics')->removeFromFavorites($ownReports[$key]['dataset']);
545
                $this->tagManager->load('analytics')->addToFavorites($ownReports[$key]['id']);
546
            }
547
        }
548
        return true;
549
    }
550
551
    private function floatvalue($val)
552
    {
553
        $val = str_replace(",", ".", $val);
554
        $val = preg_replace('/\.(?=.*\.)/', '', $val);
555
        $val = preg_replace('/[^0-9-.]+/', '', $val);
556
        if (is_numeric($val)) {
557
            return number_format(floatval($val), 2, '.', '');
558
        } else {
559
            return false;
560
        }
561
    }
562
563
}
564