Quellcode durchsuchen

per_person_counts, NetworkError in client Settlement

Maarten van den Berg vor 5 Jahren
Ursprung
Commit
a92f2344c4
3 geänderte Dateien mit 61 neuen und 18 gelöschten Zeilen
  1. 20 17
      piket_client/model.py
  2. 40 0
      piket_server/models.py
  3. 1 1
      setup.py

+ 20 - 17
piket_client/model.py

@@ -7,7 +7,7 @@ import datetime
7 7
 import enum
8 8
 import logging
9 9
 from dataclasses import dataclass
10
-from typing import Any, List, NamedTuple, Optional, Sequence, Tuple, Union
10
+from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union
11 11
 from urllib.parse import urljoin
12 12
 
13 13
 import requests
@@ -473,8 +473,9 @@ class Settlement(NamedTuple):
473 473
 
474 474
     settlement_id: int
475 475
     name: str
476
-    consumption_summary: dict
477
-    count_info: dict = {}
476
+    consumption_summary: Dict[str, Any]
477
+    count_info: Dict[str, Any] = {}
478
+    per_person_counts: Dict[str, Any] = {}
478 479
 
479 480
     @classmethod
480 481
     def from_dict(cls, data: dict) -> "Settlement":
@@ -482,7 +483,8 @@ class Settlement(NamedTuple):
482 483
             settlement_id=data["settlement_id"],
483 484
             name=data["name"],
484 485
             consumption_summary=data["consumption_summary"],
485
-            count_info=data.get("count_info", {}),
486
+            count_info=data["count_info"],
487
+            per_person_counts=data["per_person_counts"],
486 488
         )
487 489
 
488 490
     @classmethod
@@ -494,22 +496,23 @@ class Settlement(NamedTuple):
494 496
         return cls.from_dict(req.json()["settlement"])
495 497
 
496 498
     @classmethod
497
-    def get(cls, settlement_id: int) -> Optional[Settlement]:
498
-        req = requests.get(urljoin(SERVER_URL, f"/settlements/{settlement_id}"))
499
-
499
+    def get(cls, settlement_id: int) -> Union[Settlement, NetworkError]:
500 500
         try:
501
+            req = requests.get(urljoin(SERVER_URL, f"/settlements/{settlement_id}"))
502
+            req.raise_for_status()
501 503
             data = req.json()
502
-        except ValueError:
503
-            LOG.error(
504
-                "Did not get JSON on retrieving Settlement (%s): %s",
505
-                req.status_code,
506
-                req.content,
507
-            )
508
-            return None
509 504
 
510
-        if "error" in data or req.status_code != 200:
511
-            LOG.error("Could not get Export (%s): %s", req.status_code, data)
512
-            return None
505
+        except ValueError as e:
506
+            LOG.exception(e)
507
+            return NetworkError.InvalidData
508
+
509
+        except requests.ConnectionError as e:
510
+            LOG.exception(e)
511
+            return NetworkError.ConnectionFailure
512
+
513
+        except requests.HTTPError as e:
514
+            LOG.exception(e)
515
+            return NetworkError.HttpFailure
513 516
 
514 517
         data["settlement"]["count_info"] = data["count_info"]
515 518
 

+ 40 - 0
piket_server/models.py

@@ -3,6 +3,8 @@ Defines database models used by the server.
3 3
 """
4 4
 
5 5
 import datetime
6
+from typing import List, Dict, Any
7
+from collections import defaultdict
6 8
 
7 9
 from sqlalchemy import func
8 10
 from sqlalchemy.exc import SQLAlchemyError
@@ -86,6 +88,7 @@ class Settlement(db.Model):
86 88
             "name": self.name,
87 89
             "consumption_summary": self.consumption_summary,
88 90
             "unique_people": self.unique_people,
91
+            "per_person_counts": self.per_person_counts,
89 92
         }
90 93
 
91 94
     @property
@@ -146,6 +149,43 @@ class Settlement(db.Model):
146 149
 
147 150
         return result
148 151
 
152
+    @property
153
+    def per_person_counts(self) -> Dict[int, Any]:
154
+        """
155
+        Output a more usable dict containing for each person in the settlement
156
+        how many of each consumption type was counted.
157
+        """
158
+
159
+        q = (
160
+            Consumption.query.filter_by(settlement=self)
161
+            .filter_by(reversed=False)
162
+            .group_by(Consumption.person_id)
163
+            .group_by(Consumption.consumption_type_id)
164
+            .group_by(Person.full_name)
165
+            .outerjoin(Person)
166
+            .with_entities(
167
+                Consumption.person_id,
168
+                Person.full_name,
169
+                Consumption.consumption_type_id,
170
+                func.count(Consumption.consumption_id),
171
+            )
172
+            .all()
173
+        )
174
+
175
+        res: Dict[int, Any] = defaultdict(dict)
176
+
177
+        for row in q:
178
+            item = res[row[0]]
179
+            item["full_name"] = row[1]
180
+            if not item.get("counts"):
181
+                item["counts"] = {}
182
+
183
+            item["counts"][row[2]] = row[3]
184
+
185
+        return res
186
+
187
+
188
+
149 189
 
150 190
 class ConsumptionType(db.Model):
151 191
     """ Represents a type of consumption to be counted. """

+ 1 - 1
setup.py

@@ -25,7 +25,7 @@ setup(
25 25
     extras_require={
26 26
         "dev": ["black", "pylint", "mypy", "isort"],
27 27
         "server": ["Flask", "SQLAlchemy", "Flask-SQLAlchemy", "alembic", "uwsgi"],
28
-        "client": ["PySide2", "qdarkstyle>=2.6.0", "requests", "simpleaudio", "click"],
28
+        "client": ["PySide2", "qdarkstyle>=2.6.0", "requests", "simpleaudio", "click", "prettytable"],
29 29
         "osk": ["dbus-python"],
30 30
         "sentry": ["raven"],
31 31
     },