Passed
Push — master ( 96ed6e...d5b540 )
by Marcel
05:09 queued 02:42
created

DataloadController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 10
dl 0
loc 22
rs 9.9666
c 0
b 0
f 0

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 2020 Marcel Scherello
10
 */
11
12
namespace OCA\Analytics\Controller;
13
14
use Exception;
15
use OCA\Analytics\Activity\ActivityManager;
16
use OCA\Analytics\Db\DataloadMapper;
17
use OCP\AppFramework\Controller;
18
use OCP\AppFramework\Http\DataResponse;
19
use OCP\AppFramework\Http\NotFoundResponse;
20
use OCP\Files\NotFoundException;
21
use OCP\IL10N;
22
use OCP\ILogger;
23
use OCP\IRequest;
24
25
class DataloadController extends Controller
26
{
27
    private $logger;
28
    private $StorageController;
29
    private $DataSourceController;
30
    private $userId;
31
    private $ActivityManager;
32
    private $DatasetController;
33
    private $l10n;
34
    private $DataloadMapper;
35
36
    public function __construct(
37
        string $AppName,
38
        IRequest $request,
39
        IL10N $l10n,
40
        $userId,
41
        ILogger $logger,
42
        ActivityManager $ActivityManager,
43
        DataSourceController $DataSourceController,
44
        DatasetController $DatasetController,
45
        StorageController $StorageController,
46
        DataloadMapper $DataloadMapper
47
    )
48
    {
49
        parent::__construct($AppName, $request);
50
        $this->l10n = $l10n;
51
        $this->userId = $userId;
52
        $this->logger = $logger;
53
        $this->StorageController = $StorageController;
54
        $this->ActivityManager = $ActivityManager;
55
        $this->DataSourceController = $DataSourceController;
56
        $this->DatasetController = $DatasetController;
57
        $this->DataloadMapper = $DataloadMapper;
58
    }
59
60
    // Dataloads
61
    // Dataloads
62
    // Dataloads
63
64
    /**
65
     * create a new dataload
66
     *
67
     * @NoAdminRequired
68
     * @param int $datasetId
69
     * @param int $datasourceId
70
     * @return DataResponse
71
     */
72
    public function create(int $datasetId, int $datasourceId)
73
    {
74
        return new DataResponse(['id' => $this->DataloadMapper->create($datasetId, $datasourceId)]);
75
    }
76
77
    /**
78
     * get all dataloads for a dataset
79
     *
80
     * @NoAdminRequired
81
     * @param int $datasetId
82
     * @return DataResponse
83
     */
84
    public function read(int $datasetId)
85
    {
86
        $result = array();
87
        $result['dataloads'] = $this->DataloadMapper->read($datasetId);
88
        return new DataResponse($result);
89
    }
90
91
    /**
92
     * update dataload
93
     *
94
     * @NoAdminRequired
95
     * @param int $dataloadId
96
     * @param $name
97
     * @param $option
98
     * @param $schedule
99
     * @return DataResponse
100
     */
101
    public function update(int $dataloadId, $name, $option, $schedule)
102
    {
103
        return new DataResponse(['update' => $this->DataloadMapper->update($dataloadId, $name, $option, $schedule)]);
104
    }
105
106
    /**
107
     * delete a dataload
108
     *
109
     * @NoAdminRequired
110
     * @param int $dataloadId
111
     * @return bool
112
     */
113
    public function delete(int $dataloadId)
114
    {
115
        return $this->DataloadMapper->delete($dataloadId);
116
    }
117
118
    /**
119
     * simulate a dataload and output its data
120
     *
121
     * @NoAdminRequired
122
     * @param int $dataloadId
123
     * @return DataResponse
124
     * @throws NotFoundException
125
     */
126
    public function simulate(int $dataloadId)
127
    {
128
        $result = $this->getDataFromDatasource($dataloadId);
129
        return new DataResponse($result);
130
    }
131
132
    /**
133
     * execute all dataloads depending on their schedule
134
     * daily or hourly
135
     *
136
     * @NoAdminRequired
137
     * @param $schedule
138
     * @return void
139
     * @throws Exception
140
     */
141
    public function executeBySchedule($schedule)
142
    {
143
        $schedules = $this->DataloadMapper->getDataloadBySchedule($schedule);
144
        //$this->logger->debug('DataLoadController 145: execute schedule '.$schedule);
145
        foreach ($schedules as $dataload) {
146
            //$this->logger->debug('DataLoadController 147: execute dataload '.$dataload['id']);
147
            $this->execute($dataload['id']);
148
        }
149
    }
150
151
    /**
152
     * execute a dataload from datasource and store into dataset
153
     *
154
     * @NoAdminRequired
155
     * @param int $dataloadId
156
     * @return DataResponse
157
     * @throws Exception
158
     */
159
    public function execute(int $dataloadId)
160
    {
161
        $dataloadMetadata = $this->DataloadMapper->getDataloadById($dataloadId);
162
        $result = $this->getDataFromDatasource($dataloadId);
163
        $insert = $update = 0;
164
        $datasetId = $result['datasetId'];
165
        $option = json_decode($dataloadMetadata['option'], true);
166
167
        if (isset($option['delete']) and $option['delete'] === 'true') {
168
            $this->StorageController->delete($datasetId, '*', '*');
169
        }
170
        if ($result['error'] === 0) {
171
            foreach ($result['data'] as &$row) {
172
                if (count($row) === 2) {
173
                    // if datasource only delivers 2 colums, the value needs to be in the last one
174
                    $row[2] = $row[1];
175
                    $row[1] = null;
176
                }
177
                $action = $this->StorageController->update($datasetId, $row[0], $row[1], $row[2], $dataloadMetadata['user_id']);
178
                $insert = $insert + $action['insert'];
179
                $update = $update + $action['update'];
180
            }
181
        }
182
183
        $result = [
184
            'insert' => $insert,
185
            'update' => $update,
186
            'error' => $result['error'],
187
        ];
188
189
        if ($result['error'] === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_DATALOAD, $dataloadMetadata['user_id']);
190
191
        return new DataResponse($result);
192
    }
193
194
    /**
195
     * get the data from datasource
196
     * to be used in simulation or execution
197
     *
198
     * @NoAdminRequired
199
     * @param int $dataloadId
200
     * @return array|NotFoundResponse
201
     * @throws NotFoundResponse|NotFoundException
202
     */
203
    private function getDataFromDatasource(int $dataloadId)
204
    {
205
        $dataloadMetadata = $this->DataloadMapper->getDataloadById($dataloadId);
206
        $datasetMetadata = $this->DatasetController->getOwnDataset($dataloadMetadata['dataset'], $dataloadMetadata['user_id']);
207
208
        if (!empty($datasetMetadata)) {
209
            $option = json_decode($dataloadMetadata['option'], true);
210
            $option['user_id'] = $dataloadMetadata['user_id'];
211
212
            //$this->logger->debug('DataLoadController 187: ' . $dataloadMetadata['option'] . '---' . json_encode($option));
213
            $result = $this->DataSourceController->read((int)$dataloadMetadata['datasource'], $option);
214
            $result['datasetId'] = $dataloadMetadata['dataset'];
215
216
            if (isset($option['timestamp']) and $option['timestamp'] === 'true') {
217
                // if datasource should be timestamped/snapshoted
218
                // shift values by one dimension and stores date in second column
219
                $result['data'] = array_map(function ($tag) {
220
                    $columns = count($tag);
221
                    return array($tag[$columns - 2], $tag[$columns - 2], $tag[$columns - 1]);
222
                }, $result['data']);
223
                $result['data'] = $this->replaceDimension($result['data'], 1, date("Y-m-d H:i:s"));
224
            }
225
226
            return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type OCP\Files\NotFoundException which is incompatible with the documented return type OCP\AppFramework\Http\NotFoundResponse|array.
Loading history...
227
        } else {
228
            return new NotFoundResponse();
229
        }
230
    }
231
232
    /**
233
     * replace all values of one dimension
234
     *
235
     * @NoAdminRequired
236
     * @param $Array
237
     * @param $Find
238
     * @param $Replace
239
     * @return array
240
     */
241
    private function replaceDimension($Array, $Find, $Replace)
242
    {
243
        if (is_array($Array)) {
244
            foreach ($Array as $Key => $Val) {
245
                if (is_array($Array[$Key])) {
246
                    $Array[$Key] = $this->replaceDimension($Array[$Key], $Find, $Replace);
247
                } else {
248
                    if ($Key === $Find) {
249
                        $Array[$Key] = $Replace;
250
                    }
251
                }
252
            }
253
        }
254
        return $Array;
255
    }
256
257
    // Data Manipulation
258
    // Data Manipulation
259
    // Data Manipulation
260
261
    /**
262
     * update data from input form
263
     *
264
     * @NoAdminRequired
265
     * @param int $datasetId
266
     * @param $dimension1
267
     * @param $dimension2
268
     * @param $value
269
     * @return DataResponse|NotFoundResponse
270
     * @throws Exception
271
     */
272
    public function updateData(int $datasetId, $dimension1, $dimension2, $value)
273
    {
274
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
275
        if (!empty($datasetMetadata)) {
276
            $insert = $update = $errorMessage = 0;
277
            $action = array();
278
            $value = $this->floatvalue($value);
279
            if ($value === false) {
280
                $errorMessage = $this->l10n->t('3rd field must be a valid number');
281
            } else {
282
                $action = $this->StorageController->update($datasetId, $dimension1, $dimension2, $value);
283
                $insert = $insert + $action['insert'];
284
                $update = $update + $action['update'];
285
            }
286
287
            $result = [
288
                'insert' => $insert,
289
                'update' => $update,
290
                'error' => $errorMessage,
291
                'validate' => $action['validate'],
292
            ];
293
294
            //$this->logger->error('DataLoadController 88:'.$errorMessage);
295
            if ($errorMessage === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD);
296
            return new DataResponse($result);
297
        } else {
298
            return new NotFoundResponse();
299
        }
300
    }
301
302
    /**
303
     * delete data from input form
304
     *
305
     * @NoAdminRequired
306
     * @param int $datasetId
307
     * @param $dimension1
308
     * @param $dimension2
309
     * @return DataResponse|NotFoundResponse
310
     */
311
    public function deleteData(int $datasetId, $dimension1, $dimension2)
312
    {
313
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
314
        if (!empty($datasetMetadata)) {
315
            $result = $this->StorageController->delete($datasetId, $dimension1, $dimension2);
316
            return new DataResponse(['delete' => $result]);
317
        } else {
318
            return new NotFoundResponse();
319
        }
320
    }
321
322
    /**
323
     * Simulate delete data from input form
324
     *
325
     * @NoAdminRequired
326
     * @param int $datasetId
327
     * @param $dimension1
328
     * @param $dimension2
329
     * @return DataResponse|NotFoundResponse
330
     */
331
    public function deleteDataSimulate(int $datasetId, $dimension1, $dimension2)
332
    {
333
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
334
        if (!empty($datasetMetadata)) {
335
            $result = $this->StorageController->deleteSimulate($datasetId, $dimension1, $dimension2);
336
            return new DataResponse(['delete' => $result]);
337
        } else {
338
            return new NotFoundResponse();
339
        }
340
    }
341
342
    /**
343
     * Import clipboard data
344
     *
345
     * @NoAdminRequired
346
     * @param int $datasetId
347
     * @param $import
348
     * @return DataResponse|NotFoundResponse
349
     * @throws Exception
350
     */
351
    public function importClipboard($datasetId, $import)
352
    {
353
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
354
        if (!empty($datasetMetadata)) {
355
            $insert = $update = $errorMessage = $errorCounter = 0;
356
            $delimiter = $this->detectDelimiter($import);
357
            $rows = str_getcsv($import, "\n");
358
359
            foreach ($rows as &$row) {
360
                $row = str_getcsv($row, $delimiter);
361
                $row[2] = $this->floatvalue($row[2]);
362
                if ($row[2] === false) {
363
                    $errorCounter++;
364
                } else {
365
                    $action = $this->StorageController->update($datasetId, $row[0], $row[1], $row[2]);
366
                    $insert = $insert + $action['insert'];
367
                    $update = $update + $action['update'];
368
                }
369
                if ($errorCounter === 2) {
370
                    // first error is ignored; might be due to header row
371
                    $errorMessage = $this->l10n->t('3rd field must be a valid number');
372
                    break;
373
                }
374
            }
375
376
            $result = [
377
                'insert' => $insert,
378
                'update' => $update,
379
                'delimiter' => $delimiter,
380
                'error' => $errorMessage,
381
            ];
382
383
            if ($errorMessage === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_IMPORT);
384
            return new DataResponse($result);
385
        } else {
386
            return new NotFoundResponse();
387
        }
388
    }
389
390
    /**
391
     * Import data into dataset from an internal or external file
392
     *
393
     * @NoAdminRequired
394
     * @param int $datasetId
395
     * @param $path
396
     * @return DataResponse|NotFoundResponse
397
     * @throws NotFoundException
398
     * @throws Exception
399
     */
400
    public function importFile(int $datasetId, $path)
401
    {
402
        //$this->logger->debug('DataLoadController 378:' . $datasetId . $path);
403
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
404
        if (!empty($datasetMetadata)) {
405
            $insert = $update = 0;
406
            $option = array();
407
            $option['user_id'] = $datasetMetadata['user_id'];
408
            $option['path'] = $path;
409
            $option['link'] = $datasetMetadata['link'];
410
            $result = $this->DataSourceController->read(DataSourceController::DATASET_TYPE_INTERNAL_FILE, $option);
411
412
            if ($result['error'] === 0) {
413
                foreach ($result['data'] as &$row) {
414
                    $action = $this->StorageController->update($datasetId, $row[0], $row[1], $row[2]);
415
                    $insert = $insert + $action['insert'];
416
                    $update = $update + $action['update'];
417
                }
418
            }
419
420
            $result = [
421
                'insert' => $insert,
422
                'update' => $update,
423
                'error' => $result['error'],
424
            ];
425
426
            if ($result['error'] === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_IMPORT);
427
            return new DataResponse($result);
428
        } else {
429
            return new NotFoundResponse();
430
        }
431
    }
432
433
    private function detectDelimiter($data)
434
    {
435
        $delimiters = ["\t", ";", "|", ","];
436
        $data_2 = null;
437
        $delimiter = $delimiters[0];
438
        foreach ($delimiters as $d) {
439
            $firstRow = str_getcsv($data, "\n")[0];
440
            $data_1 = str_getcsv($firstRow, $d);
441
            if (sizeof($data_1) > sizeof($data_2)) {
442
                $delimiter = $d;
443
                $data_2 = $data_1;
444
            }
445
        }
446
        return $delimiter;
447
    }
448
449
    private function floatvalue($val)
450
    {
451
        $val = str_replace(",", ".", $val);
452
        $val = preg_replace('/\.(?=.*\.)/', '', $val);
453
        $val = preg_replace('/[^0-9-.]+/', '', $val);
454
        if (is_numeric($val)) {
455
            return number_format(floatval($val), 2, '.', '');
456
        } else {
457
            return false;
458
        }
459
    }
460
}