Browse Source

Expose count info in Settlement

Maarten van den Berg 6 years ago
parent
commit
130a212800
2 changed files with 122 additions and 10 deletions
  1. 113 8
      piket_client/model.py
  2. 9 2
      piket_server/__init__.py

+ 113 - 8
piket_client/model.py

@@ -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"])

+ 9 - 2
piket_server/__init__.py

@@ -71,7 +71,7 @@ class Export(db.Model):
71 71
         return {
72 72
             "export_id": self.export_id,
73 73
             "created_at": self.created_at.isoformat(),
74
-            "settlements": [s.settlement_id for s in self.settlements],
74
+            "settlement_ids": [s.settlement_id for s in self.settlements],
75 75
         }
76 76
 
77 77
 
@@ -472,6 +472,11 @@ def get_export(export_id: int):
472 472
 @app.route("/exports", methods=["POST"])
473 473
 def add_export():
474 474
     """ Create an Export, and link all un-exported Settlements to it. """
475
+    # Assert that there are Settlements to be exported.
476
+    s_count = Settlement.query.filter_by(export=None).count()
477
+    if s_count == 0:
478
+        return jsonify(error="No un-exported Settlements."), 403
479
+
475 480
     e = Export()
476 481
 
477 482
     db.session.add(e)
@@ -480,4 +485,6 @@ def add_export():
480 485
     Settlement.query.filter_by(export=None).update({"export_id": e.export_id})
481 486
     db.session.commit()
482 487
 
483
-    return jsonify(export=e.as_dict), 201
488
+    ss = [s.as_dict for s in e.settlements]
489
+
490
+    return jsonify(export=e.as_dict, settlements=ss), 201