Passed
Push — master ( 6c703d...3d333b )
by Marcel
03:19
created

ReportService::favoriteMigration()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

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