Browse Source

per_person_counts, NetworkError in client Settlement

Maarten van den Berg 5 years ago
parent
commit
a5cb66ab29
3 changed files with 61 additions and 18 deletions
  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
 import enum
7
 import enum
8
 import logging
8
 import logging
9
 from dataclasses import dataclass
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
 from urllib.parse import urljoin
11
 from urllib.parse import urljoin
12
 
12
 
13
 import requests
13
 import requests
473
 
473
 
474
     settlement_id: int
474
     settlement_id: int
475
     name: str
475
     name: str
476
-    consumption_summary: dict
476
+    consumption_summary: Dict[str, Any]
477
-    count_info: dict = {}
477
+    count_info: Dict[str, Any] = {}
478
+    per_person_counts: Dict[str, Any] = {}
478
 
479
 
479
     @classmethod
480
     @classmethod
480
     def from_dict(cls, data: dict) -> "Settlement":
481
     def from_dict(cls, data: dict) -> "Settlement":
482
             settlement_id=data["settlement_id"],
483
             settlement_id=data["settlement_id"],
483
             name=data["name"],
484
             name=data["name"],
484
             consumption_summary=data["consumption_summary"],
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
     @classmethod
490
     @classmethod
494
         return cls.from_dict(req.json()["settlement"])
496
         return cls.from_dict(req.json()["settlement"])
495
 
497
 
496
     @classmethod
498
     @classmethod
497
-    def get(cls, settlement_id: int) -> Optional[Settlement]:
499
+    def get(cls, settlement_id: int) -> Union[Settlement, NetworkError]:
498
-        req = requests.get(urljoin(SERVER_URL, f"/settlements/{settlement_id}"))
499
-
500
         try:
500
         try:
501
+            req = requests.get(urljoin(SERVER_URL, f"/settlements/{settlement_id}"))
502
+            req.raise_for_status()
501
             data = req.json()
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:
505
+        except ValueError as e:
511
-            LOG.error("Could not get Export (%s): %s", req.status_code, data)
506
+            LOG.exception(e)
512
-            return None
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
         data["settlement"]["count_info"] = data["count_info"]
517
         data["settlement"]["count_info"] = data["count_info"]
515
 
518
 

+ 40 - 0
piket_server/models.py

3
 """
3
 """
4
 
4
 
5
 import datetime
5
 import datetime
6
+from typing import List, Dict, Any
7
+from collections import defaultdict
6
 
8
 
7
 from sqlalchemy import func
9
 from sqlalchemy import func
8
 from sqlalchemy.exc import SQLAlchemyError
10
 from sqlalchemy.exc import SQLAlchemyError
86
             "name": self.name,
88
             "name": self.name,
87
             "consumption_summary": self.consumption_summary,
89
             "consumption_summary": self.consumption_summary,
88
             "unique_people": self.unique_people,
90
             "unique_people": self.unique_people,
91
+            "per_person_counts": self.per_person_counts,
89
         }
92
         }
90
 
93
 
91
     @property
94
     @property
146
 
149
 
147
         return result
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
 class ConsumptionType(db.Model):
190
 class ConsumptionType(db.Model):
151
     """ Represents a type of consumption to be counted. """
191
     """ Represents a type of consumption to be counted. """

+ 1 - 1
setup.py

25
     extras_require={
25
     extras_require={
26
         "dev": ["black", "pylint", "mypy", "isort"],
26
         "dev": ["black", "pylint", "mypy", "isort"],
27
         "server": ["Flask", "SQLAlchemy", "Flask-SQLAlchemy", "alembic", "uwsgi"],
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
         "osk": ["dbus-python"],
29
         "osk": ["dbus-python"],
30
         "sentry": ["raven"],
30
         "sentry": ["raven"],
31
     },
31
     },