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 2021 Marcel Scherello |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace OCA\Analytics\Controller; |
13
|
|
|
|
14
|
|
|
use OCA\Analytics\Datasource\DatasourceEvent; |
15
|
|
|
use OCA\Analytics\Datasource\Excel; |
16
|
|
|
use OCA\Analytics\Datasource\ExternalFile; |
17
|
|
|
use OCA\Analytics\Datasource\File; |
18
|
|
|
use OCA\Analytics\Datasource\Github; |
19
|
|
|
use OCA\Analytics\Datasource\Json; |
20
|
|
|
use OCA\Analytics\Datasource\Regex; |
21
|
|
|
use OCP\AppFramework\Controller; |
22
|
|
|
use OCP\EventDispatcher\IEventDispatcher; |
23
|
|
|
use OCP\Files\NotFoundException; |
24
|
|
|
use OCP\IL10N; |
25
|
|
|
use OCP\ILogger; |
26
|
|
|
use OCP\IRequest; |
27
|
|
|
|
28
|
|
|
class DatasourceController extends Controller |
29
|
|
|
{ |
30
|
|
|
private $logger; |
31
|
|
|
private $GithubService; |
32
|
|
|
private $FileService; |
33
|
|
|
private $ExternalFileService; |
34
|
|
|
private $RegexService; |
35
|
|
|
private $JsonService; |
36
|
|
|
private $ExcelService; |
37
|
|
|
/** @var IEventDispatcher */ |
38
|
|
|
private $dispatcher; |
39
|
|
|
private $l10n; |
40
|
|
|
|
41
|
|
|
const DATASET_TYPE_GROUP = 0; |
42
|
|
|
const DATASET_TYPE_FILE = 1; |
43
|
|
|
const DATASET_TYPE_INTERNAL_DB = 2; |
44
|
|
|
const DATASET_TYPE_GIT = 3; |
45
|
|
|
const DATASET_TYPE_EXTERNAL_FILE = 4; |
46
|
|
|
const DATASET_TYPE_REGEX = 5; |
47
|
|
|
const DATASET_TYPE_JSON = 6; |
48
|
|
|
const DATASET_TYPE_EXCEL = 7; |
49
|
|
|
|
50
|
|
|
public function __construct( |
51
|
|
|
string $AppName, |
52
|
|
|
IRequest $request, |
53
|
|
|
ILogger $logger, |
54
|
|
|
Github $GithubService, |
55
|
|
|
File $FileService, |
56
|
|
|
Regex $RegexService, |
57
|
|
|
Json $JsonService, |
58
|
|
|
ExternalFile $ExternalFileService, |
59
|
|
|
Excel $ExcelService, |
60
|
|
|
IL10N $l10n, |
61
|
|
|
IEventDispatcher $dispatcher |
62
|
|
|
) |
63
|
|
|
{ |
64
|
|
|
parent::__construct($AppName, $request); |
65
|
|
|
$this->logger = $logger; |
66
|
|
|
$this->ExternalFileService = $ExternalFileService; |
67
|
|
|
$this->GithubService = $GithubService; |
68
|
|
|
$this->RegexService = $RegexService; |
69
|
|
|
$this->FileService = $FileService; |
70
|
|
|
$this->JsonService = $JsonService; |
71
|
|
|
$this->ExcelService = $ExcelService; |
72
|
|
|
$this->dispatcher = $dispatcher; |
73
|
|
|
$this->l10n = $l10n; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* get all datasource ids + names |
78
|
|
|
* |
79
|
|
|
* @NoAdminRequired |
80
|
|
|
*/ |
81
|
|
|
public function index() |
82
|
|
|
{ |
83
|
|
|
$datasources = array(); |
84
|
|
|
$result = array(); |
85
|
|
|
foreach ($this->getDatasources() as $key => $class) { |
86
|
|
|
$datasources[$key] = $class->getName(); |
87
|
|
|
} |
88
|
|
|
$result['datasources'] = $datasources; |
89
|
|
|
|
90
|
|
|
$options = array(); |
91
|
|
|
foreach ($this->getDatasources() as $key => $class) { |
92
|
|
|
$options[$key] = $class->getTemplate(); |
93
|
|
|
} |
94
|
|
|
$result['options'] = $options; |
95
|
|
|
|
96
|
|
|
return $result; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* get all datasource templates |
101
|
|
|
* |
102
|
|
|
* @NoAdminRequired |
103
|
|
|
* @return array |
104
|
|
|
*/ |
105
|
|
|
public function getTemplates() |
106
|
|
|
{ |
107
|
|
|
$result = array(); |
108
|
|
|
foreach ($this->getDatasources() as $key => $class) { |
109
|
|
|
$result[$key] = $class->getTemplate(); |
110
|
|
|
} |
111
|
|
|
return $result; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Get the data from a datasource; |
116
|
|
|
* |
117
|
|
|
* @NoAdminRequired |
118
|
|
|
* @param int $datasourceId |
119
|
|
|
* @param $datasetMetadata |
120
|
|
|
* @return array|NotFoundException |
121
|
|
|
*/ |
122
|
|
|
public function read(int $datasourceId, $datasetMetadata) |
123
|
|
|
{ |
124
|
|
|
$option = array(); |
125
|
|
|
// before 3.1.0, the options were in another format. as of 3.1.0 the standard option array is used |
126
|
|
|
if ($datasetMetadata['link'][0] !== '{') { |
127
|
|
|
$option['link'] = $datasetMetadata['link']; |
128
|
|
|
} else { |
129
|
|
|
$option = json_decode($datasetMetadata['link'], true); |
130
|
|
|
} |
131
|
|
|
$option['user_id'] = $datasetMetadata['user_id']; |
132
|
|
|
|
133
|
|
|
try { |
134
|
|
|
$result = $this->getDatasources()[$datasourceId]->readData($option); |
135
|
|
|
|
136
|
|
|
if (isset($option['timestamp']) and $option['timestamp'] === 'true') { |
137
|
|
|
// if datasource should be timestamped/snapshoted |
138
|
|
|
// shift values by one dimension and stores date in second column |
139
|
|
|
$result['data'] = array_map(function ($tag) { |
140
|
|
|
$columns = count($tag); |
141
|
|
|
return array($tag[$columns - 2], $tag[$columns - 2], $tag[$columns - 1]); |
142
|
|
|
}, $result['data']); |
143
|
|
|
date_default_timezone_set('UTC'); |
144
|
|
|
$result['data'] = $this->replaceDimension($result['data'], 1, date("Y-m-d H:i:s") . 'Z'); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
// filter resultset if required |
148
|
|
|
if (isset($datasetMetadata['filteroptions']) && strlen($datasetMetadata['filteroptions']) >> 2) { |
149
|
|
|
$result['data'] = $this->filterData($result['data'], $datasetMetadata['filteroptions']); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
} catch (\Error $e) { |
153
|
|
|
$result['error'] = $e->getMessage(); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
if (empty($result['data'])) { |
157
|
|
|
$result['status'] = 'nodata'; |
158
|
|
|
} |
159
|
|
|
return $result; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* combine internal and registered datasources |
164
|
|
|
* @return array |
165
|
|
|
*/ |
166
|
|
|
private function getDatasources() |
167
|
|
|
{ |
168
|
|
|
return $this->getOwnDatasources() + $this->getRegisteredDatasources(); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* map all internal datasources to their IDs |
173
|
|
|
* @return array |
174
|
|
|
*/ |
175
|
|
|
private function getOwnDatasources() |
176
|
|
|
{ |
177
|
|
|
$datasources = []; |
178
|
|
|
$datasources[self::DATASET_TYPE_FILE] = $this->FileService; |
179
|
|
|
$datasources[self::DATASET_TYPE_EXCEL] = $this->ExcelService; |
180
|
|
|
$datasources[self::DATASET_TYPE_GIT] = $this->GithubService; |
181
|
|
|
$datasources[self::DATASET_TYPE_EXTERNAL_FILE] = $this->ExternalFileService; |
182
|
|
|
$datasources[self::DATASET_TYPE_REGEX] = $this->RegexService; |
183
|
|
|
$datasources[self::DATASET_TYPE_JSON] = $this->JsonService; |
184
|
|
|
return $datasources; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* map all registered datasources to their IDs |
189
|
|
|
* @return array |
190
|
|
|
*/ |
191
|
|
|
private function getRegisteredDatasources() |
192
|
|
|
{ |
193
|
|
|
$datasources = []; |
194
|
|
|
$event = new DatasourceEvent(); |
195
|
|
|
$this->dispatcher->dispatchTyped($event); |
196
|
|
|
|
197
|
|
|
foreach ($event->getDataSources() as $class) { |
198
|
|
|
$uniqueId = '99' . \OC::$server->get($class)->getId(); |
199
|
|
|
|
200
|
|
|
if (isset($datasources[$uniqueId])) { |
201
|
|
|
$this->logger->logException(new \InvalidArgumentException('Datasource with the same ID already registered: ' . \OC::$server->get($class)->getName()), ['level' => ILogger::INFO]); |
|
|
|
|
202
|
|
|
continue; |
203
|
|
|
} |
204
|
|
|
$datasources[$uniqueId] = \OC::$server->get($class); |
205
|
|
|
} |
206
|
|
|
return $datasources; |
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
|
|
|
/** |
235
|
|
|
* apply the fiven filters to the hole result set |
236
|
|
|
* |
237
|
|
|
* @NoAdminRequired |
238
|
|
|
* @param $data |
239
|
|
|
* @param $filter |
240
|
|
|
* @return array |
241
|
|
|
*/ |
242
|
|
|
private function filterData($data, $filter) |
243
|
|
|
{ |
244
|
|
|
$options = json_decode($filter, true); |
245
|
|
|
if (isset($options['filter'])) { |
246
|
|
|
|
247
|
|
|
foreach ($options['filter'] as $key => $value) { |
248
|
|
|
|
249
|
|
|
$filterValue = $value['value']; |
250
|
|
|
$filterOption = $value['option']; |
251
|
|
|
$filtered = array(); |
252
|
|
|
|
253
|
|
|
foreach ($data as $record) { |
254
|
|
|
if ( |
255
|
|
|
($filterOption === 'EQ' && $record[$key] === $filterValue) |
256
|
|
|
|| ($filterOption === 'GT' && $record[$key] > $filterValue) |
257
|
|
|
|| ($filterOption === 'LT' && $record[$key] < $filterValue) |
258
|
|
|
|| ($filterOption === 'LIKE' && strpos($record[$key], $filterValue) !== FALSE) |
259
|
|
|
) { |
260
|
|
|
array_push($filtered, $record); |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
$data = $filtered; |
264
|
|
|
} |
265
|
|
|
} |
266
|
|
|
return $data; |
267
|
|
|
} |
268
|
|
|
} |
This class constant has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.