Issues (496)

lib/Service/StorageService.php (4 issues)

1
<?php
2
/**
3
 * Analytics
4
 *
5
 * SPDX-FileCopyrightText: 2019-2022 Marcel Scherello
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8
9
namespace OCA\Analytics\Service;
10
11
use OCA\Analytics\Db\StorageMapper;
12
use OCP\DB\Exception;
0 ignored issues
show
The type OCP\DB\Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Psr\Log\LoggerInterface;
0 ignored issues
show
The type Psr\Log\LoggerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
15
class StorageService {
16
	private $logger;
17
	private $StorageMapper;
18
	private $ThresholdService;
19
	private $DatasetService;
20
	private $ReportService;
21
	private $VariableService;
22
23
	public function __construct(
24
		LoggerInterface  $logger,
25
		StorageMapper    $StorageMapper,
26
		DatasetService   $DatasetService,
27
		ThresholdService $ThresholdService,
28
		VariableService  $VariableService,
29
		ReportService    $ReportService
30
	) {
31
		$this->logger = $logger;
32
		$this->StorageMapper = $StorageMapper;
33
		$this->DatasetService = $DatasetService;
34
		$this->ThresholdService = $ThresholdService;
35
		$this->VariableService = $VariableService;
36
		$this->ReportService = $ReportService;
37
	}
38
39
	/**
40
	 * Read data from the storage backend
41
	 *
42
	 * @NoAdminRequired
43
	 * @param $datasetId
44
	 * @param $reportMetadata
45
	 * @return array
46
	 * @throws Exceptionx
47
	 */
48
	public function read($datasetId, $reportMetadata) {
49
		$availableDimensions = array();
50
		$header = array();
51
		$datasetMetadata = $this->DatasetService->read($datasetId);
52
		if ($reportMetadata && $reportMetadata['filteroptions'] !== null) {
53
			$options = json_decode($reportMetadata['filteroptions'], true);
54
		} else {
55
			$options = null;
56
		}
57
58
		if (!empty($datasetMetadata)) {
59
			// output the dimensions available for filtering of this dataset
60
			// this needs to map the technical name to its display name in the report
61
			$availableDimensions['dimension1'] = $datasetMetadata['dimension1'];
62
			$availableDimensions['dimension2'] = $datasetMetadata['dimension2'];
63
64
			// return the header texts of the data being transferred according to the current drill down state selected by user
65
			// if the dimension is not part of the drill down filter, it is not hidden => to be displayed
66
			if (!isset($options['drilldown']['dimension1'])) $header[0] = $datasetMetadata['dimension1'];
67
			if (!isset($options['drilldown']['dimension2'])) $header[1] = $datasetMetadata['dimension2'];
68
			$header[6] = $datasetMetadata['value'];
69
			$header = array_values($header);
70
71
			$data = $this->StorageMapper->read($datasetMetadata['id'], $options);
72
			$data = array_values($data);
73
			foreach ($data as $key => $value) {
74
				$data[$key] = array_values($value);
75
			}
76
		}
77
78
		return empty($data) ? [
79
			'dimensions' => $availableDimensions,
80
			'status' => 'nodata',
81
			'error' => 0
82
		] : [
83
			'header' => $header,
84
			'dimensions' => $availableDimensions,
85
			'data' => $data,
86
			'error' => 0
87
		];
88
	}
89
90
	/**
91
	 * Update data to the storage backend
92
	 *
93
	 * @NoAdminRequired
94
	 * @param int $datasetId
95
	 * @param $dimension1
96
	 * @param $dimension2
97
	 * @param $value
98
	 * @param string|null $user_id
99
	 * @param $bulkInsert
100
	 * @param $aggregation
101
	 * @return array
102
	 * @throws Exception
103
	 */
104
	public function update(
105
		int    $datasetId,
106
			   $dimension1,
107
			   $dimension2,
108
			   $value,
109
		string $user_id = null,
110
			   $bulkInsert = null,
111
			   $aggregation = null
112
	) {
113
		TODO:
114
		//dates in both columns
115
		$dimension2 = $this->convertGermanDateFormat($dimension2);
116
		//convert date into timestamp
117
		//$timestamp = $this->convertGermanDateFormat($timestamp);
118
		$value = $this->floatvalue($value);
119
		$validate = '';
120
		$insert = $update = $error = $action = 0;
0 ignored issues
show
The assignment to $action is dead and can be removed.
Loading history...
121
122
		// replace text variables like %now%
123
		$dimension1 = $this->VariableService->replaceTextVariablesSingle($dimension1);
124
		$dimension2 = $this->VariableService->replaceTextVariablesSingle($dimension2);
125
126
		if ($value !== false) {
127
			try {
128
				$action = $this->StorageMapper->create($datasetId, $dimension1, $dimension2, $value, $user_id, null, $bulkInsert, $aggregation);
129
			} catch (\Exception $e) {
130
				$error = 1;
131
			}
132
			if ($action === 'insert') $insert = 1; elseif ($action === 'update') $update = 1;
133
		} else {
134
			$error = 1;
135
		}
136
137
		// get all reports for the dataset and evaluate their thresholds for push notifications
138
		if ($error === 0) {
139
			foreach ($this->ReportService->reportsForDataset($datasetId) as $report) {
140
				$validateResult = $this->ThresholdService->validate($report['id'], $dimension1, $dimension2, $value, $insert);
141
				if ($validateResult !== '') $validate = $validateResult;
142
			}
143
		}
144
145
		return [
146
			'insert' => $insert,
147
			'update' => $update,
148
			'error' => $error,
149
			'validate' => $validate
150
		];
151
	}
152
153
	/**
154
	 * delete data
155
	 *
156
	 * @NoAdminRequired
157
	 * @param int $datasetId
158
	 * @param $dimension1
159
	 * @param $dimension2
160
	 * @param string|null $user_id
161
	 * @return bool
162
	 */
163
	public function delete(int $datasetId, $dimension1, $dimension2, string $user_id = null) {
164
		return $this->StorageMapper->delete($datasetId, $dimension1, $dimension2, $user_id);
165
	}
166
167
	/**
168
	 * Simulate delete data
169
	 *
170
	 * @NoAdminRequired
171
	 * @param int $datasetId
172
	 * @param $dimension1
173
	 * @param $dimension2
174
	 * @return array
175
	 * @throws Exception
176
	 */
177
	public function deleteSimulate(int $datasetId, $dimension1, $dimension2) {
178
		return $this->StorageMapper->deleteSimulate($datasetId, $dimension1, $dimension2);
179
	}
180
181
	/**
182
	 * Delete data with variables; used for data deletion jobs
183
	 *
184
	 * @NoAdminRequired
185
	 * @param int $datasetId
186
	 * @param $filter
187
	 * @return bool
188
	 * @throws Exception
189
	 */
190
	public function deleteWithFilter(int $datasetId, $filter) {
191
		return $this->StorageMapper->deleteWithFilter($datasetId, $filter);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->StorageMap...er($datasetId, $filter) returns the type integer which is incompatible with the documented return type boolean.
Loading history...
192
	}
193
194
	/**
195
	 * Simulate delete data with variables; used for data deletion jobs
196
	 *
197
	 * @NoAdminRequired
198
	 * @param int $datasetId
199
	 * @param $filter
200
	 * @return bool
201
	 * @throws Exception
202
	 */
203
	public function deleteWithFilterSimulate(int $datasetId, $filter) {
204
		return $this->StorageMapper->deleteWithFilterSimulate($datasetId, $filter);
205
	}
206
207
	/**
208
	 * Get the number of records for a dataset
209
	 * @param int $datasetId
210
	 * @param string|null $user_id
211
	 * @return array
212
	 */
213
	public function getRecordCount(int $datasetId, string $user_id = null) {
214
		return $this->StorageMapper->getRecordCount($datasetId, $user_id);
215
	}
216
217
	private function floatvalue($val) {
218
		// if value is a 3 digit comma number with one leading zero like 0,111, it should not go through the 1000 separator removal
219
		if (preg_match('/(?<=\b0),(?=\d{3}\b)/', $val) === 1 && preg_match('/(?<=\b0).(?=\d{3}\b)/', $val) === 1) {
220
			// remove , as 1000 separator
221
			$val = preg_replace('/(?<=\d)\,(?=\d{3}\b)/', '', $val);
222
			// remove . as 1000 separator
223
			$val = preg_replace('/(?<=\d)\.(?=\d{3}\b)/', '', $val);
224
		}
225
		// convert remaining comma to decimal point
226
		$val = str_replace(",", ".", $val);
227
		if (is_numeric($val)) {
228
			return number_format(floatval($val), 2, '.', '');
229
		} else {
230
			return false;
231
		}
232
	}
233
234
	private function convertGermanDateFormat($val) {
235
		if ($val !== null) {
236
			$fullString = explode(' ', $val);
237
			$dateLength = strlen($fullString[0]);
238
			$dateParts = explode('.', $fullString[0]);
239
240
			if ($dateLength >= 6 && $dateLength <= 10 && count($dateParts) === 3) {
241
				// is most likely a german date format 20.02.2020
242
				$fullString[0] = $dateParts[2] . '-' . sprintf('%02d', $dateParts[1]) . '-' . sprintf('%02d', $dateParts[0]);
243
				$val = implode(' ', $fullString);
244
			}
245
		}
246
		return $val;
247
	}
248
}