|
@@ -3,7 +3,7 @@ Provides access to the models stored in the database, via the server.
|
3
|
3
|
"""
|
4
|
4
|
import datetime
|
5
|
5
|
import logging
|
6
|
|
-from typing import NamedTuple
|
|
6
|
+from typing import NamedTuple, Sequence
|
7
|
7
|
from urllib.parse import urljoin
|
8
|
8
|
|
9
|
9
|
import requests
|
|
@@ -12,6 +12,7 @@ import requests
|
12
|
12
|
LOG = logging.getLogger(__name__)
|
13
|
13
|
|
14
|
14
|
SERVER_URL = "http://127.0.0.1:5000"
|
|
15
|
+DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
|
15
|
16
|
|
16
|
17
|
|
17
|
18
|
class ServerStatus:
|
|
@@ -29,8 +30,6 @@ class ServerStatus:
|
29
|
30
|
except requests.ConnectionError as ex:
|
30
|
31
|
return False, ex
|
31
|
32
|
|
32
|
|
- datetime_format = "%Y-%m-%dT%H:%M:%S.%f"
|
33
|
|
-
|
34
|
33
|
@classmethod
|
35
|
34
|
def unsettled_consumptions(cls) -> dict:
|
36
|
35
|
req = requests.get(urljoin(SERVER_URL, "status"))
|
|
@@ -39,10 +38,10 @@ class ServerStatus:
|
39
|
38
|
|
40
|
39
|
if data["unsettled"]["amount"]:
|
41
|
40
|
data["unsettled"]["first"] = datetime.datetime.strptime(
|
42
|
|
- data["unsettled"]["first"], cls.datetime_format
|
|
41
|
+ data["unsettled"]["first"], DATETIME_FORMAT
|
43
|
42
|
)
|
44
|
43
|
data["unsettled"]["last"] = datetime.datetime.strptime(
|
45
|
|
- data["unsettled"]["last"], cls.datetime_format
|
|
44
|
+ data["unsettled"]["last"], DATETIME_FORMAT
|
46
|
45
|
)
|
47
|
46
|
|
48
|
47
|
return data
|
|
@@ -190,6 +189,90 @@ class Person(NamedTuple):
|
190
|
189
|
)
|
191
|
190
|
|
192
|
191
|
|
|
192
|
+class Export(NamedTuple):
|
|
193
|
+ created_at: datetime.datetime
|
|
194
|
+ settlement_ids: Sequence[int]
|
|
195
|
+ export_id: int
|
|
196
|
+ settlements: Sequence["Settlement"] = []
|
|
197
|
+
|
|
198
|
+ @classmethod
|
|
199
|
+ def from_dict(cls, data: dict) -> "Export":
|
|
200
|
+ """ Reconstruct an Export from a dict. """
|
|
201
|
+ return cls(
|
|
202
|
+ export_id=data["export_id"],
|
|
203
|
+ created_at=datetime.datetime.strptime(data["created_at"], DATETIME_FORMAT),
|
|
204
|
+ settlement_ids=data["settlement_ids"],
|
|
205
|
+ settlements=data.get("settlements", []),
|
|
206
|
+ )
|
|
207
|
+
|
|
208
|
+ @classmethod
|
|
209
|
+ def get_all(cls) -> ["Export"]:
|
|
210
|
+ """ Get a list of all existing Exports. """
|
|
211
|
+ req = requests.get(urljoin(SERVER_URL, "exports"))
|
|
212
|
+
|
|
213
|
+ try:
|
|
214
|
+ data = req.json()
|
|
215
|
+ except ValueError:
|
|
216
|
+ LOG.error(
|
|
217
|
+ "Did not get JSON on listing Exports (%s): %s",
|
|
218
|
+ req.status_code,
|
|
219
|
+ req.content,
|
|
220
|
+ )
|
|
221
|
+ return None
|
|
222
|
+
|
|
223
|
+ if "error" in data or req.status_code != 200:
|
|
224
|
+ LOG.error("Could not list Exports (%s): %s", req.status_code, data)
|
|
225
|
+ return None
|
|
226
|
+
|
|
227
|
+ return [cls.from_dict(e) for e in data["exports"]]
|
|
228
|
+
|
|
229
|
+ @classmethod
|
|
230
|
+ def get(cls, export_id: int) -> "Export":
|
|
231
|
+ """ Retrieve one Export. """
|
|
232
|
+ req = requests.get(urljoin(SERVER_URL, f"exports/{export_id}"))
|
|
233
|
+
|
|
234
|
+ try:
|
|
235
|
+ data = req.json()
|
|
236
|
+ except ValueError:
|
|
237
|
+ LOG.error(
|
|
238
|
+ "Did not get JSON on getting Export (%s): %s",
|
|
239
|
+ req.status_code,
|
|
240
|
+ req.content,
|
|
241
|
+ )
|
|
242
|
+ return None
|
|
243
|
+
|
|
244
|
+ if "error" in data or req.status_code != 200:
|
|
245
|
+ LOG.error("Could not get Export (%s): %s", req.status_code, data)
|
|
246
|
+ return None
|
|
247
|
+
|
|
248
|
+ data["export"]["settlements"] = data["settlements"]
|
|
249
|
+
|
|
250
|
+ return cls.from_dict(data["export"])
|
|
251
|
+
|
|
252
|
+ @classmethod
|
|
253
|
+ def create(cls) -> "Export":
|
|
254
|
+ """ Create a new Export, containing all un-exported Settlements. """
|
|
255
|
+ req = requests.post(urljoin(SERVER_URL, "exports"))
|
|
256
|
+
|
|
257
|
+ try:
|
|
258
|
+ data = req.json()
|
|
259
|
+ except ValueError:
|
|
260
|
+ LOG.error(
|
|
261
|
+ "Did not get JSON on adding Export (%s): %s",
|
|
262
|
+ req.status_code,
|
|
263
|
+ req.content,
|
|
264
|
+ )
|
|
265
|
+ return None
|
|
266
|
+
|
|
267
|
+ if "error" in data or req.status_code != 201:
|
|
268
|
+ LOG.error("Could not create Export (%s): %s", req.status_code, data)
|
|
269
|
+ return None
|
|
270
|
+
|
|
271
|
+ data["export"]["settlements"] = data["settlements"]
|
|
272
|
+
|
|
273
|
+ return cls.from_dict(data["export"])
|
|
274
|
+
|
|
275
|
+
|
193
|
276
|
class ConsumptionType(NamedTuple):
|
194
|
277
|
""" Represents a stored ConsumptionType. """
|
195
|
278
|
|
|
@@ -299,14 +382,12 @@ class Consumption(NamedTuple):
|
299
|
382
|
@classmethod
|
300
|
383
|
def from_dict(cls, data: dict) -> "Consumption":
|
301
|
384
|
""" Reconstruct a Consumption from a dict. """
|
302
|
|
- datetime_format = "%Y-%m-%dT%H:%M:%S.%f"
|
303
|
|
- # 2018-08-31T17:30:47.871521
|
304
|
385
|
return cls(
|
305
|
386
|
consumption_id=data["consumption_id"],
|
306
|
387
|
person_id=data["person_id"],
|
307
|
388
|
consumption_type_id=data["consumption_type_id"],
|
308
|
389
|
settlement_id=data["settlement_id"],
|
309
|
|
- created_at=datetime.datetime.strptime(data["created_at"], datetime_format),
|
|
390
|
+ created_at=datetime.datetime.strptime(data["created_at"], DATETIME_FORMAT),
|
310
|
391
|
reversed=data["reversed"],
|
311
|
392
|
)
|
312
|
393
|
|
|
@@ -345,6 +426,7 @@ class Settlement(NamedTuple):
|
345
|
426
|
settlement_id: int
|
346
|
427
|
name: str
|
347
|
428
|
consumption_summary: dict
|
|
429
|
+ count_info: dict = {}
|
348
|
430
|
|
349
|
431
|
@classmethod
|
350
|
432
|
def from_dict(cls, data: dict) -> "Settlement":
|
|
@@ -352,6 +434,7 @@ class Settlement(NamedTuple):
|
352
|
434
|
settlement_id=data["settlement_id"],
|
353
|
435
|
name=data["name"],
|
354
|
436
|
consumption_summary=data["consumption_summary"],
|
|
437
|
+ count_info=data.get("count_info", {}),
|
355
|
438
|
)
|
356
|
439
|
|
357
|
440
|
@classmethod
|
|
@@ -361,3 +444,25 @@ class Settlement(NamedTuple):
|
361
|
444
|
)
|
362
|
445
|
|
363
|
446
|
return cls.from_dict(req.json()["settlement"])
|
|
447
|
+
|
|
448
|
+ @classmethod
|
|
449
|
+ def get(cls, settlement_id: int) -> "Settlement":
|
|
450
|
+ req = requests.get(urljoin(SERVER_URL, f"/settlements/{settlement_id}"))
|
|
451
|
+
|
|
452
|
+ try:
|
|
453
|
+ data = req.json()
|
|
454
|
+ except ValueError:
|
|
455
|
+ LOG.error(
|
|
456
|
+ "Did not get JSON on retrieving Settlement (%s): %s",
|
|
457
|
+ req.status_code,
|
|
458
|
+ req.content,
|
|
459
|
+ )
|
|
460
|
+ return None
|
|
461
|
+
|
|
462
|
+ if "error" in data or req.status_code != 200:
|
|
463
|
+ LOG.error("Could not get Export (%s): %s", req.status_code, data)
|
|
464
|
+ return None
|
|
465
|
+
|
|
466
|
+ data["settlement"]["count_info"] = data["count_info"]
|
|
467
|
+
|
|
468
|
+ return cls.from_dict(data["settlement"])
|