pyDetektia 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyDetektia-1.0.0/LICENSE.txt +0 -0
- pyDetektia-1.0.0/PKG-INFO +12 -0
- pyDetektia-1.0.0/README.md +1 -0
- pyDetektia-1.0.0/pyDetektia/__init__.py +0 -0
- pyDetektia-1.0.0/pyDetektia/manager.py +929 -0
- pyDetektia-1.0.0/pyDetektia.egg-info/PKG-INFO +12 -0
- pyDetektia-1.0.0/pyDetektia.egg-info/SOURCES.txt +10 -0
- pyDetektia-1.0.0/pyDetektia.egg-info/dependency_links.txt +1 -0
- pyDetektia-1.0.0/pyDetektia.egg-info/requires.txt +1 -0
- pyDetektia-1.0.0/pyDetektia.egg-info/top_level.txt +1 -0
- pyDetektia-1.0.0/setup.cfg +4 -0
- pyDetektia-1.0.0/setup.py +15 -0
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyDetektia
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A package for easily interaction with Detektia's API endpoints.
|
|
5
|
+
Home-page: https://github.com/detektia/pyDetektia
|
|
6
|
+
Author: Detektia
|
|
7
|
+
Author-email: info@detektia.com
|
|
8
|
+
License: ToBeChosen
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE.txt
|
|
11
|
+
|
|
12
|
+
# pyDetektia
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# pyDetektia
|
|
File without changes
|
|
@@ -0,0 +1,929 @@
|
|
|
1
|
+
###--------------------------###
|
|
2
|
+
###----- manager module -----###
|
|
3
|
+
###--------------------------###
|
|
4
|
+
|
|
5
|
+
'''
|
|
6
|
+
This module contains two classes:
|
|
7
|
+
- ApiUser: Parent class
|
|
8
|
+
- ApiAdmin: Child class, inherits from ApiUser
|
|
9
|
+
|
|
10
|
+
These classes are used for interacting with the database through the API.
|
|
11
|
+
'''
|
|
12
|
+
|
|
13
|
+
### load modules
|
|
14
|
+
import json
|
|
15
|
+
import datetime as dt
|
|
16
|
+
from copy import deepcopy
|
|
17
|
+
|
|
18
|
+
import requests
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#---------------------------------------------------------------------------#
|
|
22
|
+
#---------------------------------------------------------------------------#
|
|
23
|
+
|
|
24
|
+
###-------------------------###
|
|
25
|
+
###----- ApiUser class -----###
|
|
26
|
+
|
|
27
|
+
class ApiUser:
|
|
28
|
+
|
|
29
|
+
def __init__(self, username, password, ip):
|
|
30
|
+
self._user = username
|
|
31
|
+
self._password = password
|
|
32
|
+
self._ip = ip
|
|
33
|
+
|
|
34
|
+
self._token, self._headers, self._organization = self._get_token()
|
|
35
|
+
|
|
36
|
+
def _get_token(self):
|
|
37
|
+
|
|
38
|
+
ret = requests.post(
|
|
39
|
+
f"http://{self._ip}/auth/token",
|
|
40
|
+
data={
|
|
41
|
+
"username": self._user,
|
|
42
|
+
"password": self._password
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
response = ret.json()
|
|
46
|
+
token = response["access_token"]
|
|
47
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
48
|
+
organization = response["organization"]
|
|
49
|
+
|
|
50
|
+
return (token, headers, organization)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _get_datasetname(self, title, scenario, user=None):
|
|
54
|
+
'''
|
|
55
|
+
Returns the datasetname of the given title and scenario.
|
|
56
|
+
'''
|
|
57
|
+
|
|
58
|
+
if user is None:
|
|
59
|
+
user = self._user
|
|
60
|
+
|
|
61
|
+
params = {
|
|
62
|
+
"title": title,
|
|
63
|
+
"scenario": scenario
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
ret = requests.get(
|
|
67
|
+
f"http://{self._ip}/users/{user}/datasetname",
|
|
68
|
+
params = params,
|
|
69
|
+
headers=self._headers
|
|
70
|
+
)
|
|
71
|
+
jsonret = ret.json()
|
|
72
|
+
|
|
73
|
+
if ret.status_code != 200:
|
|
74
|
+
raise ValueError(jsonret['detail'])
|
|
75
|
+
|
|
76
|
+
return jsonret['datasetname']
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _select_datasetname(self, dataset=None, title=None, scenario=None, user=None):
|
|
80
|
+
'''
|
|
81
|
+
Checks if dataset is None, if so, it returns the datasetname of the given title and scenario.
|
|
82
|
+
'''
|
|
83
|
+
if dataset is None:
|
|
84
|
+
if (title is not None) and (scenario is not None):
|
|
85
|
+
return self._get_datasetname(title, scenario, user)
|
|
86
|
+
else:
|
|
87
|
+
raise ValueError("Either 'dataset' or both 'title' and 'scenario' are needed.")
|
|
88
|
+
else:
|
|
89
|
+
return dataset
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
###----------------------º
|
|
94
|
+
##--------------------
|
|
95
|
+
#--- usable methods
|
|
96
|
+
def get_metadata_all(self, user=None):
|
|
97
|
+
'''
|
|
98
|
+
Returns a list of metadata dictionaries.
|
|
99
|
+
'''
|
|
100
|
+
|
|
101
|
+
if user is None:
|
|
102
|
+
user = self._user
|
|
103
|
+
|
|
104
|
+
ret = requests.get(
|
|
105
|
+
f"http://{self._ip}/users/{user}/datasets",
|
|
106
|
+
headers=self._headers
|
|
107
|
+
)
|
|
108
|
+
jsonret = ret.json()
|
|
109
|
+
|
|
110
|
+
if ret.status_code != 200:
|
|
111
|
+
raise ValueError(jsonret['detail'])
|
|
112
|
+
|
|
113
|
+
return jsonret
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_tables_scheme(self, user=None):
|
|
117
|
+
'''
|
|
118
|
+
Returns a dictionary with all the point and polygons scenarios and datasets.
|
|
119
|
+
'''
|
|
120
|
+
|
|
121
|
+
if user is None:
|
|
122
|
+
user = self._user
|
|
123
|
+
|
|
124
|
+
scenarios_list = self.list_scenarios(user)
|
|
125
|
+
polygons_scenarios_list = self.list_polygons_scenarios(user)
|
|
126
|
+
|
|
127
|
+
tables_dict = {'points': {}, 'polygons': {}}
|
|
128
|
+
|
|
129
|
+
for scenario in scenarios_list:
|
|
130
|
+
tables_dict['points'][scenario] = []
|
|
131
|
+
|
|
132
|
+
for dataset in self.list_datasets(scenario, user):
|
|
133
|
+
tables_dict['points'][scenario].append(dataset)
|
|
134
|
+
|
|
135
|
+
for polygons_scenario in polygons_scenarios_list:
|
|
136
|
+
tables_dict['polygons'][polygons_scenario] = []
|
|
137
|
+
|
|
138
|
+
for polygons_dataset in self.list_polygons_datasets(polygons_scenario, user):
|
|
139
|
+
tables_dict['polygons'][polygons_scenario].append(polygons_dataset)
|
|
140
|
+
|
|
141
|
+
return tables_dict
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
###----------------------------------###
|
|
145
|
+
###----- points dataset methods -----###
|
|
146
|
+
def list_scenarios(self, user=None):
|
|
147
|
+
'''
|
|
148
|
+
Returns a list of scenarios names.
|
|
149
|
+
'''
|
|
150
|
+
|
|
151
|
+
if user is None:
|
|
152
|
+
user = self._user
|
|
153
|
+
|
|
154
|
+
ret = requests.get(
|
|
155
|
+
f"http://{self._ip}/users/{user}/datasets",
|
|
156
|
+
headers=self._headers
|
|
157
|
+
)
|
|
158
|
+
jsonret = ret.json()
|
|
159
|
+
|
|
160
|
+
if ret.status_code != 200:
|
|
161
|
+
raise ValueError(jsonret['detail'])
|
|
162
|
+
|
|
163
|
+
scenario_list = list(set([data['scenario'] for data in jsonret]))
|
|
164
|
+
|
|
165
|
+
return scenario_list
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def list_datasets(self, scenario, user=None):
|
|
169
|
+
'''
|
|
170
|
+
Returns a list of dataset names.
|
|
171
|
+
'''
|
|
172
|
+
|
|
173
|
+
if user is None:
|
|
174
|
+
user = self._user
|
|
175
|
+
|
|
176
|
+
ret = requests.get(
|
|
177
|
+
f"http://{self._ip}/users/{user}/datasets",
|
|
178
|
+
headers=self._headers
|
|
179
|
+
)
|
|
180
|
+
jsonret = ret.json()
|
|
181
|
+
|
|
182
|
+
if ret.status_code != 200:
|
|
183
|
+
raise ValueError(jsonret['detail'])
|
|
184
|
+
|
|
185
|
+
dataset_list = list(set([data['datasetname'] for data in jsonret if data['scenario'] == scenario]))
|
|
186
|
+
|
|
187
|
+
return dataset_list
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def get_metadata(self, dataset=None, title=None, scenario=None, user=None):
|
|
191
|
+
'''
|
|
192
|
+
Returns the metadata of the dataset.
|
|
193
|
+
'''
|
|
194
|
+
|
|
195
|
+
if user is None:
|
|
196
|
+
user = self._user
|
|
197
|
+
|
|
198
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
199
|
+
|
|
200
|
+
ret = requests.get(
|
|
201
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}/metadata",
|
|
202
|
+
headers=self._headers
|
|
203
|
+
)
|
|
204
|
+
jsonret = ret.json()
|
|
205
|
+
|
|
206
|
+
if ret.status_code != 200:
|
|
207
|
+
raise ValueError(jsonret['detail'])
|
|
208
|
+
|
|
209
|
+
return jsonret
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
#-----------------------#
|
|
213
|
+
#--- manage datasets ---#
|
|
214
|
+
def add_dataset(self, dataset_json, title=None, scenario=None, organization=None):
|
|
215
|
+
'''
|
|
216
|
+
This method adds new dataset data and metadata tables from the dataset_json object.
|
|
217
|
+
'''
|
|
218
|
+
|
|
219
|
+
if organization is None:
|
|
220
|
+
organization = self._organization
|
|
221
|
+
|
|
222
|
+
dataset_json['metadata']['title'] = title
|
|
223
|
+
dataset_json['metadata']['scenario'] = scenario
|
|
224
|
+
|
|
225
|
+
ret = requests.put(
|
|
226
|
+
f"http://{self._ip}/organizations/{organization}/datasets",
|
|
227
|
+
json=dataset_json,
|
|
228
|
+
headers=self._headers
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if ret.status_code != 200:
|
|
232
|
+
raise ValueError(ret.json()['detail'])
|
|
233
|
+
|
|
234
|
+
return ret.status_code
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def change_title(self, new_title, dataset, organization=None):
|
|
238
|
+
"""
|
|
239
|
+
This method changes the title of the dataset.
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
if organization is None:
|
|
243
|
+
organization = self._organization
|
|
244
|
+
|
|
245
|
+
ret = requests.post(
|
|
246
|
+
f"http://{self._ip}/organizations/{organization}/datasets/{dataset}",
|
|
247
|
+
json={'new_title': new_title},
|
|
248
|
+
headers=self._headers
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if ret.status_code != 200:
|
|
252
|
+
raise ValueError(ret.json()['detail'])
|
|
253
|
+
|
|
254
|
+
return {"status_code" :ret.status_code, "detail": ret.json()}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
###------------------------------###
|
|
259
|
+
###----- point data methods -----###
|
|
260
|
+
def get_dates(self, dataset=None, title=None, scenario=None, user=None):
|
|
261
|
+
'''
|
|
262
|
+
Returns a dictionary with the dates.
|
|
263
|
+
'''
|
|
264
|
+
|
|
265
|
+
if user is None:
|
|
266
|
+
user = self._user
|
|
267
|
+
|
|
268
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
269
|
+
|
|
270
|
+
ret = requests.get(
|
|
271
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}/metadata",
|
|
272
|
+
headers=self._headers
|
|
273
|
+
)
|
|
274
|
+
jsonret = ret.json()
|
|
275
|
+
|
|
276
|
+
if ret.status_code != 200:
|
|
277
|
+
raise ValueError(jsonret['detail'])
|
|
278
|
+
|
|
279
|
+
days_list = jsonret['dates']
|
|
280
|
+
dates_dict = {(dt.date(1, 1, 1) + dt.timedelta(days=int(days)-365)).strftime("%Y-%m-%d"): days for days in days_list}
|
|
281
|
+
|
|
282
|
+
return dates_dict
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def get_data(self, dataset=None, title=None, scenario=None, polygon=None, date_span=None, user=None):
|
|
286
|
+
'''
|
|
287
|
+
Returns data in a json object including velocities and the displacements within the date span, limits are included.
|
|
288
|
+
- date_span has to be a list of the form [date_start, date_end].
|
|
289
|
+
If date_span is None, the whole dataset is returned.
|
|
290
|
+
If date_span is not None, the velocities are recalculated for the given date span.
|
|
291
|
+
'''
|
|
292
|
+
|
|
293
|
+
if user is None:
|
|
294
|
+
user = self._user
|
|
295
|
+
|
|
296
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
297
|
+
|
|
298
|
+
if date_span is None:
|
|
299
|
+
dates = self.get_metadata(dataset=dataset, user=user)['dates']
|
|
300
|
+
params = {'date_span': (dates[0], dates[-1])}
|
|
301
|
+
else:
|
|
302
|
+
params = {'date_span': (date_span[0], date_span[1])}
|
|
303
|
+
|
|
304
|
+
if polygon is not None:
|
|
305
|
+
params['polygon'] = json.dumps(polygon)
|
|
306
|
+
|
|
307
|
+
ret = requests.get(
|
|
308
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}",
|
|
309
|
+
headers=self._headers,
|
|
310
|
+
params = params
|
|
311
|
+
)
|
|
312
|
+
jsonret = ret.json()
|
|
313
|
+
|
|
314
|
+
if ret.status_code != 200:
|
|
315
|
+
raise ValueError(jsonret['detail'])
|
|
316
|
+
|
|
317
|
+
return jsonret
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def get_complete_json(self, dataset=None, title=None, scenario=None, user=None):
|
|
321
|
+
'''
|
|
322
|
+
Returns data (with all dates) and metadata together in a json.
|
|
323
|
+
'''
|
|
324
|
+
|
|
325
|
+
if user is None:
|
|
326
|
+
user = self._user
|
|
327
|
+
|
|
328
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
329
|
+
|
|
330
|
+
metadata = self.get_metadata(dataset=dataset, user=user)
|
|
331
|
+
|
|
332
|
+
dates = metadata['dates']
|
|
333
|
+
date_span = [dates[0], dates[-1]]
|
|
334
|
+
|
|
335
|
+
data = self.get_data(dataset, date_span=date_span, user=user)
|
|
336
|
+
|
|
337
|
+
complete_json = json.loads(json.dumps({"metadata": metadata, "data": data}))
|
|
338
|
+
|
|
339
|
+
return complete_json
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def get_velocities(self, dataset=None, title=None, scenario=None, polygon=None, extended=False, user=None):
|
|
343
|
+
'''
|
|
344
|
+
Returns data in a json format with the velocities stored in the database.
|
|
345
|
+
- polygon: if not None, set the polygon to filter the data spatially.
|
|
346
|
+
- extended: if True, returns the velocities of the points together with the extended values.
|
|
347
|
+
'''
|
|
348
|
+
|
|
349
|
+
if user is None:
|
|
350
|
+
user = self._user
|
|
351
|
+
|
|
352
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
353
|
+
|
|
354
|
+
params = {}
|
|
355
|
+
if polygon is not None:
|
|
356
|
+
params['polygon'] = json.dumps(polygon)
|
|
357
|
+
|
|
358
|
+
if extended:
|
|
359
|
+
params['extended'] = extended
|
|
360
|
+
|
|
361
|
+
ret = requests.get(
|
|
362
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}",
|
|
363
|
+
headers=self._headers,
|
|
364
|
+
params = params
|
|
365
|
+
)
|
|
366
|
+
jsonret = ret.json()
|
|
367
|
+
|
|
368
|
+
if ret.status_code != 200:
|
|
369
|
+
raise ValueError(jsonret['detail'])
|
|
370
|
+
|
|
371
|
+
return jsonret
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def get_point_ids(self, dataset=None, title=None, scenario=None, polygon=None, user=None):
|
|
375
|
+
'''
|
|
376
|
+
Returns points ids of the given dataset.
|
|
377
|
+
'''
|
|
378
|
+
|
|
379
|
+
if user is None:
|
|
380
|
+
user = self._user
|
|
381
|
+
|
|
382
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
383
|
+
|
|
384
|
+
if polygon is None:
|
|
385
|
+
ret = requests.get(
|
|
386
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}",
|
|
387
|
+
headers=self._headers
|
|
388
|
+
)
|
|
389
|
+
else:
|
|
390
|
+
ret = requests.get(
|
|
391
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}",
|
|
392
|
+
headers=self._headers,
|
|
393
|
+
params={'polygon': polygon}
|
|
394
|
+
)
|
|
395
|
+
jsonret = ret.json()
|
|
396
|
+
|
|
397
|
+
if ret.status_code != 200:
|
|
398
|
+
raise ValueError(jsonret['detail'])
|
|
399
|
+
|
|
400
|
+
point_ids = [point['id'] for point in jsonret['features']]
|
|
401
|
+
|
|
402
|
+
return point_ids
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def get_data_details(self, ids, dataset=None, title=None, scenario=None, user=None):
|
|
406
|
+
'''
|
|
407
|
+
Returns details of the data points labelled the given ids.
|
|
408
|
+
'''
|
|
409
|
+
|
|
410
|
+
if user is None:
|
|
411
|
+
user = self._user
|
|
412
|
+
|
|
413
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
414
|
+
|
|
415
|
+
if len(ids) != 0:
|
|
416
|
+
params = {"ids": ids}
|
|
417
|
+
|
|
418
|
+
ret = requests.get(
|
|
419
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}/details",
|
|
420
|
+
headers=self._headers,
|
|
421
|
+
params=params
|
|
422
|
+
)
|
|
423
|
+
jsonret = ret.json()
|
|
424
|
+
|
|
425
|
+
if ret.status_code != 200:
|
|
426
|
+
raise ValueError(jsonret['detail'])
|
|
427
|
+
|
|
428
|
+
return jsonret
|
|
429
|
+
|
|
430
|
+
else:
|
|
431
|
+
raise ValueError("No point id is provided")
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
#----------------------#
|
|
435
|
+
#--- manage plugins ---#
|
|
436
|
+
def get_extended_metadata(self, dataset=None, title=None, scenario=None, user=None):
|
|
437
|
+
'''
|
|
438
|
+
Returns the extended metadata table of a given dataset.
|
|
439
|
+
'''
|
|
440
|
+
|
|
441
|
+
if user is None:
|
|
442
|
+
user = self._user
|
|
443
|
+
|
|
444
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
445
|
+
|
|
446
|
+
ret = requests.get(
|
|
447
|
+
f"http://{self._ip}/users/{user}/extended/{dataset}/metadata",
|
|
448
|
+
headers=self._headers,
|
|
449
|
+
)
|
|
450
|
+
jsonret = ret.json()
|
|
451
|
+
|
|
452
|
+
if ret.status_code != 200:
|
|
453
|
+
raise ValueError(jsonret['detail'])
|
|
454
|
+
|
|
455
|
+
return jsonret
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def get_extended_names(self, dataset=None, title=None, scenario=None, user=None):
|
|
459
|
+
'''
|
|
460
|
+
Returns the list of the extended keys.
|
|
461
|
+
'''
|
|
462
|
+
|
|
463
|
+
if user is None:
|
|
464
|
+
user = self._user
|
|
465
|
+
|
|
466
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
467
|
+
|
|
468
|
+
jsonret = self.get_extended_metadata(dataset, user=user)
|
|
469
|
+
|
|
470
|
+
return [metadata['key'] for metadata in jsonret]
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def get_extended(self, key, dataset=None, title=None, scenario=None, user=None):
|
|
474
|
+
'''
|
|
475
|
+
Returns extended values with indices in a json format.
|
|
476
|
+
'''
|
|
477
|
+
|
|
478
|
+
if user is None:
|
|
479
|
+
user = self._user
|
|
480
|
+
|
|
481
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
482
|
+
|
|
483
|
+
ret = requests.get(
|
|
484
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}/extended",
|
|
485
|
+
params={'key': key},
|
|
486
|
+
headers=self._headers,
|
|
487
|
+
)
|
|
488
|
+
jsonret = ret.json()
|
|
489
|
+
|
|
490
|
+
if ret.status_code != 200:
|
|
491
|
+
raise ValueError(jsonret['detail'])
|
|
492
|
+
|
|
493
|
+
return jsonret
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
#------------------------#
|
|
497
|
+
#--- polygons methods ---#
|
|
498
|
+
def list_polygons_scenarios(self, user=None):
|
|
499
|
+
'''
|
|
500
|
+
Returns a list of scenarios names.
|
|
501
|
+
'''
|
|
502
|
+
|
|
503
|
+
if user is None:
|
|
504
|
+
user = self._user
|
|
505
|
+
|
|
506
|
+
ret = requests.get(
|
|
507
|
+
f"http://{self._ip}/users/{user}/polygons",
|
|
508
|
+
headers=self._headers
|
|
509
|
+
)
|
|
510
|
+
jsonret = ret.json()
|
|
511
|
+
|
|
512
|
+
if ret.status_code != 200:
|
|
513
|
+
raise ValueError(jsonret['detail'])
|
|
514
|
+
|
|
515
|
+
scenario_list = [data['scenario'] for data in jsonret]
|
|
516
|
+
|
|
517
|
+
return scenario_list
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def list_polygons_datasets(self, scenario, user=None):
|
|
521
|
+
'''
|
|
522
|
+
Returns a list of the loaded polygons metadata in this user and scenario.
|
|
523
|
+
'''
|
|
524
|
+
|
|
525
|
+
if user is None:
|
|
526
|
+
user = self._user
|
|
527
|
+
|
|
528
|
+
ret = requests.get(
|
|
529
|
+
f"http://{self._ip}/users/{user}/polygons",
|
|
530
|
+
headers=self._headers
|
|
531
|
+
)
|
|
532
|
+
jsonret = ret.json()
|
|
533
|
+
|
|
534
|
+
if ret.status_code != 200:
|
|
535
|
+
raise ValueError(jsonret['detail'])
|
|
536
|
+
|
|
537
|
+
dataset_list = [data['datasetname'] for data in jsonret if data['scenario'] == scenario]
|
|
538
|
+
|
|
539
|
+
return dataset_list
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def get_polygons(self, polygon_dataset, user=None):
|
|
543
|
+
'''
|
|
544
|
+
Returns a json object with the polygons and indices loaded in dataset_name.
|
|
545
|
+
'''
|
|
546
|
+
|
|
547
|
+
if user is None:
|
|
548
|
+
user = self._user
|
|
549
|
+
|
|
550
|
+
ret = requests.get(
|
|
551
|
+
f"http://{self._ip}/users/{user}/polygons/{polygon_dataset}",
|
|
552
|
+
headers=self._headers
|
|
553
|
+
)
|
|
554
|
+
jsonret = ret.json()
|
|
555
|
+
|
|
556
|
+
if ret.status_code != 200:
|
|
557
|
+
raise ValueError(jsonret['detail'])
|
|
558
|
+
|
|
559
|
+
return jsonret
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def get_polygons_metadata(self, polygon_dataset, user=None):
|
|
563
|
+
'''
|
|
564
|
+
Returns a json object with the polygons metadata loaded in dataset_name.
|
|
565
|
+
'''
|
|
566
|
+
|
|
567
|
+
if user is None:
|
|
568
|
+
user = self._user
|
|
569
|
+
|
|
570
|
+
ret = requests.get(
|
|
571
|
+
f"http://{self._ip}/users/{user}/polygons/{polygon_dataset}/metadata",
|
|
572
|
+
headers=self._headers
|
|
573
|
+
)
|
|
574
|
+
jsonret = ret.json()
|
|
575
|
+
|
|
576
|
+
if ret.status_code != 200:
|
|
577
|
+
raise ValueError(jsonret['detail'])
|
|
578
|
+
|
|
579
|
+
return jsonret
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def get_indices_metadata(self, polygon_dataset, index_types=None, date_span=None, user=None):
|
|
583
|
+
"""
|
|
584
|
+
Returns the indices metadata of polygon_dataset and user
|
|
585
|
+
"""
|
|
586
|
+
|
|
587
|
+
if user is None:
|
|
588
|
+
user = self._user
|
|
589
|
+
|
|
590
|
+
if date_span is not None:
|
|
591
|
+
params = {'date_span': (date_span[0], date_span[1])}
|
|
592
|
+
|
|
593
|
+
if index_types is not None:
|
|
594
|
+
params['index_types'] = index_types
|
|
595
|
+
|
|
596
|
+
ret = requests.get(
|
|
597
|
+
f"http://{self._ip}/users/{user}/indices/{polygon_dataset}/metadata",
|
|
598
|
+
headers=self._headers,
|
|
599
|
+
params=params
|
|
600
|
+
)
|
|
601
|
+
jsonret = ret.json()
|
|
602
|
+
|
|
603
|
+
else:
|
|
604
|
+
if index_types is not None:
|
|
605
|
+
params = {'index_types': index_types}
|
|
606
|
+
|
|
607
|
+
ret = requests.get(
|
|
608
|
+
f"http://{self._ip}/users/{user}/indices/{polygon_dataset}/metadata",
|
|
609
|
+
headers=self._headers,
|
|
610
|
+
params=params
|
|
611
|
+
)
|
|
612
|
+
jsonret = ret.json()
|
|
613
|
+
|
|
614
|
+
else:
|
|
615
|
+
ret = requests.get(
|
|
616
|
+
f"http://{self._ip}/users/{user}/indices/{polygon_dataset}/metadata",
|
|
617
|
+
headers=self._headers
|
|
618
|
+
)
|
|
619
|
+
jsonret = ret.json()
|
|
620
|
+
|
|
621
|
+
if ret.status_code != 200:
|
|
622
|
+
raise ValueError(jsonret['detail'])
|
|
623
|
+
|
|
624
|
+
return jsonret
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
def get_polygons_time_series(self, polygon_dataset, ids, index_types=None, date_span=None, user=None):
|
|
628
|
+
'''
|
|
629
|
+
Returns polygons time series of type 'index_type' labelled by the given ids in between the dates in 'date_span'.
|
|
630
|
+
'''
|
|
631
|
+
|
|
632
|
+
if user is None:
|
|
633
|
+
user = self._user
|
|
634
|
+
|
|
635
|
+
if len(ids) != 0:
|
|
636
|
+
indices_json_list = self.get_indices_metadata(polygon_dataset, index_types=index_types, date_span=date_span, user=user)
|
|
637
|
+
if index_types is None: # if is None, then get all the index_types
|
|
638
|
+
index_types = list(set(row['index_type'] for row in indices_json_list))
|
|
639
|
+
|
|
640
|
+
#--- index dict
|
|
641
|
+
index_dict = {}
|
|
642
|
+
for index_type in index_types:
|
|
643
|
+
index_dict[index_type] = {}
|
|
644
|
+
|
|
645
|
+
for indices_json in indices_json_list:
|
|
646
|
+
index = indices_json['index']
|
|
647
|
+
index_dict[indices_json['index_type']][index] = indices_json['date']
|
|
648
|
+
|
|
649
|
+
#--- empty response
|
|
650
|
+
response = {
|
|
651
|
+
'type': 'FeatureCollection',
|
|
652
|
+
'features': [],
|
|
653
|
+
'bbox': None
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
#--- loop over index types
|
|
657
|
+
for index_type in index_types:
|
|
658
|
+
|
|
659
|
+
#--- query
|
|
660
|
+
params = {"ids": ids, "indices": list(index_dict[index_type].keys())}
|
|
661
|
+
|
|
662
|
+
ret = requests.get(
|
|
663
|
+
f"http://{self._ip}/users/{user}/polygons/{polygon_dataset}/details",
|
|
664
|
+
headers=self._headers,
|
|
665
|
+
params=params
|
|
666
|
+
)
|
|
667
|
+
jsonret = ret.json()
|
|
668
|
+
|
|
669
|
+
if ret.status_code != 200:
|
|
670
|
+
raise ValueError(jsonret['detail'])
|
|
671
|
+
|
|
672
|
+
#--- rename indices
|
|
673
|
+
for js in jsonret['features']:
|
|
674
|
+
properties = js['properties']
|
|
675
|
+
id = properties['id']
|
|
676
|
+
|
|
677
|
+
properties = {str(v): properties[k] for k, v in index_dict[index_type].items()}
|
|
678
|
+
js['properties'] = properties
|
|
679
|
+
js['id'] = id
|
|
680
|
+
js['index_type'] = index_type
|
|
681
|
+
|
|
682
|
+
response['features'].append(js)
|
|
683
|
+
|
|
684
|
+
return response
|
|
685
|
+
|
|
686
|
+
else:
|
|
687
|
+
raise ValueError("No polygon id is provided")
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
#-------------------#
|
|
691
|
+
#--- use plugins ---#
|
|
692
|
+
def use_plugin(self, plugin_name, parameters_dict, dataset=None, title=None, scenario=None, user=None):
|
|
693
|
+
'''
|
|
694
|
+
Uses the plugin "plugin_name" which requires the parameters "parameters_dict" in a dictionary form.
|
|
695
|
+
'''
|
|
696
|
+
|
|
697
|
+
if user is None:
|
|
698
|
+
user = self._user
|
|
699
|
+
|
|
700
|
+
dataset = self._select_datasetname(dataset, title, scenario, user)
|
|
701
|
+
|
|
702
|
+
ret = requests.put(
|
|
703
|
+
f"http://{self._ip}/users/{user}/datasets/{dataset}/process",
|
|
704
|
+
headers=self._headers,
|
|
705
|
+
json={
|
|
706
|
+
"name": plugin_name,
|
|
707
|
+
"data": parameters_dict
|
|
708
|
+
}
|
|
709
|
+
)
|
|
710
|
+
jsonret = ret.json()
|
|
711
|
+
|
|
712
|
+
if ret.status_code != 200:
|
|
713
|
+
raise ValueError(ret.json()['detail'])
|
|
714
|
+
|
|
715
|
+
return jsonret
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
#---------------------------------------------------------------------------#
|
|
720
|
+
#---------------------------------------------------------------------------#
|
|
721
|
+
|
|
722
|
+
###--------------------------###
|
|
723
|
+
###----- ApiAdmin class -----###
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
class ApiAdmin(ApiUser):
|
|
727
|
+
'''
|
|
728
|
+
This class can be created only as a admin user.
|
|
729
|
+
'''
|
|
730
|
+
|
|
731
|
+
def __init__(self, username, password, ip):
|
|
732
|
+
super().__init__(username, password, ip)
|
|
733
|
+
|
|
734
|
+
self._token, self._headers, self._organization = self._get_token()
|
|
735
|
+
|
|
736
|
+
if isinstance(self.get_users_info(), dict):
|
|
737
|
+
raise ValueError("User is not admin")
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
def _reformat_polygons(self, polygons_json):
|
|
741
|
+
"""
|
|
742
|
+
This function checks if the polygon json has the right format, if not is is modified as needed.
|
|
743
|
+
"""
|
|
744
|
+
|
|
745
|
+
reformated_polygons_json = deepcopy(polygons_json)
|
|
746
|
+
|
|
747
|
+
for i, d in enumerate(reformated_polygons_json['data']['features']):
|
|
748
|
+
if 'id' in d['properties'].keys():
|
|
749
|
+
d['properties']['id'] = int(d['properties']['id']) # make sure that 'id' in 'properties' is an integer.
|
|
750
|
+
else:
|
|
751
|
+
d['properties']['id'] = i
|
|
752
|
+
|
|
753
|
+
if 'uid' not in d['properties'].keys():
|
|
754
|
+
d['properties']['uid'] = d['properties']['id']
|
|
755
|
+
|
|
756
|
+
if d['geometry']['type'] == 'MultiPolygon':
|
|
757
|
+
d['geometry']['type'] = 'Polygon'
|
|
758
|
+
d['geometry']['coordinates'] = d['geometry']['coordinates'][0]
|
|
759
|
+
|
|
760
|
+
return reformated_polygons_json
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
###----------------------
|
|
764
|
+
##--------------------
|
|
765
|
+
#--- usable methods
|
|
766
|
+
|
|
767
|
+
#--------------------#
|
|
768
|
+
#--- manage users ---#
|
|
769
|
+
def get_users_info(self):
|
|
770
|
+
'''
|
|
771
|
+
Returns a list of dictionaries. Each dictionary has the information.
|
|
772
|
+
'''
|
|
773
|
+
|
|
774
|
+
ret = requests.get(
|
|
775
|
+
f"http://{self._ip}/users",
|
|
776
|
+
headers=self._headers
|
|
777
|
+
)
|
|
778
|
+
jsonret = ret.json()
|
|
779
|
+
|
|
780
|
+
return jsonret
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
def get_users_list(self):
|
|
784
|
+
'''
|
|
785
|
+
Returns a list with all the user names.
|
|
786
|
+
'''
|
|
787
|
+
|
|
788
|
+
ret = requests.get(
|
|
789
|
+
f"http://{self._ip}/users",
|
|
790
|
+
headers=self._headers,
|
|
791
|
+
)
|
|
792
|
+
jsonret = ret.json()
|
|
793
|
+
|
|
794
|
+
user_list = [user['name'] for user in jsonret]
|
|
795
|
+
|
|
796
|
+
return user_list
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
def create_user(self, organization, new_user, new_pwd, e_mail=None, _is_admin=False):
|
|
800
|
+
'''
|
|
801
|
+
This mehotd creates a new user.
|
|
802
|
+
'''
|
|
803
|
+
|
|
804
|
+
is_admin = "TRUE" if _is_admin else "FALSE"
|
|
805
|
+
email = e_mail or f"{new_user}@detektia.com"
|
|
806
|
+
|
|
807
|
+
ret = requests.post(
|
|
808
|
+
f"http://{self._ip}/users",
|
|
809
|
+
json={
|
|
810
|
+
"organization": organization,
|
|
811
|
+
"name": new_user,
|
|
812
|
+
"password": new_pwd,
|
|
813
|
+
"email": email,
|
|
814
|
+
"is_admin": is_admin,
|
|
815
|
+
},
|
|
816
|
+
headers=self._headers,
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
if ret.status_code != 200:
|
|
820
|
+
raise ValueError(ret.json()['detail'])
|
|
821
|
+
|
|
822
|
+
return ret.status_code
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def delete_user(self, user_to_delete):
|
|
826
|
+
'''
|
|
827
|
+
This mehotd deletes user.
|
|
828
|
+
'''
|
|
829
|
+
|
|
830
|
+
ret = requests.delete(
|
|
831
|
+
f"http://{self._ip}/users/{user_to_delete}",
|
|
832
|
+
headers=self._headers
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
if ret.status_code != 204:
|
|
836
|
+
raise ValueError(ret.json()['detail'])
|
|
837
|
+
|
|
838
|
+
return ret.status_code
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
#-----------------------#
|
|
842
|
+
#--- manage datasets ---#
|
|
843
|
+
def delete_dataset(self, dataset, organization):
|
|
844
|
+
'''
|
|
845
|
+
This method deletes a complete dataset.
|
|
846
|
+
'''
|
|
847
|
+
|
|
848
|
+
ret = requests.delete(
|
|
849
|
+
f"http://{self._ip}/organizations/{organization}/datasets/{dataset}",
|
|
850
|
+
headers=self._headers
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
if ret.status_code != 204:
|
|
854
|
+
raise ValueError(ret.json()['detail'])
|
|
855
|
+
|
|
856
|
+
return ret.status_code
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
#-----------------------#
|
|
860
|
+
#--- manage extended ---#
|
|
861
|
+
def add_extended(self, dataset, extended_dict, organization):
|
|
862
|
+
'''
|
|
863
|
+
This methods adds an extended_dict in the extended table of dataset.
|
|
864
|
+
'''
|
|
865
|
+
|
|
866
|
+
ret = requests.patch(
|
|
867
|
+
f"http://{self._ip}/organizations/{organization}/datasets/{dataset}/extended",
|
|
868
|
+
json=extended_dict,
|
|
869
|
+
headers=self._headers
|
|
870
|
+
)
|
|
871
|
+
|
|
872
|
+
if ret.status_code != 200:
|
|
873
|
+
raise ValueError(ret.json()['detail'])
|
|
874
|
+
|
|
875
|
+
return ret.status_code
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
def delete_extended(self, dataset, key, organization):
|
|
879
|
+
'''
|
|
880
|
+
This method deletes the extended values with key 'key' in the dataset 'dataset'.
|
|
881
|
+
'''
|
|
882
|
+
|
|
883
|
+
ret = requests.delete(
|
|
884
|
+
f"http://{self._ip}/organizations/{organization}/datasets/{dataset}/extended",
|
|
885
|
+
params={'key': key},
|
|
886
|
+
headers=self._headers
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
if ret.status_code != 204:
|
|
890
|
+
raise ValueError(ret.json()['detail'])
|
|
891
|
+
|
|
892
|
+
return ret.status_code
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
#-----------------------#
|
|
896
|
+
#--- manage polygons ---#
|
|
897
|
+
def add_polygons(self, polygons_dataset, polygons_json, organization):
|
|
898
|
+
'''
|
|
899
|
+
This method adds polygons to the dataset with name dataset_name.
|
|
900
|
+
'''
|
|
901
|
+
|
|
902
|
+
polygons_json = self._reformat_polygons(polygons_json)
|
|
903
|
+
|
|
904
|
+
ret = requests.put(
|
|
905
|
+
f"http://{self._ip}/organizations/{organization}/polygons/{polygons_dataset}",
|
|
906
|
+
json=polygons_json,
|
|
907
|
+
headers=self._headers
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
if ret.status_code != 200:
|
|
911
|
+
raise ValueError(ret.json()['detail'])
|
|
912
|
+
|
|
913
|
+
return ret.status_code
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
def delete_polygons(self, polygons_dataset, organization):
|
|
917
|
+
'''
|
|
918
|
+
This method delete polygons of dataset with name polygons_dataset.
|
|
919
|
+
'''
|
|
920
|
+
|
|
921
|
+
ret = requests.delete(
|
|
922
|
+
f"http://{self._ip}/organizations/{organization}/polygons/{polygons_dataset}",
|
|
923
|
+
headers=self._headers
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
if ret.status_code != 204:
|
|
927
|
+
raise ValueError(ret.json()['detail'])
|
|
928
|
+
|
|
929
|
+
return ret.status_code
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyDetektia
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A package for easily interaction with Detektia's API endpoints.
|
|
5
|
+
Home-page: https://github.com/detektia/pyDetektia
|
|
6
|
+
Author: Detektia
|
|
7
|
+
Author-email: info@detektia.com
|
|
8
|
+
License: ToBeChosen
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE.txt
|
|
11
|
+
|
|
12
|
+
# pyDetektia
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
pyDetektia/__init__.py
|
|
5
|
+
pyDetektia/manager.py
|
|
6
|
+
pyDetektia.egg-info/PKG-INFO
|
|
7
|
+
pyDetektia.egg-info/SOURCES.txt
|
|
8
|
+
pyDetektia.egg-info/dependency_links.txt
|
|
9
|
+
pyDetektia.egg-info/requires.txt
|
|
10
|
+
pyDetektia.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyDetektia
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name='pyDetektia',
|
|
5
|
+
version='1.0.0',
|
|
6
|
+
url='https://github.com/detektia/pyDetektia',
|
|
7
|
+
author='Detektia',
|
|
8
|
+
author_email='info@detektia.com',
|
|
9
|
+
packages=['pyDetektia'],
|
|
10
|
+
install_requires=['requests'],
|
|
11
|
+
license='ToBeChosen',
|
|
12
|
+
description="A package for easily interaction with Detektia's API endpoints.",
|
|
13
|
+
long_description=open('README.md').read(),
|
|
14
|
+
long_description_content_type='text/markdown',
|
|
15
|
+
)
|