Passed
Push — master ( cb24fc...339795 )
by Marcel
04:00 queued 12s
created

ReportService::update()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 2
b 0
f 0
nc 2
nop 12
dl 0
loc 9
rs 10

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
        foreach ($array as $key => $value) {
0 ignored issues
show
introduced by
$value is overwriting one of the parameters of this function.
Loading history...
188
            $array[$key] = htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
189
        }
190
        $link = json_encode($array);
191
192
        if ($type === DatasourceController::DATASET_TYPE_GROUP) {
193
            $parent = 0;
194
        }
195
        if ($type === DatasourceController::DATASET_TYPE_INTERNAL_DB && $dataset === 0) { // New dataset
196
            $dataset = $this->DatasetService->create($name, $dimension1, $dimension2, $value);
197
        }
198
        $reportId = $this->ReportMapper->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value);
199
        $this->ActivityManager->triggerEvent($reportId, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_ADD);
200
201
        if ($addReport !== null) {
202
            $this->updateGroup($addReport, $reportId);
203
        }
204
        return $reportId;
205
    }
206
207
    /**
208
     * copy an existing report with the current navigation status
209
     *
210
     * @NoAdminRequired
211
     * @param int $reportId
212
     * @param $chartoptions
213
     * @param $dataoptions
214
     * @param $filteroptions
215
     * @return int
216
     * @throws Exception
217
     */
218
    public function createCopy(int $reportId, $chartoptions, $dataoptions, $filteroptions)
219
    {
220
221
        $template = $this->ReportMapper->readOwn($reportId);
222
        $newId = $this->ReportMapper->create(
223
        // TRANSLATORS Noun
224
            $template['name'] . ' ' . $this->l10n->t('copy'),
225
            $template['subheader'],
226
            $template['parent'],
227
            $template['type'],
228
            $template['dataset'],
229
            $template['link'],
230
            $template['visualization'],
231
            $template['chart'],
232
            $template['dimension1'],
233
            $template['dimension2'],
234
            $template['value']);
235
        $this->ReportMapper->updateOptions($newId, $chartoptions, $dataoptions, $filteroptions);
236
        return $newId;
237
    }
238
239
    /**
240
     * create new report
241
     *
242
     * @param string $file
243
     * @return int
244
     */
245
    public function createFromDataFile($file = '')
246
    {
247
        $this->ActivityManager->triggerEvent(0, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_ADD);
248
249
        if ($file !== '') {
250
            $name = explode('.', end(explode('/', $file)))[0];
251
            $subheader = $file;
252
            $parent = 0;
253
            $dataset = 0;
254
            $type = DatasourceController::DATASET_TYPE_FILE;
255
            $link = $file;
256
            $visualization = 'table';
257
            $chart = 'line';
258
            $reportId = $this->ReportMapper->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, '', '', '');
259
        }
260
        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...
261
    }
262
263
    /**
264
     * update report details
265
     *
266
     * @param int $reportId
267
     * @param $name
268
     * @param $subheader
269
     * @param int $parent
270
     * @param $link
271
     * @param $visualization
272
     * @param $chart
273
     * @param $chartoptions
274
     * @param $dataoptions
275
     * @param $dimension1
276
     * @param $dimension2
277
     * @param $value
278
     * @return bool
279
     */
280
    public function update(int $reportId, $name, $subheader, int $parent, $link, $visualization, $chart, $chartoptions, $dataoptions, $dimension1 = null, $dimension2 = null, $value = null)
281
    {
282
        $array = json_decode($link, true);
283
        foreach ($array as $key => $value) {
284
            $array[$key] = htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
285
        }
286
        $link = json_encode($array);
287
288
        return $this->ReportMapper->update($reportId, $name, $subheader, $parent, $link, $visualization, $chart, $chartoptions, $dataoptions, $dimension1, $dimension2, $value);
289
    }
290
291
    /**
292
     * Delete Dataset and all depending objects
293
     *
294
     * @param int $reportId
295
     * @return string
296
     * @throws Exception
297
     */
298
    public function delete(int $reportId)
299
    {
300
        $metadata = $this->ReportMapper->readOwn($reportId);
301
        //$this->ActivityManager->triggerEvent($reportId, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_DELETE);
302
        $this->ShareService->deleteShareByReport($reportId);
303
        $this->ThresholdMapper->deleteThresholdByReport($reportId);
304
        $this->setFavorite($reportId, 'false');
305
        $this->ReportMapper->delete($reportId);
306
307
        $report = $this->reportsForDataset((int)$metadata['dataset']);
308
        if (empty($report) && (int)$metadata['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB) {
309
            return $metadata['dataset'];
310
        } else {
311
            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...
312
        }
313
    }
314
315
    /**
316
     * get dataset by user
317
     *
318
     * @param string $userId
319
     * @return array|bool
320
     * @throws Exception
321
     */
322
    public function deleteByUser(string $userId)
323
    {
324
        $reports = $this->ReportMapper->indexByUser($userId);
325
        foreach ($reports as $report) {
326
            $this->ShareService->deleteShareByReport($report['id']);
327
            $this->ThresholdMapper->deleteThresholdByReport($report['id']);
328
            $this->setFavorite($report['id'], 'false');
329
            $this->ReportMapper->delete($report['id']);
330
        }
331
        return true;
332
    }
333
334
    /**
335
     * get own reports which are marked as favorites
336
     *
337
     * @return array|bool
338
     */
339
    public function getOwnFavoriteReports()
340
    {
341
        $ownReports = $this->ReportMapper->index();
342
        $favorites = $this->tagManager->load('analytics')->getFavorites();
343
        $sharedReports = $this->ShareService->getSharedReports();
344
345
        foreach ($favorites as $favorite) {
346
            if (array_search($favorite, array_column($ownReports, 'id')) === false
347
                && array_search($favorite, array_column($sharedReports, 'id')) === false) {
348
                unset($favorites[$favorite]);
349
                $this->tagManager->load('analytics')->removeFromFavorites($favorite);
350
            }
351
        }
352
353
        return $favorites;
354
    }
355
356
    /**
357
     * set/remove the favorite flag for a report
358
     *
359
     * @param int $reportId
360
     * @param string $favorite
361
     * @return bool
362
     */
363
    public function setFavorite(int $reportId, string $favorite)
364
    {
365
        if ($favorite === 'true') {
366
            $return = $this->tagManager->load('analytics')->addToFavorites($reportId);
367
        } else {
368
            $return = $this->tagManager->load('analytics')->removeFromFavorites($reportId);
369
        }
370
        return $return;
371
    }
372
373
    /**
374
     * Import Report from File
375
     *
376
     * @param string|null $path
377
     * @param string|null $raw
378
     * @return int
379
     * @throws \OCP\Files\NotFoundException
380
     * @throws \OCP\Files\NotPermittedException
381
     */
382
    public function import(string $path = null, string $raw = null)
383
    {
384
        if ($path !== '') {
385
            $file = $this->rootFolder->getUserFolder($this->userId)->get($path);
386
            $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

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