Browse Source

Add Person model to GUI

Maarten van den Berg 6 years ago
parent
commit
5a3af46bf7
4 changed files with 147 additions and 32 deletions
  1. 13 30
      piket_client/gui.py
  2. 7 0
      piket_client/logger.py
  3. 125 0
      piket_client/model.py
  4. 2 2
      piket_server/__init__.py

+ 13 - 30
piket_client/gui.py

@@ -3,7 +3,6 @@ Provides the graphical front-end for Piket.
3 3
 """
4 4
 import os
5 5
 import sys
6
-from urllib.parse import urljoin
7 6
 
8 7
 # pylint: disable=E0611
9 8
 from PySide2.QtWidgets import (
@@ -20,7 +19,6 @@ from PySide2.QtGui import QIcon
20 19
 from PySide2.QtCore import QSize, Qt
21 20
 
22 21
 # pylint: enable=E0611
23
-import requests
24 22
 
25 23
 try:
26 24
     import dbus
@@ -28,6 +26,7 @@ except ImportError:
28 26
     dbus = None
29 27
 
30 28
 from piket_client.sound import PLOP_WAVE
29
+from piket_client.model import Person
31 30
 
32 31
 
33 32
 def plop() -> None:
@@ -35,46 +34,31 @@ def plop() -> None:
35 34
     PLOP_WAVE.play()
36 35
 
37 36
 
38
-SERVER_URL = "http://127.0.0.1:5000"
39
-
40
-
41
-def get_people() -> [dict]:
42
-    """ Request list of active people from the server. """
43
-    request = requests.get(urljoin(SERVER_URL, "/people"))
44
-    return request.json()["people"]
45
-
46
-
47 37
 class NameButton(QPushButton):
48 38
     """ Wraps a QPushButton to provide a counter. """
49 39
 
50
-    def __init__(self, person: dict, *args, **kwargs) -> None:
51
-        self.person_id = person["person_id"]
52
-        self.name = person["name"]
53
-        self.count = person["consumptions"]["1"]
40
+    def __init__(self, person: Person, *args, **kwargs) -> None:
41
+        self.person = person
42
+        self.count = person.consumptions["1"]
54 43
 
55 44
         super().__init__(self.current_label, *args, **kwargs)
56 45
         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
57 46
 
58
-        self.clicked.connect(self.plop)
47
+        self.clicked.connect(self.process_click)
59 48
 
60 49
     @property
61 50
     def current_label(self) -> str:
62 51
         """ Return the label to show on the button. """
63
-        return f"{self.name} ({self.count})"
52
+        return f"{self.person.name} ({self.count})"
64 53
 
65
-    def plop(self) -> None:
54
+    def process_click(self) -> None:
66 55
         """ Process a click on this button. """
67
-        req = requests.post(
68
-            urljoin(SERVER_URL, f"people/{self.person_id}/add_consumption")
69
-        )
70
-        if req.status_code == 200:
71
-            json = req.json()
72
-            person = json["person"]
73
-            self.count = person["consumptions"]["1"]
56
+        if self.person.add_consumption():
57
+            self.count = self.person.consumptions["1"]
74 58
             self.setText(self.current_label)
75 59
             plop()
76 60
         else:
77
-            print("Oh shit kapot")
61
+            print("Jantoeternuitje, kapot")
78 62
 
79 63
 
80 64
 class NameButtons(QWidget):
@@ -92,7 +76,7 @@ class NameButtons(QWidget):
92 76
         for each. """
93 77
         self.layout = QGridLayout()
94 78
 
95
-        for index, person in enumerate(get_people()):
79
+        for index, person in enumerate(Person.get_all()):
96 80
             button = NameButton(person)
97 81
             self.layout.addWidget(button, index // 2, index % 2)
98 82
 
@@ -175,9 +159,8 @@ class PiketMainWindow(QMainWindow):
175 159
         )
176 160
         self.hide_keyboard()
177 161
         if ok and name:
178
-            req = requests.post(
179
-                urljoin(SERVER_URL, "people"), json={"person": {"name": name}}
180
-            )
162
+            person = Person(name=name)
163
+            person = person.save()
181 164
 
182 165
             self.main_widget = NameButtons()
183 166
             self.setCentralWidget(self.main_widget)

+ 7 - 0
piket_client/logger.py

@@ -0,0 +1,7 @@
1
+import logging
2
+from logging import getLogger
3
+
4
+logging.basicConfig(
5
+        format='%(asctime)s - %(name)s - %(levelname)s: %(message)s',
6
+        level=logging.INFO
7
+)

+ 125 - 0
piket_client/model.py

@@ -0,0 +1,125 @@
1
+'''
2
+Provides access to the models stored in the database, via the server.
3
+'''
4
+from typing import NamedTuple
5
+from urllib.parse import urljoin
6
+
7
+import requests
8
+
9
+from . import logger
10
+
11
+LOG = logger.getLogger('model')
12
+
13
+SERVER_URL = "http://127.0.0.1:5000"
14
+
15
+class Person(NamedTuple):
16
+    ''' Represents a Person, as retrieved from the database. '''
17
+    name: str
18
+    person_id: int = None
19
+    consumptions: dict = {}
20
+
21
+    def add_consumption(self) -> bool:
22
+        ''' Register a consumption for this Person. '''
23
+        req = requests.post(
24
+            urljoin(SERVER_URL, f'people/{self.person_id}/add_consumption')
25
+        )
26
+        try:
27
+            data = req.json()
28
+
29
+            if 'error' in data:
30
+                LOG.error(
31
+                    'Could not add consumption for %s (%s): %s',
32
+                    self.person_id, req.status_code, data
33
+                )
34
+                return False
35
+
36
+            self.consumptions.update(data['person']['consumptions'])
37
+
38
+            return True
39
+        except ValueError:
40
+            LOG.error(
41
+                'Did not get JSON on adding Consumption (%s): %s',
42
+                req.status_code, req.content
43
+            )
44
+            return False
45
+
46
+    def create(self) -> 'Person':
47
+        ''' Create a new Person from the current attributes. As tuples are
48
+        immutable, a new Person with the correct id is returned. '''
49
+        req = requests.post(
50
+            urljoin(SERVER_URL, "people"), json={"person": {"name": self.name}}
51
+        )
52
+
53
+        try:
54
+            data = req.json()
55
+        except ValueError:
56
+            LOG.error(
57
+                'Did not get JSON on adding Person (%s): %s',
58
+                req.status_code, req.content
59
+            )
60
+            return None
61
+
62
+        if 'error' in data or req.status_code != 201:
63
+            LOG.error(
64
+                'Could not create Person (%s): %s',
65
+                req.status_code, data
66
+            )
67
+            return None
68
+
69
+        return Person.from_dict(data['person'])
70
+
71
+    @classmethod
72
+    def get(cls, person_id: int) -> 'Person':
73
+        ''' Retrieve a Person by id. '''
74
+        req = requests.get(urljoin(SERVER_URL, f'/people/{person_id}'))
75
+
76
+        try:
77
+            data = req.json()
78
+
79
+            if 'error' in data:
80
+                LOG.warning(
81
+                    'Could not get person %s (%s): %s',
82
+                    person_id, req.status_code, data
83
+                )
84
+                return None
85
+
86
+            return Person.from_dict(data['person'])
87
+
88
+        except ValueError:
89
+            LOG.error(
90
+                'Did not get JSON from server on getting Person (%s): %s',
91
+                req.status_code, req.content
92
+            )
93
+            return None
94
+
95
+    @classmethod
96
+    def get_all(cls) -> ['Person']:
97
+        ''' Get all active People. '''
98
+        req = requests.get(urljoin(SERVER_URL, '/people'))
99
+
100
+        try:
101
+            data = req.json()
102
+
103
+            if 'error' in data:
104
+                LOG.warning(
105
+                    'Could not get people (%s): %s',
106
+                    req.status_code, data
107
+                )
108
+
109
+            return [Person.from_dict(item) for item in data['people']]
110
+
111
+        except ValueError:
112
+            LOG.error(
113
+                'Did not get JSON from server on getting People (%s): %s',
114
+                req.status_code, req.content
115
+            )
116
+            return None
117
+
118
+    @classmethod
119
+    def from_dict(cls, data: dict) -> 'Person':
120
+        ''' Reconstruct a Person object from a dict. '''
121
+        return Person(
122
+            name = data['name'],
123
+            person_id = data['person_id'],
124
+            consumptions = data['consumptions']
125
+        )

+ 2 - 2
piket_server/__init__.py

@@ -179,7 +179,7 @@ def add_person():
179 179
     except SQLAlchemyError:
180 180
         return jsonify({"error": "Invalid arguments for Person."})
181 181
 
182
-    return jsonify(person=person.as_dict)
182
+    return jsonify(person=person.as_dict), 201
183 183
 
184 184
 
185 185
 @app.route("/people/<int:person_id>/add_consumption", methods=["POST"])
@@ -198,4 +198,4 @@ def add_consumption(person_id: int):
198 198
             400,
199 199
         )
200 200
 
201
-    return jsonify(person=person.as_dict, consumption=consumption.as_dict)
201
+    return jsonify(person=person.as_dict, consumption=consumption.as_dict), 201