Passed
Push — master ( ffe1e4...544106 )
by Marcel
05:27
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 int $datasource
97
     * @param $option
98
     * @param int $dataset
99
     * @return DataResponse
100
     */
101
    public function update(int $dataloadId, $name, $option)
102
    {
103
        return new DataResponse($this->DataloadMapper->update($dataloadId, $name, $option));
0 ignored issues
show
Bug introduced by
$this->DataloadMapper->u...loadId, $name, $option) 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

103
        return new DataResponse(/** @scrutinizer ignore-type */ $this->DataloadMapper->update($dataloadId, $name, $option));
Loading history...
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 a dataload from datasource and store into dataset
134
     *
135
     * @NoAdminRequired
136
     * @param int $dataloadId
137
     * @param $path
138
     * @return DataResponse|NotFoundResponse
139
     * @throws NotFoundException
140
     * @throws \Exception
141
     */
142
    public function execute(int $dataloadId)
143
    {
144
        $result = $this->getDataFromDatasource($dataloadId);
145
        $insert = $update = 0;
146
        $datasetId = $result['datasetId'];
147
148
        if ($result['error'] === 0) {
149
            foreach ($result['data'] as &$row) {
150
                $action = $this->StorageController->update($datasetId, $row['dimension1'], $row['dimension2'], $row['dimension3']);
151
                $insert = $insert + $action['insert'];
152
                $update = $update + $action['update'];
153
            }
154
        }
155
156
        $result = [
157
            'insert' => $insert,
158
            'update' => $update,
159
            'error' => $result['error'],
160
        ];
161
162
        if ($result['error'] === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_IMPORT);
163
164
        return new DataResponse($result);
165
    }
166
167
    /**
168
     * get the data from datasource
169
     * to be used in simulation or execution
170
     *
171
     * @NoAdminRequired
172
     * @param int $dataloadId
173
     * @return array
174
     * @throws NotFoundException
175
     */
176
    private function getDataFromDatasource(int $dataloadId)
177
    {
178
        //$this->logger->error('DataLoadController 71:'.$dataloadId);
179
        $dataloadMetadata = $this->DataloadMapper->getDataloadById($dataloadId);
180
        $datasetMetadata = $this->DatasetController->getOwnDataset($dataloadMetadata['dataset']);
181
182
        if (!empty($datasetMetadata)) {
183
            $option = json_decode($dataloadMetadata['option'], true);
184
            $option['user_id'] = $this->userId;
185
186
            $this->logger->debug('DataLoadController 188:' . $dataloadMetadata['option'] . '---' . json_encode($option));
187
            $result = $this->DataSourceController->read((int)$dataloadMetadata['datasource'], $option);
188
            $result['datasetId'] = $dataloadMetadata['dataset'];
189
190
            if (isset($option['timestamp']) and $option['timestamp'] === 'true') {
191
                // if datasource should be timestamped/snapshoted
192
                // shift values by one dimension
193
                $result['data'] = array_map(function ($tag) {
194
                    return array(
195
                        'dimension1' => $tag['dimension2'],
196
                        'dimension2' => $tag['dimension2'],
197
                        'dimension3' => $tag['dimension3']
198
                    );
199
                }, $result['data']);
200
                $result['data'] = $this->replaceDimension($result['data'], 'dimension2', date("Y-m-dTH:i:s"));
201
            }
202
203
            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...
204
        } else {
205
            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...
206
        }
207
    }
208
209
    /**
210
     * replace all values of one dimension
211
     *
212
     * @NoAdminRequired
213
     * @param $Array
214
     * @param $Find
215
     * @param $Replace
216
     * @return array
217
     */
218
    private function replaceDimension($Array, $Find, $Replace)
219
    {
220
        if (is_array($Array)) {
221
            foreach ($Array as $Key => $Val) {
222
                if (is_array($Array[$Key])) {
223
                    $Array[$Key] = $this->replaceDimension($Array[$Key], $Find, $Replace);
224
                } else {
225
                    if ($Key == $Find) {
226
                        $Array[$Key] = $Replace;
227
                    }
228
                }
229
            }
230
        }
231
        return $Array;
232
    }
233
234
    // Data Manipulation
235
    // Data Manipulation
236
    // Data Manipulation
237
238
    /**
239
     * update data from input form
240
     *
241
     * @NoAdminRequired
242
     * @param int $datasetId
243
     * @param $dimension1
244
     * @param $dimension2
245
     * @param $dimension3
246
     * @return DataResponse|NotFoundResponse
247
     * @throws \Exception
248
     */
249
    public function updateData(int $datasetId, $dimension1, $dimension2, $dimension3)
250
    {
251
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
252
        if (!empty($datasetMetadata)) {
253
            $insert = $update = $errorMessage = 0;
254
            $action = array();
255
            $dimension3 = $this->floatvalue($dimension3);
256
            if ($dimension3 === false) {
257
                $errorMessage = $this->l10n->t('3rd field must be a valid number');
258
            } else {
259
                $action = $this->StorageController->update($datasetId, $dimension1, $dimension2, $dimension3);
260
                $insert = $insert + $action['insert'];
261
                $update = $update + $action['update'];
262
            }
263
264
            $result = [
265
                'insert' => $insert,
266
                'update' => $update,
267
                'error' => $errorMessage,
268
                'validate' => $action['validate'],
269
            ];
270
271
            //$this->logger->error('DataLoadController 88:'.$errorMessage);
272
            if ($errorMessage === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD);
273
            return new DataResponse($result);
274
        } else {
275
            return new NotFoundResponse();
276
        }
277
    }
278
279
    /**
280
     * update data from input form
281
     *
282
     * @NoAdminRequired
283
     * @param int $datasetId
284
     * @param $dimension1
285
     * @param $dimension2
286
     * @return DataResponse|NotFoundResponse
287
     */
288
    public function deleteData(int $datasetId, $dimension1, $dimension2)
289
    {
290
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
291
        if (!empty($datasetMetadata)) {
292
            $result = $this->StorageController->delete($datasetId, $dimension1, $dimension2);
293
            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

293
            return new DataResponse(/** @scrutinizer ignore-type */ $result);
Loading history...
294
        } else {
295
            return new NotFoundResponse();
296
        }
297
    }
298
299
    /**
300
     * Import clipboard data
301
     *
302
     * @NoAdminRequired
303
     * @param int $datasetId
304
     * @param $import
305
     * @return DataResponse|NotFoundResponse
306
     * @throws \Exception
307
     */
308
    public function importClipboard($datasetId, $import)
309
    {
310
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
311
        if (!empty($datasetMetadata)) {
312
            $insert = $update = $errorMessage = $errorCounter = 0;
313
            $delimiter = $this->detectDelimiter($import);
314
            $rows = str_getcsv($import, "\n");
315
316
            foreach ($rows as &$row) {
317
                $row = str_getcsv($row, $delimiter);
318
                $row[2] = $this->floatvalue($row[2]);
319
                if ($row[2] === false) {
320
                    $errorCounter++;
321
                } else {
322
                    $action = $this->StorageController->update($datasetId, $row[0], $row[1], $row[2]);
323
                    $insert = $insert + $action['insert'];
324
                    $update = $update + $action['update'];
325
                }
326
                if ($errorCounter === 2) {
327
                    // first error is ignored; might be due to header row
328
                    $errorMessage = $this->l10n->t('3rd field must be a valid number');
329
                    break;
330
                }
331
            }
332
333
            $result = [
334
                'insert' => $insert,
335
                'update' => $update,
336
                'delimiter' => $delimiter,
337
                'error' => $errorMessage,
338
            ];
339
340
            if ($errorMessage === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_IMPORT);
341
            return new DataResponse($result);
342
        } else {
343
            return new NotFoundResponse();
344
        }
345
    }
346
347
    /**
348
     * Import data into dataset from an internal or external file
349
     *
350
     * @NoAdminRequired
351
     * @param int $datasetId
352
     * @param $path
353
     * @return DataResponse|NotFoundResponse
354
     * @throws NotFoundException
355
     * @throws \Exception
356
     */
357
    public function importFile(int $datasetId, $path)
358
    {
359
        //$this->logger->error('DataLoadController 100:'.$datasetId. $path);
360
        $datasetMetadata = $this->DatasetController->getOwnDataset($datasetId);
361
        if (!empty($datasetMetadata)) {
362
            $insert = $update = 0;
363
            $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...
364
            $option['path'] = $path;
365
            $option['link'] = $datasetMetadata['link'];
366
            $result = $this->DataSourceController->read(DataSourceController::DATASET_TYPE_INTERNAL_FILE, $option);
367
368
            if ($result['error'] === 0) {
369
                foreach ($result['data'] as &$row) {
370
                    $action = $this->StorageController->update($datasetId, $row['dimension1'], $row['dimension2'], $row['dimension3']);
371
                    $insert = $insert + $action['insert'];
372
                    $update = $update + $action['update'];
373
                }
374
            }
375
376
            $result = [
377
                'insert' => $insert,
378
                'update' => $update,
379
                'error' => $result['error'],
380
            ];
381
382
            if ($result['error'] === 0) $this->ActivityManager->triggerEvent($datasetId, ActivityManager::OBJECT_DATA, ActivityManager::SUBJECT_DATA_ADD_IMPORT);
383
            return new DataResponse($result);
384
        } else {
385
            return new NotFoundResponse();
386
        }
387
    }
388
389
    private function detectDelimiter($data)
390
    {
391
        $delimiters = ["\t", ";", "|", ","];
392
        $data_2 = null;
393
        $delimiter = $delimiters[0];
394
        foreach ($delimiters as $d) {
395
            $firstRow = str_getcsv($data, "\n")[0];
396
            $data_1 = str_getcsv($firstRow, $d);
397
            if (sizeof($data_1) > sizeof($data_2)) {
398
                $delimiter = $d;
399
                $data_2 = $data_1;
400
            }
401
        }
402
        return $delimiter;
403
    }
404
405
    private function floatvalue($val)
406
    {
407
        $val = str_replace(",", ".", $val);
408
        $val = preg_replace('/\.(?=.*\.)/', '', $val);
409
        $val = preg_replace('/[^0-9-.]+/', '', $val);
410
        if (is_numeric($val)) {
411
            return number_format(floatval($val), 2, '.', '');
412
        } else {
413
            return false;
414
        }
415
    }
416
}