Passed
Push — master ( 3334e1...093f01 )
by Marcel
02:14
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
 * Data 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 Marcel Scherello
10
 */
11
12
namespace OCA\Analytics\Controller;
13
14
use OCA\Analytics\Activity\ActivityManager;
15
use OCA\Analytics\Db\DataloadMapper;
16
use OCP\AppFramework\Controller;
17
use OCP\AppFramework\Http\DataResponse;
18
use OCP\AppFramework\Http\NotFoundResponse;
19
use OCP\Files\NotFoundException;
20
use OCP\IL10N;
21
use OCP\ILogger;
22
use OCP\IRequest;
23
24
class DataloadController extends Controller
25
{
26
    private $logger;
27
    private $StorageController;
28
    private $DataSourceController;
29
    private $userId;
30
    private $ActivityManager;
31
    private $DatasetController;
32
    private $l10n;
33
    private $DataloadMapper;
34
35
    public function __construct(
36
        string $AppName,
37
        IRequest $request,
38
        IL10N $l10n,
39
        $userId,
40
        ILogger $logger,
41
        ActivityManager $ActivityManager,
42
        DataSourceController $DataSourceController,
43
        DatasetController $DatasetController,
44
        StorageController $StorageController,
45
        DataloadMapper $DataloadMapper
46
    )
47
    {
48
        parent::__construct($AppName, $request);
49
        $this->l10n = $l10n;
50
        $this->userId = $userId;
51
        $this->logger = $logger;
52
        $this->StorageController = $StorageController;
53
        $this->ActivityManager = $ActivityManager;
54
        $this->DataSourceController = $DataSourceController;
55
        $this->DatasetController = $DatasetController;
56
        $this->DataloadMapper = $DataloadMapper;
57
    }
58
59
    // Dataloads
60
    // Dataloads
61
    // Dataloads
62
63
    /**
64
     * create a new dataload
65
     *
66
     * @NoAdminRequired
67
     * @param int $datasetId
68
     * @param int $datasourceId
69
     * @return DataResponse
70
     */
71
    public function create(int $datasetId, int $datasourceId)
72
    {
73
        return new DataResponse($this->DataloadMapper->create($datasetId, $datasourceId));
0 ignored issues
show
Bug introduced by
$this->DataloadMapper->c...tasetId, $datasourceId) of type integer is incompatible with the type array|object expected by parameter $data of OCP\AppFramework\Http\DataResponse::__construct(). ( Ignorable by Annotation )

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

73
        return new DataResponse(/** @scrutinizer ignore-type */ $this->DataloadMapper->create($datasetId, $datasourceId));
Loading history...
74
    }
75
76
    /**
77
     * get all dataloads for a dataset
78
     *
79
     * @NoAdminRequired
80
     * @param int $datasetId
81
     * @return DataResponse
82
     */
83
    public function read(int $datasetId)
84
    {
85
        $result['dataloads'] = $this->DataloadMapper->read($datasetId);;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
86
        $result['templates'] = $this->DataSourceController->getTemplates();
87
        return new DataResponse($result);
88
    }
89
90
    /**
91
     * update dataload
92
     *
93
     * @NoAdminRequired
94
     * @param int $dataloadId
95
     * @param $name
96
     * @param $option
97
     * @param $schedule
98
     * @return DataResponse
99
     */
100
    public function update(int $dataloadId, $name, $option, $schedule)
101
    {
102
        return new DataResponse($this->DataloadMapper->update($dataloadId, $name, $option, $schedule));
0 ignored issues
show
Bug introduced by
$this->DataloadMapper->u...me, $option, $schedule) of type true is incompatible with the type array|object expected by parameter $data of OCP\AppFramework\Http\DataResponse::__construct(). ( Ignorable by Annotation )

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

102
        return new DataResponse(/** @scrutinizer ignore-type */ $this->DataloadMapper->update($dataloadId, $name, $option, $schedule));
Loading history...
103
    }
104
105
    /**
106
     * delete a dataload
107
     *
108
     * @NoAdminRequired
109
     * @param int $dataloadId
110
     * @return bool
111
     */
112
    public function delete(int $dataloadId)
113
    {
114
        return $this->DataloadMapper->delete($dataloadId);
115
    }
116
117
    /**
118
     * simulate a dataload and output its data
119
     *
120
     * @NoAdminRequired
121
     * @param int $dataloadId
122
     * @return DataResponse
123
     * @throws NotFoundException
124
     */
125
    public function simulate(int $dataloadId)
126
    {
127
        $result = $this->getDataFromDatasource($dataloadId);
128
        return new DataResponse($result);
129
    }
130
131
    /**
132
     * execute all dataloads depending on their schedule
133
     * daily or hourly
134
     *
135
     * @NoAdminRequired
136
     * @param $schedule
137
     * @return void
138
     * @throws \Exception
139
     */
140
    public function executeBySchedule($schedule)
141
    {
142
        $schedules = $this->DataloadMapper->getDataloadBySchedule($schedule);
143
        //$this->logger->debug('DataLoadController 143: execute schedule '.$schedule);
144
        foreach ($schedules as $dataload) {
145
            //$this->logger->debug('DataLoadController 145: execute dataload '.$dataload['id']);
146
            $this->execute($dataload['id']);
147
        }
148
    }
149
150
    /**
151
     * execute a dataload from datasource and store into dataset
152
     *
153
     * @NoAdminRequired
154
     * @param int $dataloadId
155
     * @return DataResponse
156
     * @throws \Exception
157
     */
158
    public function execute(int $dataloadId)
159
    {
160
        //$this->logger->debug('DataLoadController 143:'.$dataloadId);
161
        $dataloadMetadata = $this->DataloadMapper->getDataloadById($dataloadId);
162
        $result = $this->getDataFromDatasource($dataloadId);
163
        $insert = $update = 0;
164
        $datasetId = $result['datasetId'];
165
        //$this->logger->debug('DataLoadController 146: loading into dataset '.$datasetId);
166
167
        if ($result['error'] === 0) {
168
            //$this->logger->debug('DataLoadController 149: OK');
169
            foreach ($result['data'] as &$row) {
170
                $action = $this->StorageController->update($datasetId, $row['dimension1'], $row['dimension2'], $row['dimension3'], $dataloadMetadata['user_id']);
171
                $insert = $insert + $action['insert'];
172
                $update = $update + $action['update'];
173
            }
174
        }
175
176
        $result = [
177
            'insert' => $insert,
178
            'update' => $update,
179
            'error' => $result['error'],
180
        ];
181
182
        if ($result['error'] === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_DATALOAD, $dataloadMetadata['user_id']);
183
184
        return new DataResponse($result);
185
    }
186
187
    /**
188
     * get the data from datasource
189
     * to be used in simulation or execution
190
     *
191
     * @NoAdminRequired
192
     * @param int $dataloadId
193
     * @return array
194
     * @throws NotFoundException
195
     */
196
    private function getDataFromDatasource(int $dataloadId)
197
    {
198
        $dataloadMetadata = $this->DataloadMapper->getDataloadById($dataloadId);
199
        $datasetMetadata = $this->DatasetController->getOwnDataset($dataloadMetadata['dataset'], $dataloadMetadata['user_id']);
200
201
        if (!empty($datasetMetadata)) {
202
            $option = json_decode($dataloadMetadata['option'], true);
203
            $option['user_id'] = $dataloadMetadata['user_id'];
204
205
            //$this->logger->debug('DataLoadController 187: ' . $dataloadMetadata['option'] . '---' . json_encode($option));
206
            $result = $this->DataSourceController->read((int)$dataloadMetadata['datasource'], $option);
207
            $result['datasetId'] = $dataloadMetadata['dataset'];
208
209
            if (isset($option['timestamp']) and $option['timestamp'] === 'true') {
210
                // if datasource should be timestamped/snapshoted
211
                // shift values by one dimension
212
                $result['data'] = array_map(function ($tag) {
213
                    return array(
214
                        'dimension1' => $tag['dimension2'],
215
                        'dimension2' => $tag['dimension2'],
216
                        'dimension3' => $tag['dimension3']
217
                    );
218
                }, $result['data']);
219
                $result['data'] = $this->replaceDimension($result['data'], 'dimension2', date("Y-m-d H:i:s"));
220
            }
221
222
            return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result returns the type OCP\Files\NotFoundException which is incompatible with the documented return type array.
Loading history...
223
        } else {
224
            return new NotFoundResponse();
0 ignored issues
show
Bug Best Practice introduced by
The expression return new OCP\AppFramew...Http\NotFoundResponse() returns the type OCP\AppFramework\Http\NotFoundResponse which is incompatible with the documented return type array.
Loading history...
225
        }
226
    }
227
228
    /**
229
     * replace all values of one dimension
230
     *
231
     * @NoAdminRequired
232
     * @param $Array
233
     * @param $Find
234
     * @param $Replace
235
     * @return array
236
     */
237
    private function replaceDimension($Array, $Find, $Replace)
238
    {
239
        if (is_array($Array)) {
240
            foreach ($Array as $Key => $Val) {
241
                if (is_array($Array[$Key])) {
242
                    $Array[$Key] = $this->replaceDimension($Array[$Key], $Find, $Replace);
243
                } else {
244
                    if ($Key == $Find) {
245
                        $Array[$Key] = $Replace;
246
                    }
247
                }
248
            }
249
        }
250
        return $Array;
251
    }
252
253
    // Data Manipulation
254
    // Data Manipulation
255
    // Data Manipulation
256
257
    /**
258
     * update data from input form
259
     *
260
     * @NoAdminRequired
261
     * @param int $datasetId
262
     * @param $dimension1
263
     * @param $dimension2
264
     * @param $dimension3
265
     * @return DataResponse|NotFoundResponse
266
     * @throws \Exception
267
     */
268
    public function updateData(int $datasetId, $dimension1, $dimension2, $dimension3)
269
    {
270
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
271
        if (!empty($datasetMetadata)) {
272
            $insert = $update = $errorMessage = 0;
273
            $action = array();
274
            $dimension3 = $this->floatvalue($dimension3);
275
            if ($dimension3 === false) {
276
                $errorMessage = $this->l10n->t('3rd field must be a valid number');
277
            } else {
278
                $action = $this->StorageController->update($datasetId, $dimension1, $dimension2, $dimension3);
279
                $insert = $insert + $action['insert'];
280
                $update = $update + $action['update'];
281
            }
282
283
            $result = [
284
                'insert' => $insert,
285
                'update' => $update,
286
                'error' => $errorMessage,
287
                'validate' => $action['validate'],
288
            ];
289
290
            //$this->logger->error('DataLoadController 88:'.$errorMessage);
291
            if ($errorMessage === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD);
292
            return new DataResponse($result);
293
        } else {
294
            return new NotFoundResponse();
295
        }
296
    }
297
298
    /**
299
     * update data from input form
300
     *
301
     * @NoAdminRequired
302
     * @param int $datasetId
303
     * @param $dimension1
304
     * @param $dimension2
305
     * @return DataResponse|NotFoundResponse
306
     */
307
    public function deleteData(int $datasetId, $dimension1, $dimension2)
308
    {
309
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
310
        if (!empty($datasetMetadata)) {
311
            $result = $this->StorageController->delete($datasetId, $dimension1, $dimension2);
312
            return new DataResponse($result);
0 ignored issues
show
Bug introduced by
$result of type true is incompatible with the type array|object expected by parameter $data of OCP\AppFramework\Http\DataResponse::__construct(). ( Ignorable by Annotation )

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

312
            return new DataResponse(/** @scrutinizer ignore-type */ $result);
Loading history...
313
        } else {
314
            return new NotFoundResponse();
315
        }
316
    }
317
318
    /**
319
     * Import clipboard data
320
     *
321
     * @NoAdminRequired
322
     * @param int $datasetId
323
     * @param $import
324
     * @return DataResponse|NotFoundResponse
325
     * @throws \Exception
326
     */
327
    public function importClipboard($datasetId, $import)
328
    {
329
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
330
        if (!empty($datasetMetadata)) {
331
            $insert = $update = $errorMessage = $errorCounter = 0;
332
            $delimiter = $this->detectDelimiter($import);
333
            $rows = str_getcsv($import, "\n");
334
335
            foreach ($rows as &$row) {
336
                $row = str_getcsv($row, $delimiter);
337
                $row[2] = $this->floatvalue($row[2]);
338
                if ($row[2] === false) {
339
                    $errorCounter++;
340
                } else {
341
                    $action = $this->StorageController->update($datasetId, $row[0], $row[1], $row[2]);
342
                    $insert = $insert + $action['insert'];
343
                    $update = $update + $action['update'];
344
                }
345
                if ($errorCounter === 2) {
346
                    // first error is ignored; might be due to header row
347
                    $errorMessage = $this->l10n->t('3rd field must be a valid number');
348
                    break;
349
                }
350
            }
351
352
            $result = [
353
                'insert' => $insert,
354
                'update' => $update,
355
                'delimiter' => $delimiter,
356
                'error' => $errorMessage,
357
            ];
358
359
            if ($errorMessage === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_IMPORT);
360
            return new DataResponse($result);
361
        } else {
362
            return new NotFoundResponse();
363
        }
364
    }
365
366
    /**
367
     * Import data into dataset from an internal or external file
368
     *
369
     * @NoAdminRequired
370
     * @param int $datasetId
371
     * @param $path
372
     * @return DataResponse|NotFoundResponse
373
     * @throws NotFoundException
374
     * @throws \Exception
375
     */
376
    public function importFile(int $datasetId, $path)
377
    {
378
        //$this->logger->error('DataLoadController 100:'.$datasetId. $path);
379
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
380
        if (!empty($datasetMetadata)) {
381
            $insert = $update = 0;
382
            $option['user_id'] = $datasetMetadata['user_id'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$option was never initialized. Although not strictly required by PHP, it is generally a good practice to add $option = array(); before regardless.
Loading history...
383
            $option['path'] = $path;
384
            $option['link'] = $datasetMetadata['link'];
385
            $result = $this->DataSourceController->read(DataSourceController::DATASET_TYPE_INTERNAL_FILE, $option);
386
387
            if ($result['error'] === 0) {
388
                foreach ($result['data'] as &$row) {
389
                    $action = $this->StorageController->update($datasetId, $row['dimension1'], $row['dimension2'], $row['dimension3']);
390
                    $insert = $insert + $action['insert'];
391
                    $update = $update + $action['update'];
392
                }
393
            }
394
395
            $result = [
396
                'insert' => $insert,
397
                'update' => $update,
398
                'error' => $result['error'],
399
            ];
400
401
            if ($result['error'] === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_IMPORT);
402
            return new DataResponse($result);
403
        } else {
404
            return new NotFoundResponse();
405
        }
406
    }
407
408
    private function detectDelimiter($data)
409
    {
410
        $delimiters = ["\t", ";", "|", ","];
411
        $data_2 = null;
412
        $delimiter = $delimiters[0];
413
        foreach ($delimiters as $d) {
414
            $firstRow = str_getcsv($data, "\n")[0];
415
            $data_1 = str_getcsv($firstRow, $d);
416
            if (sizeof($data_1) > sizeof($data_2)) {
417
                $delimiter = $d;
418
                $data_2 = $data_1;
419
            }
420
        }
421
        return $delimiter;
422
    }
423
424
    private function floatvalue($val)
425
    {
426
        $val = str_replace(",", ".", $val);
427
        $val = preg_replace('/\.(?=.*\.)/', '', $val);
428
        $val = preg_replace('/[^0-9-.]+/', '', $val);
429
        if (is_numeric($val)) {
430
            return number_format(floatval($val), 2, '.', '');
431
        } else {
432
            return false;
433
        }
434
    }
435
}