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

ReportService::updateRefresh()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
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
}