Passed
Push — master ( 0d32dd...265c4a )
by Marcel
02:32
created

ReportService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 13
c 2
b 0
f 0
nc 1
nop 13
dl 0
loc 29
rs 9.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 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\AppFramework\Http\DataResponse;
22
use OCP\Files\IRootFolder;
23
use OCP\ITagManager;
24
use OCP\IConfig;
25
use Psr\Log\LoggerInterface;
26
27
class ReportService
28
{
29
    /** @var IConfig */
30
    protected $config;
31
    private $userId;
32
    private $logger;
33
    private $tagManager;
34
    private $ShareService;
35
    private $DatasetService;
36
    private $StorageMapper;
37
    private $ReportMapper;
38
    private $ThresholdMapper;
39
    private $DataloadMapper;
40
    private $ActivityManager;
41
    private $rootFolder;
42
    private $VariableService;
43
44
    public function __construct(
45
        $userId,
46
        LoggerInterface $logger,
47
        ITagManager $tagManager,
48
        ShareService $ShareService,
49
        DatasetService $DatasetService,
50
        StorageMapper $StorageMapper,
51
        ReportMapper $ReportMapper,
52
        ThresholdMapper $ThresholdMapper,
53
        DataloadMapper $DataloadMapper,
54
        ActivityManager $ActivityManager,
55
        IRootFolder $rootFolder,
56
        IConfig $config,
57
        VariableService $VariableService
58
    )
59
    {
60
        $this->userId = $userId;
61
        $this->logger = $logger;
62
        $this->tagManager = $tagManager;
63
        $this->ShareService = $ShareService;
64
        $this->DatasetService = $DatasetService;
65
        $this->ThresholdMapper = $ThresholdMapper;
66
        $this->StorageMapper = $StorageMapper;
67
        $this->ReportMapper = $ReportMapper;
68
        $this->DataloadMapper = $DataloadMapper;
69
        $this->ActivityManager = $ActivityManager;
70
        $this->rootFolder = $rootFolder;
71
        $this->VariableService = $VariableService;
72
        $this->config = $config;
73
    }
74
75
    /**
76
     * get all reports
77
     *
78
     * @return DataResponse
79
     * @throws \OCP\PreConditionNotMetException
80
     */
81
    public function index()
82
    {
83
        $ownReports = $this->ReportMapper->index();
84
85
        // get dataload indicators for icons shown in the advanced screen
86
        $dataloads = $this->DataloadMapper->getAllDataloadMetadata();
87
        foreach ($dataloads as $dataload) {
88
            $key = array_search($dataload['dataset'], array_column($ownReports, 'dataset'));
89
            if ($key !== '') {
90
                if ($dataload['schedules'] !== '' and $dataload['schedules'] !== null) {
91
                    $dataload['schedules'] = 1;
92
                } else {
93
                    $dataload['schedules'] = 0;
94
                }
95
                $ownReports[$key]['dataloads'] = $dataload['dataloads'];
96
                $ownReports[$key]['schedules'] = $dataload['schedules'];
97
            }
98
        }
99
100
        // get shared reports and remove doublicates
101
        $sharedReports = $this->ShareService->getSharedReports();
102
        foreach ($sharedReports as $sharedReport) {
103
            if (!array_search($sharedReport['id'], array_column($ownReports, 'id'))) {
104
                $sharedReport['type'] = '99';
105
                $sharedReport['parrent'] = '0';
106
                array_push($ownReports, $sharedReport);
107
            }
108
        }
109
110
        $favoriteMigration = $this->config->getUserValue($this->userId, 'analytics', 'favMig', '0');
111
        if ($favoriteMigration === '0') {
112
            $this->logger->info('Favorite migration being performed');
113
            $this->favoriteMigration($ownReports);
114
            $this->config->setUserValue($this->userId, 'analytics', 'favMig', 3.7);
115
        }
116
117
        $favorites = $this->tagManager->load('analytics')->getFavorites();
118
        foreach ($ownReports as &$ownReport) {
119
            $hasTag = 0;
120
            if (is_array($favorites) and in_array($ownReport['id'], $favorites)) {
121
                $hasTag = 1;
122
            }
123
            $ownReport['favorite'] = $hasTag;
124
            $ownReport = $this->VariableService->replaceTextVariables($ownReport);
125
        }
126
127
        return $ownReports;
128
    }
129
130
    /**
131
     * migrate old favorite ids
132
     *
133
     * @param $ownReports
134
     * @return bool
135
     */
136
    private function favoriteMigration($ownReports) {
137
        $favorites = $this->tagManager->load('analytics')->getFavorites();
138
        foreach ($favorites as $favorite) {
139
            $key = array_search($favorite, array_column($ownReports, 'dataset'));
140
            if ($key) {
141
                $this->logger->info('Favorite was migrated from '. $ownReports[$key]['dataset'] . ' to new report ' . $ownReports[$key]['id']);
142
                $this->tagManager->load('analytics')->removeFromFavorites($ownReports[$key]['dataset']);
143
                $this->tagManager->load('analytics')->addToFavorites($ownReports[$key]['id']);
144
            }
145
        }
146
        return true;
147
    }
148
149
    /**
150
     * get own report details
151
     *
152
     * @param int $reportId
153
     * @param string|null $user_id
154
     * @return array
155
     */
156
    public function read(int $reportId, string $user_id = null)
157
    {
158
        $ownReport = $this->ReportMapper->read($reportId, $user_id);
159
        if (!empty($ownReport)) {
160
            $ownReport['permissions'] = \OCP\Constants::PERMISSION_UPDATE;
161
            $ownReport = $this->VariableService->replaceTextVariables($ownReport);
162
163
            if ($ownReport['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB && $ownReport['dataset'] !== 0) {
164
                $dataset = $this->DatasetService->read($ownReport['dataset'],  $user_id);
165
                $ownReport['dimension1'] = $dataset['dimension1'];
166
                $ownReport['dimension2'] = $dataset['dimension2'];
167
                $ownReport['value'] = $dataset['value'];
168
            }
169
170
        }
171
        return $ownReport;
172
    }
173
174
    /**
175
     * get own reports which are marked as favorites
176
     *
177
     * @return array|bool
178
     */
179
    public function getOwnFavoriteReports()
180
    {
181
        $ownReports = $this->ReportMapper->index();
182
        $favorites = $this->tagManager->load('analytics')->getFavorites();
183
        $sharedReports = $this->ShareService->getSharedReports();
184
185
        foreach ($favorites as $favorite) {
186
            if (array_search($favorite, array_column($ownReports, 'id')) === false
187
                && array_search($favorite, array_column($sharedReports, 'id')) === false) {
188
                unset($favorites[$favorite]);
189
                $this->tagManager->load('analytics')->removeFromFavorites($favorite);
190
            }
191
        }
192
193
        return $favorites;
194
    }
195
196
    /**
197
     * create new blank report
198
     *
199
     * @return int
200
     */
201
    public function create($name, $subheader, $parent, $type, int $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value): int
202
    {
203
        if ($type === DatasourceController::DATASET_TYPE_GROUP) {
204
            $parent = 0;
205
        }
206
        if ($type === DatasourceController::DATASET_TYPE_INTERNAL_DB && $dataset === 0) { // New dataset
207
            $dataset = $this->DatasetService->create($name, $dimension1, $dimension2, $value);
208
        }
209
        $reportId = $this->ReportMapper->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value);
210
        $this->ActivityManager->triggerEvent($reportId, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_ADD);
211
        return $reportId;
212
    }
213
214
    /**
215
     * copy an existing report with the current navigation status
216
     *
217
     * @NoAdminRequired
218
     * @param int $reportId
219
     * @param $chartoptions
220
     * @param $dataoptions
221
     * @param $filteroptions
222
     * @return int
223
     */
224
    public function createCopy(int $reportId, $chartoptions, $dataoptions, $filteroptions)
225
    {
226
227
        $newId = $this->ReportMapper->create();
0 ignored issues
show
Bug introduced by
The call to OCA\Analytics\Db\ReportMapper::create() has too few arguments starting with name. ( Ignorable by Annotation )

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

227
        /** @scrutinizer ignore-call */ 
228
        $newId = $this->ReportMapper->create();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
228
        $template = $this->ReportMapper->read($reportId);
229
        $this->ReportMapper->update($newId,
230
            $template['name'] . ' copy',
231
            $template['subheader'],
232
            $template['parent'],
233
            $template['type'],
234
            $template['dataset'],
235
            $template['link'],
236
            $template['visualization'],
237
            $template['chart'],
238
            $template['chartoptions'],
239
            $template['dataoptions'],
240
            $template['dimension1'],
241
            $template['dimension2'],
242
            $template['value']);
0 ignored issues
show
Unused Code introduced by
The call to OCA\Analytics\Db\ReportMapper::update() has too many arguments starting with $template['value']. ( Ignorable by Annotation )

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

242
        $this->ReportMapper->/** @scrutinizer ignore-call */ 
243
                             update($newId,

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
243
        $this->ReportMapper->updateOptions($newId, $chartoptions, $dataoptions, $filteroptions);
244
        return $newId;
245
    }
246
247
    /**
248
     * create new report
249
     *
250
     * @param string $file
251
     * @return int
252
     */
253
    public function createFromDataFile($file = '')
254
    {
255
        $this->ActivityManager->triggerEvent(0, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_ADD);
256
257
        if ($file !== '') {
258
            $name = explode('.', end(explode('/', $file)))[0];
259
            $subheader = $file;
260
            $parent = 0;
261
            $dataset = 0;
262
            $type = DatasourceController::DATASET_TYPE_FILE;
263
            $link = $file;
264
            $visualization = 'table';
265
            $chart = 'line';
266
            $reportId = $this->ReportMapper->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, '', '', '');
267
        }
268
        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...
269
    }
270
271
    /**
272
     * update report details
273
     *
274
     * @param int $reportId
275
     * @param $name
276
     * @param $subheader
277
     * @param int $parent
278
     * @param $link
279
     * @param $visualization
280
     * @param $chart
281
     * @param $chartoptions
282
     * @param $dataoptions
283
     * @param null $dimension1
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $dimension1 is correct as it would always require null to be passed?
Loading history...
284
     * @param null $dimension2
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $dimension2 is correct as it would always require null to be passed?
Loading history...
285
     * @param null $value
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
286
     * @return bool
287
     * @throws \OCP\DB\Exception
288
     */
289
    public function update(int $reportId, $name, $subheader, int $parent, $link, $visualization, $chart, $chartoptions, $dataoptions, $dimension1 = null, $dimension2 = null, $value = null)
290
    {
291
        return $this->ReportMapper->update($reportId, $name, $subheader, $parent, $link, $visualization, $chart, $chartoptions, $dataoptions, $dimension1, $dimension2, $value);
292
    }
293
294
    /**
295
     * set/remove the favorite flag for a report
296
     *
297
     * @param int $reportId
298
     * @param string $favorite
299
     * @return bool
300
     */
301
    public function setFavorite(int $reportId, string $favorite)
302
    {
303
        if ($favorite === 'true') {
304
            $return = $this->tagManager->load('analytics')->addToFavorites($reportId);
305
        } else {
306
            $return = $this->tagManager->load('analytics')->removeFromFavorites($reportId);
307
        }
308
        return $return;
309
    }
310
311
    /**
312
     * Import Report from File
313
     *
314
     * @param string|null $path
315
     * @param string|null $raw
316
     * @return int
317
     * @throws \OCP\Files\NotFoundException
318
     * @throws \OCP\Files\NotPermittedException
319
     */
320
    public function import(string $path = null, string $raw = null)
321
    {
322
        if ($path !== '') {
323
            $file = $this->rootFolder->getUserFolder($this->userId)->get($path);
324
            $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

324
            /** @scrutinizer ignore-call */ 
325
            $data = $file->getContent();
Loading history...
325
        } else if ($raw !== null) {
326
            $data = $raw;
327
        } else {
328
            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...
329
        }
330
        $data = json_decode($data, true);
331
332
        $report = $data['report'];
333
        isset($report['name']) ? $name = $report['name'] : $name = '';
334
        isset($report['subheader']) ? $subheader = $report['subheader'] : $subheader = '';
335
        $parent = 0;
336
        $dataset = 0;
337
        isset($report['type']) ? $type = $report['type'] : $type = null;
338
        isset($report['link']) ? $link = $report['link'] : $link = null;
339
        isset($report['visualization']) ? $visualization = $report['visualization'] : $visualization = null;
340
        isset($report['chart']) ? $chart = $report['chart'] : $chart = null;
341
        isset($report['chartoptions']) ? $chartoptions = $report['chartoptions'] : $chartoptions = null;
342
        isset($report['dataoptions']) ? $dataoptions = $report['dataoptions'] : $dataoptions = null;
343
        isset($report['filteroptions']) ? $filteroptions = $report['filteroptions'] : $filteroptions = null;
344
        isset($report['dimension1']) ? $dimension1 = $report['dimension1'] : $dimension1 = null;
345
        isset($report['dimension2']) ? $dimension2 = $report['dimension2'] : $dimension2 = null;
346
        isset($report['value']) ? $value = $report['value'] : $value = null;
347
348
        $reportId = $this->create($name, $subheader, $parent, $type, $dataset, $link, $visualization, $chart, $dimension1, $dimension2, $value);
349
        $this->updateOptions($reportId, $chartoptions, $dataoptions, $filteroptions);
350
        $report = $this->ReportMapper->read($reportId);
351
        $datasetId = $report['dataset'];
352
353
        foreach ($data['dataload'] as $dataload) {
354
            isset($dataload['datasource']) ? $datasource = $dataload['datasource'] : $datasource = null;
355
            isset($dataload['name']) ? $name = $dataload['name'] : $name = null;
356
            isset($dataload['option']) ? $option = $dataload['option'] : $option = null;
357
            $schedule = null;
358
359
            $dataloadId = $this->DataloadMapper->create($datasetId, $datasource);
360
            $this->DataloadMapper->update($dataloadId, $name, $option, $schedule);
361
        }
362
363
        foreach ($data['threshold'] as $threshold) {
364
            isset($threshold['dimension1']) ? $dimension1 = $threshold['dimension1'] : $dimension1 = null;
365
            isset($threshold['value']) ? $value = $threshold['value'] : $value = null;
366
            isset($threshold['option']) ? $option = $threshold['option'] : $option = null;
367
            isset($threshold['severity']) ? $severity = $threshold['severity'] : $severity = null;
368
            $value = $this->floatvalue($value);
369
            $this->ThresholdMapper->create($reportId, $dimension1, $value, $option, $severity);
370
        }
371
372
        foreach ($data['data'] as $dData) {
373
            isset($dData[0]) ? $dimension1 = $dData[0] : $dimension1 = null;
374
            isset($dData[1]) ? $dimension2 = $dData[1] : $dimension2 = null;
375
            isset($dData[2]) ? $value = $dData[2] : $value = null;
376
            $this->StorageMapper->create($datasetId, $dimension1, $dimension2, $value);
377
        }
378
379
        if (isset($data['favorite'])) {
380
            $this->setFavorite($reportId, $data['favorite']);
381
        }
382
383
        return $reportId;
384
    }
385
386
    private function floatvalue($val)
387
    {
388
        $val = str_replace(",", ".", $val);
389
        $val = preg_replace('/\.(?=.*\.)/', '', $val);
390
        $val = preg_replace('/[^0-9-.]+/', '', $val);
391
        if (is_numeric($val)) {
392
            return number_format(floatval($val), 2, '.', '');
393
        } else {
394
            return false;
395
        }
396
    }
397
398
    /**
399
     * Export Report
400
     *
401
     * @param int $reportId
402
     * @return DataDownloadResponse
403
     */
404
    public function export(int $reportId)
405
    {
406
        $result = array();
407
        $result['report'] = $this->ReportMapper->read($reportId);
408
        $datasetId = $result['report']['dataset'];
409
        $result['dataload'] = $this->DataloadMapper->read($datasetId);
410
        $result['threshold'] = $this->ThresholdMapper->getThresholdsByReport($reportId);
411
        $result['favorite'] = '';
412
413
        if ($result['report']['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB) {
414
            $result['data'] = $this->StorageMapper->read($datasetId);
415
        }
416
417
        unset($result['report']['id'], $result['report']['user_id'], $result['report']['user_id'], $result['report']['parent'], $result['report']['dataset']);
418
        $data = json_encode($result);
419
        return new DataDownloadResponse($data, $result['report']['name'] . '.export.txt', 'text/plain; charset=utf-8');
420
    }
421
422
    /**
423
     * Delete Dataset and all depending objects
424
     *
425
     * @param int $reportId
426
     * @return bool
427
     */
428
    public function delete(int $reportId): bool
429
    {
430
        $metadata = $this->read($reportId);
431
        $this->ActivityManager->triggerEvent($reportId, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_DELETE);
432
        $this->ShareService->deleteShareByReport($reportId);
433
        $this->ThresholdMapper->deleteThresholdByReport($reportId);
434
        $this->setFavorite($reportId, 'false');
435
        $this->ReportMapper->delete($reportId);
436
437
        if (empty($this->reportsForDataset($metadata['dataset'])) && $metadata['type'] === DatasourceController::DATASET_TYPE_INTERNAL_DB) {
438
            return false;
439
        } else {
440
            return true;
441
        }
442
    }
443
444
    /**
445
     * Update report options
446
     *
447
     * @param int $reportId
448
     * @param $chartoptions
449
     * @param $dataoptions
450
     * @param $filteroptions
451
     * @return bool
452
     */
453
    public function updateOptions(int $reportId, $chartoptions, $dataoptions, $filteroptions)
454
    {
455
        return $this->ReportMapper->updateOptions($reportId, $chartoptions, $dataoptions, $filteroptions);
456
    }
457
458
    /**
459
     * get report refresh options
460
     *
461
     * @NoAdminRequired
462
     * @param int $reportId
463
     * @param $refresh
464
     * @return bool
465
     */
466
    public function updateRefresh(int $reportId, $refresh)
467
    {
468
        return $this->ReportMapper->updateRefresh($reportId, $refresh);
469
    }
470
471
    /**
472
     * search for reports
473
     *
474
     * @param string $searchString
475
     * @return array
476
     */
477
    public function search(string $searchString)
478
    {
479
        return $this->ReportMapper->search($searchString);
480
    }
481
482
    /**
483
     * @throws \OCP\DB\Exception
484
     */
485
    public function reportsForDataset($datasetId) {
486
        return $this->ReportMapper->reportsForDataset($datasetId);
487
    }
488
}