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
 """
3
 """
4
 import datetime
4
 import datetime
5
 import logging
5
 import logging
6
-from typing import NamedTuple
6
+from typing import NamedTuple, Sequence
7
 from urllib.parse import urljoin
7
 from urllib.parse import urljoin
8
 
8
 
9
 import requests
9
 import requests
12
 LOG = logging.getLogger(__name__)
12
 LOG = logging.getLogger(__name__)
13
 
13
 
14
 SERVER_URL = "http://127.0.0.1:5000"
14
 SERVER_URL = "http://127.0.0.1:5000"
15
+DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
15
 
16
 
16
 
17
 
17
 class ServerStatus:
18
 class ServerStatus:
29
         except requests.ConnectionError as ex:
30
         except requests.ConnectionError as ex:
30
             return False, ex
31
             return False, ex
31
 
32
 
32
-    datetime_format = "%Y-%m-%dT%H:%M:%S.%f"
33
-
34
     @classmethod
33
     @classmethod
35
     def unsettled_consumptions(cls) -> dict:
34
     def unsettled_consumptions(cls) -> dict:
36
         req = requests.get(urljoin(SERVER_URL, "status"))
35
         req = requests.get(urljoin(SERVER_URL, "status"))
39
 
38
 
40
         if data["unsettled"]["amount"]:
39
         if data["unsettled"]["amount"]:
41
             data["unsettled"]["first"] = datetime.datetime.strptime(
40
             data["unsettled"]["first"] = datetime.datetime.strptime(
42
-                data["unsettled"]["first"], cls.datetime_format
41
+                data["unsettled"]["first"], DATETIME_FORMAT
43
             )
42
             )
44
             data["unsettled"]["last"] = datetime.datetime.strptime(
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
         return data
47
         return data
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
 class ConsumptionType(NamedTuple):
276
 class ConsumptionType(NamedTuple):
194
     """ Represents a stored ConsumptionType. """
277
     """ Represents a stored ConsumptionType. """
195
 
278
 
299
     @classmethod
382
     @classmethod
300
     def from_dict(cls, data: dict) -> "Consumption":
383
     def from_dict(cls, data: dict) -> "Consumption":
301
         """ Reconstruct a Consumption from a dict. """
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
         return cls(
385
         return cls(
305
             consumption_id=data["consumption_id"],
386
             consumption_id=data["consumption_id"],
306
             person_id=data["person_id"],
387
             person_id=data["person_id"],
307
             consumption_type_id=data["consumption_type_id"],
388
             consumption_type_id=data["consumption_type_id"],
308
             settlement_id=data["settlement_id"],
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
             reversed=data["reversed"],
391
             reversed=data["reversed"],
311
         )
392
         )
312
 
393
 
345
     settlement_id: int
426
     settlement_id: int
346
     name: str
427
     name: str
347
     consumption_summary: dict
428
     consumption_summary: dict
429
+    count_info: dict = {}
348
 
430
 
349
     @classmethod
431
     @classmethod
350
     def from_dict(cls, data: dict) -> "Settlement":
432
     def from_dict(cls, data: dict) -> "Settlement":
352
             settlement_id=data["settlement_id"],
434
             settlement_id=data["settlement_id"],
353
             name=data["name"],
435
             name=data["name"],
354
             consumption_summary=data["consumption_summary"],
436
             consumption_summary=data["consumption_summary"],
437
+            count_info=data.get("count_info", {}),
355
         )
438
         )
356
 
439
 
357
     @classmethod
440
     @classmethod
361
         )
444
         )
362
 
445
 
363
         return cls.from_dict(req.json()["settlement"])
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
         return {
71
         return {
72
             "export_id": self.export_id,
72
             "export_id": self.export_id,
73
             "created_at": self.created_at.isoformat(),
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
 @app.route("/exports", methods=["POST"])
472
 @app.route("/exports", methods=["POST"])
473
 def add_export():
473
 def add_export():
474
     """ Create an Export, and link all un-exported Settlements to it. """
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
     e = Export()
480
     e = Export()
476
 
481
 
477
     db.session.add(e)
482
     db.session.add(e)
480
     Settlement.query.filter_by(export=None).update({"export_id": e.export_id})
485
     Settlement.query.filter_by(export=None).update({"export_id": e.export_id})
481
     db.session.commit()
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