Passed
Push — master ( 810bf3...bcc162 )
by Marcel
03:09 queued 11s
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 2021 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
        if (count($ownReports) === 0) return $ownReports;
90
91
        // get data load indicators for icons shown in the advanced screen
92
        $dataloads = $this->DataloadMapper->getAllDataloadMetadata();
93
        foreach ($dataloads as $dataload) {
94
            $key = array_search($dataload['dataset'], array_column($ownReports, 'dataset'));
95
            if ($key !== '') {
96
                if ($dataload['schedules'] !== '' and $dataload['schedules'] !== null) {
97
                    $dataload['schedules'] = 1;
98
                } else {
99
                    $dataload['schedules'] = 0;
100
                }
101
                $ownReports[$key]['dataloads'] = $dataload['dataloads'];
102
                $ownReports[$key]['schedules'] = $dataload['schedules'];
103
            }
104
        }
105
106
        // get shared reports and remove duplicates
107
        $sharedReports = $this->ShareService->getSharedReports();
108
        foreach ($sharedReports as $sharedReport) {
109
            if (!array_search($sharedReport['id'], array_column($ownReports, 'id'))) {
110
                $sharedReport['type'] = '99';
111
                $sharedReport['parrent'] = '0';
112
                array_push($ownReports, $sharedReport);
113
            }
114
        }
115
116
        $favoriteMigration = $this->config->getUserValue($this->userId, 'analytics', 'favMig', '0');
117
        if ($favoriteMigration === '0') {
118
            $this->logger->info('Favorite migration being performed');
119
            $this->favoriteMigration($ownReports);
120
            $this->config->setUserValue($this->userId, 'analytics', 'favMig', 3.7);
121
        }
122
123
        $favorites = $this->tagManager->load('analytics')->getFavorites();
124
        foreach ($ownReports as &$ownReport) {
125
            $hasTag = 0;
126
            if (is_array($favorites) and in_array($ownReport['id'], $favorites)) {
127
                $hasTag = 1;
128
            }
129
            $ownReport['favorite'] = $hasTag;
130
            $ownReport = $this->VariableService->replaceTextVariables($ownReport);
131
        }
132
133
        return $ownReports;
134
    }
135
136
    /**
137
     * get own report details
138
     *
139
     * @param int $reportId
140
     * @return array
141
     * @throws Exception
142
     */
143
    public function read(int $reportId, $replace = true)
144
    {
145
        $ownReport = $this->ReportMapper->read($reportId);
146
        if (!empty($ownReport)) {
147
            $ownReport['permissions'] = \OCP\Constants::PERMISSION_UPDATE;
148
            if ($replace) $ownReport = $this->VariableService->replaceTextVariables($ownReport);
149
150
            if ($ownReport['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB && $ownReport['dataset'] !== 0) {
151
                $dataset = $this->DatasetService->readOwn($ownReport['dataset']);
152
                $ownReport['dimension1'] = $dataset['dimension1'];
153
                $ownReport['dimension2'] = $dataset['dimension2'];
154
                $ownReport['value'] = $dataset['value'];
155
            }
156
157
        }
158
        return $ownReport;
159
    }
160
161
    /**
162
     * create new blank report
163
     *
164
     * @return int
165
     */
166
    public function create($name, $subheader, $parent, $type, int $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value): int
167
    {
168
        $array = json_decode($link, true);
169
        foreach ($array as $key => $value) {
0 ignored issues
show
introduced by
$value is overwriting one of the parameters of this function.
Loading history...
170
            $array[$key] = htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
171
        }
172
        $link = json_encode($array);
173
174
        if ($type === DatasourceController::DATASET_TYPE_GROUP) {
175
            $parent = 0;
176
        }
177
        if ($type === DatasourceController::DATASET_TYPE_INTERNAL_DB && $dataset === 0) { // New dataset
178
            $dataset = $this->DatasetService->create($name, $dimension1, $dimension2, $value);
179
        }
180
        $reportId = $this->ReportMapper->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value);
181
        $this->ActivityManager->triggerEvent($reportId, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_ADD);
182
        return $reportId;
183
    }
184
185
    /**
186
     * copy an existing report with the current navigation status
187
     *
188
     * @NoAdminRequired
189
     * @param int $reportId
190
     * @param $chartoptions
191
     * @param $dataoptions
192
     * @param $filteroptions
193
     * @return int
194
     * @throws Exception
195
     */
196
    public function createCopy(int $reportId, $chartoptions, $dataoptions, $filteroptions)
197
    {
198
199
        $template = $this->ReportMapper->read($reportId);
200
        $newId = $this->ReportMapper->create(
201
        // TRANSLATORS Noun
202
            $template['name'] . ' ' . $this->l10n->t('copy'),
203
            $template['subheader'],
204
            $template['parent'],
205
            $template['type'],
206
            $template['dataset'],
207
            $template['link'],
208
            $template['visualization'],
209
            $template['chart'],
210
            $template['dimension1'],
211
            $template['dimension2'],
212
            $template['value']);
213
        $this->ReportMapper->updateOptions($newId, $chartoptions, $dataoptions, $filteroptions);
214
        return $newId;
215
    }
216
217
    /**
218
     * create new report
219
     *
220
     * @param string $file
221
     * @return int
222
     */
223
    public function createFromDataFile($file = '')
224
    {
225
        $this->ActivityManager->triggerEvent(0, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_ADD);
226
227
        if ($file !== '') {
228
            $name = explode('.', end(explode('/', $file)))[0];
229
            $subheader = $file;
230
            $parent = 0;
231
            $dataset = 0;
232
            $type = DatasourceController::DATASET_TYPE_FILE;
233
            $link = $file;
234
            $visualization = 'table';
235
            $chart = 'line';
236
            $reportId = $this->ReportMapper->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, '', '', '');
237
        }
238
        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...
239
    }
240
241
    /**
242
     * update report details
243
     *
244
     * @param int $reportId
245
     * @param $name
246
     * @param $subheader
247
     * @param int $parent
248
     * @param $link
249
     * @param $visualization
250
     * @param $chart
251
     * @param $chartoptions
252
     * @param $dataoptions
253
     * @param $dimension1
254
     * @param $dimension2
255
     * @param $value
256
     * @return bool
257
     */
258
    public function update(int $reportId, $name, $subheader, int $parent, $link, $visualization, $chart, $chartoptions, $dataoptions, $dimension1 = null, $dimension2 = null, $value = null)
259
    {
260
        $array = json_decode($link, true);
261
        foreach ($array as $key => $value) {
262
            $array[$key] = htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
263
        }
264
        $link = json_encode($array);
265
266
        return $this->ReportMapper->update($reportId, $name, $subheader, $parent, $link, $visualization, $chart, $chartoptions, $dataoptions, $dimension1, $dimension2, $value);
267
    }
268
269
    /**
270
     * Delete Dataset and all depending objects
271
     *
272
     * @param int $reportId
273
     * @return string
274
     * @throws Exception
275
     */
276
    public function delete(int $reportId)
277
    {
278
        $metadata = $this->read($reportId);
279
        $this->ActivityManager->triggerEvent($reportId, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_DELETE);
280
        $this->ShareService->deleteShareByReport($reportId);
281
        $this->ThresholdMapper->deleteThresholdByReport($reportId);
282
        $this->setFavorite($reportId, 'false');
283
        $this->ReportMapper->delete($reportId);
284
285
        $report = $this->reportsForDataset((int)$metadata['dataset']);
286
        if (empty($report) && (int)$metadata['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB) {
287
            return $metadata['dataset'];
288
        } else {
289
            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...
290
        }
291
    }
292
293
    /**
294
     * get own reports which are marked as favorites
295
     *
296
     * @return array|bool
297
     */
298
    public function getOwnFavoriteReports()
299
    {
300
        $ownReports = $this->ReportMapper->index();
301
        $favorites = $this->tagManager->load('analytics')->getFavorites();
302
        $sharedReports = $this->ShareService->getSharedReports();
303
304
        foreach ($favorites as $favorite) {
305
            if (array_search($favorite, array_column($ownReports, 'id')) === false
306
                && array_search($favorite, array_column($sharedReports, 'id')) === false) {
307
                unset($favorites[$favorite]);
308
                $this->tagManager->load('analytics')->removeFromFavorites($favorite);
309
            }
310
        }
311
312
        return $favorites;
313
    }
314
315
    /**
316
     * set/remove the favorite flag for a report
317
     *
318
     * @param int $reportId
319
     * @param string $favorite
320
     * @return bool
321
     */
322
    public function setFavorite(int $reportId, string $favorite)
323
    {
324
        if ($favorite === 'true') {
325
            $return = $this->tagManager->load('analytics')->addToFavorites($reportId);
326
        } else {
327
            $return = $this->tagManager->load('analytics')->removeFromFavorites($reportId);
328
        }
329
        return $return;
330
    }
331
332
    /**
333
     * Import Report from File
334
     *
335
     * @param string|null $path
336
     * @param string|null $raw
337
     * @return int
338
     * @throws \OCP\Files\NotFoundException
339
     * @throws \OCP\Files\NotPermittedException
340
     */
341
    public function import(string $path = null, string $raw = null)
342
    {
343
        if ($path !== '') {
344
            $file = $this->rootFolder->getUserFolder($this->userId)->get($path);
345
            $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

345
            /** @scrutinizer ignore-call */ 
346
            $data = $file->getContent();
Loading history...
346
        } else if ($raw !== null) {
347
            $data = $raw;
348
        } else {
349
            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...
350
        }
351
        $data = json_decode($data, true);
352
353
        $report = $data['report'];
354
        isset($report['name']) ? $name = $report['name'] : $name = '';
355
        isset($report['subheader']) ? $subheader = $report['subheader'] : $subheader = '';
356
        $parent = 0;
357
        $dataset = 0;
358
        isset($report['type']) ? $type = $report['type'] : $type = null;
359
        isset($report['link']) ? $link = $report['link'] : $link = null;
360
        isset($report['visualization']) ? $visualization = $report['visualization'] : $visualization = null;
361
        isset($report['chart']) ? $chart = $report['chart'] : $chart = null;
362
        isset($report['chartoptions']) ? $chartoptions = $report['chartoptions'] : $chartoptions = null;
363
        isset($report['dataoptions']) ? $dataoptions = $report['dataoptions'] : $dataoptions = null;
364
        isset($report['filteroptions']) ? $filteroptions = $report['filteroptions'] : $filteroptions = null;
365
        isset($report['dimension1']) ? $dimension1 = $report['dimension1'] : $dimension1 = null;
366
        isset($report['dimension2']) ? $dimension2 = $report['dimension2'] : $dimension2 = null;
367
        isset($report['value']) ? $value = $report['value'] : $value = null;
368
369
        $reportId = $this->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value);
370
        $this->updateOptions($reportId, $chartoptions, $dataoptions, $filteroptions);
371
        $report = $this->ReportMapper->read($reportId);
372
        $datasetId = $report['dataset'];
373
374
        foreach ($data['dataload'] as $dataload) {
375
            isset($dataload['datasource']) ? $datasource = $dataload['datasource'] : $datasource = null;
376
            isset($dataload['name']) ? $name = $dataload['name'] : $name = null;
377
            isset($dataload['option']) ? $option = $dataload['option'] : $option = null;
378
            $schedule = null;
379
380
            $dataloadId = $this->DataloadMapper->create($datasetId, $datasource);
381
            $this->DataloadMapper->update($dataloadId, $name, $option, $schedule);
382
        }
383
384
        foreach ($data['threshold'] as $threshold) {
385
            isset($threshold['dimension1']) ? $dimension1 = $threshold['dimension1'] : $dimension1 = null;
386
            isset($threshold['value']) ? $value = $threshold['value'] : $value = null;
387
            isset($threshold['option']) ? $option = $threshold['option'] : $option = null;
388
            isset($threshold['severity']) ? $severity = $threshold['severity'] : $severity = null;
389
            $value = $this->floatvalue($value);
390
            $this->ThresholdMapper->create($reportId, $dimension1, $value, $option, $severity);
391
        }
392
393
        foreach ($data['data'] as $dData) {
394
            isset($dData[0]) ? $dimension1 = $dData[0] : $dimension1 = null;
395
            isset($dData[1]) ? $dimension2 = $dData[1] : $dimension2 = null;
396
            isset($dData[2]) ? $value = $dData[2] : $value = null;
397
            $this->StorageMapper->create($datasetId, $dimension1, $dimension2, $value);
398
        }
399
400
        if (isset($data['favorite'])) {
401
            $this->setFavorite($reportId, $data['favorite']);
402
        }
403
404
        return $reportId;
405
    }
406
407
    /**
408
     * Export Report
409
     *
410
     * @param int $reportId
411
     * @return DataDownloadResponse
412
     */
413
    public function export(int $reportId)
414
    {
415
        $result = array();
416
        $result['report'] = $this->ReportMapper->read($reportId);
417
        $datasetId = $result['report']['dataset'];
418
        $result['dataload'] = $this->DataloadMapper->read($datasetId);
419
        $result['threshold'] = $this->ThresholdMapper->getThresholdsByReport($reportId);
420
        $result['favorite'] = '';
421
422
        if ($result['report']['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB) {
423
            $result['data'] = $this->StorageMapper->read($datasetId);
424
        }
425
426
        unset($result['report']['id'], $result['report']['user_id'], $result['report']['user_id'], $result['report']['parent'], $result['report']['dataset']);
427
        $data = json_encode($result);
428
        return new DataDownloadResponse($data, $result['report']['name'] . '.export.txt', 'text/plain; charset=utf-8');
429
    }
430
431
    /**
432
     * Update report options
433
     *
434
     * @param int $reportId
435
     * @param $chartoptions
436
     * @param $dataoptions
437
     * @param $filteroptions
438
     * @return bool
439
     */
440
    public function updateOptions(int $reportId, $chartoptions, $dataoptions, $filteroptions)
441
    {
442
        return $this->ReportMapper->updateOptions($reportId, $chartoptions, $dataoptions, $filteroptions);
443
    }
444
445
    /**
446
     * get report refresh options
447
     *
448
     * @NoAdminRequired
449
     * @param int $reportId
450
     * @param $refresh
451
     * @return bool
452
     */
453
    public function updateRefresh(int $reportId, $refresh)
454
    {
455
        return $this->ReportMapper->updateRefresh($reportId, $refresh);
456
    }
457
458
    /**
459
     * search for reports
460
     *
461
     * @param string $searchString
462
     * @return array
463
     */
464
    public function search(string $searchString)
465
    {
466
        return $this->ReportMapper->search($searchString);
467
    }
468
469
    /**
470
     * @throws Exception
471
     */
472
    public function reportsForDataset($datasetId) {
473
        return $this->ReportMapper->reportsForDataset($datasetId);
474
    }
475
476
    /**
477
     * migrate old favorite ids
478
     *
479
     * @param $ownReports
480
     * @return bool
481
     */
482
    private function favoriteMigration($ownReports) {
483
        $favorites = $this->tagManager->load('analytics')->getFavorites();
484
        foreach ($favorites as $favorite) {
485
            $key = array_search($favorite, array_column($ownReports, 'dataset'));
486
            if ($key) {
487
                $this->logger->info('Favorite was migrated from '. $ownReports[$key]['dataset'] . ' to new report ' . $ownReports[$key]['id']);
488
                $this->tagManager->load('analytics')->removeFromFavorites($ownReports[$key]['dataset']);
489
                $this->tagManager->load('analytics')->addToFavorites($ownReports[$key]['id']);
490
            }
491
        }
492
        return true;
493
    }
494
495
    private function floatvalue($val)
496
    {
497
        $val = str_replace(",", ".", $val);
498
        $val = preg_replace('/\.(?=.*\.)/', '', $val);
499
        $val = preg_replace('/[^0-9-.]+/', '', $val);
500
        if (is_numeric($val)) {
501
            return number_format(floatval($val), 2, '.', '');
502
        } else {
503
            return false;
504
        }
505
    }
506
507
}
508