Maarten van den Berg 6 years ago
parent
commit
4bf03e3923

+ 6 - 10
piket_client/gui.py

@@ -127,8 +127,9 @@ class PiketMainWindow(QMainWindow):
127 127
         if dbus:
128 128
             try:
129 129
                 session_bus = dbus.SessionBus()
130
-                self.osk = session_bus.get_object('org.onboard.Onboard',
131
-                        '/org/onboard/Onboard/Keyboard')
130
+                self.osk = session_bus.get_object(
131
+                    "org.onboard.Onboard", "/org/onboard/Onboard/Keyboard"
132
+                )
132 133
             except dbus.exceptions.DBusException:
133 134
                 # Onboard not present or dbus broken
134 135
                 self.osk = None
@@ -157,11 +158,7 @@ class PiketMainWindow(QMainWindow):
157 158
         ag.setExclusive(True)
158 159
 
159 160
         for ct in ConsumptionType.get_all():
160
-            action = QAction(
161
-                self.load_icon(ct.icon or 'beer_bottle.svg'),
162
-                ct.name,
163
-                ag
164
-            )
161
+            action = QAction(self.load_icon(ct.icon or "beer_bottle.svg"), ct.name, ag)
165 162
             action.setCheckable(True)
166 163
             action.setData(str(ct.consumption_type_id))
167 164
 
@@ -176,18 +173,17 @@ class PiketMainWindow(QMainWindow):
176 173
         self.consumption_type_changed.connect(self.main_widget.consumption_type_changed)
177 174
         self.setCentralWidget(self.main_widget)
178 175
 
179
-
180 176
     @Slot(QAction)
181 177
     def consumption_type_change(self, action: QAction):
182 178
         self.consumption_type_changed.emit(action.data())
183 179
 
184 180
     def show_keyboard(self) -> None:
185
-        ''' Show the virtual keyboard, if possible. '''
181
+        """ Show the virtual keyboard, if possible. """
186 182
         if self.osk:
187 183
             self.osk.Show()
188 184
 
189 185
     def hide_keyboard(self) -> None:
190
-        ''' Hide the virtual keyboard, if possible. '''
186
+        """ Hide the virtual keyboard, if possible. """
191 187
         if self.osk:
192 188
             self.osk.Hide()
193 189
 

+ 1 - 2
piket_client/logger.py

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

+ 90 - 85
piket_client/model.py

@@ -1,6 +1,6 @@
1
-'''
1
+"""
2 2
 Provides access to the models stored in the database, via the server.
3
-'''
3
+"""
4 4
 from typing import NamedTuple
5 5
 from urllib.parse import urljoin
6 6
 
@@ -8,45 +8,49 @@ import requests
8 8
 
9 9
 from . import logger
10 10
 
11
-LOG = logger.getLogger('model')
11
+LOG = logger.getLogger("model")
12 12
 
13 13
 SERVER_URL = "http://127.0.0.1:5000"
14 14
 
15
+
15 16
 class Person(NamedTuple):
16
-    ''' Represents a Person, as retrieved from the database. '''
17
+    """ Represents a Person, as retrieved from the database. """
18
+
17 19
     name: str
18 20
     person_id: int = None
19 21
     consumptions: dict = {}
20 22
 
21 23
     def add_consumption(self, type_id: str) -> bool:
22
-        ''' Register a consumption for this Person. '''
24
+        """ Register a consumption for this Person. """
23 25
         req = requests.post(
24
-            urljoin(SERVER_URL,
25
-                f'people/{self.person_id}/add_consumption/{type_id}')
26
+            urljoin(SERVER_URL, f"people/{self.person_id}/add_consumption/{type_id}")
26 27
         )
27 28
         try:
28 29
             data = req.json()
29 30
 
30
-            if 'error' in data:
31
+            if "error" in data:
31 32
                 LOG.error(
32
-                    'Could not add consumption for %s (%s): %s',
33
-                    self.person_id, req.status_code, data
33
+                    "Could not add consumption for %s (%s): %s",
34
+                    self.person_id,
35
+                    req.status_code,
36
+                    data,
34 37
                 )
35 38
                 return False
36 39
 
37
-            self.consumptions.update(data['person']['consumptions'])
40
+            self.consumptions.update(data["person"]["consumptions"])
38 41
 
39 42
             return True
40 43
         except ValueError:
41 44
             LOG.error(
42
-                'Did not get JSON on adding Consumption (%s): %s',
43
-                req.status_code, req.content
45
+                "Did not get JSON on adding Consumption (%s): %s",
46
+                req.status_code,
47
+                req.content,
44 48
             )
45 49
             return False
46 50
 
47
-    def create(self) -> 'Person':
48
-        ''' Create a new Person from the current attributes. As tuples are
49
-        immutable, a new Person with the correct id is returned. '''
51
+    def create(self) -> "Person":
52
+        """ Create a new Person from the current attributes. As tuples are
53
+        immutable, a new Person with the correct id is returned. """
50 54
         req = requests.post(
51 55
             urljoin(SERVER_URL, "people"), json={"person": {"name": self.name}}
52 56
         )
@@ -55,163 +59,164 @@ class Person(NamedTuple):
55 59
             data = req.json()
56 60
         except ValueError:
57 61
             LOG.error(
58
-                'Did not get JSON on adding Person (%s): %s',
59
-                req.status_code, req.content
62
+                "Did not get JSON on adding Person (%s): %s",
63
+                req.status_code,
64
+                req.content,
60 65
             )
61 66
             return None
62 67
 
63
-        if 'error' in data or req.status_code != 201:
64
-            LOG.error(
65
-                'Could not create Person (%s): %s',
66
-                req.status_code, data
67
-            )
68
+        if "error" in data or req.status_code != 201:
69
+            LOG.error("Could not create Person (%s): %s", req.status_code, data)
68 70
             return None
69 71
 
70
-        return Person.from_dict(data['person'])
72
+        return Person.from_dict(data["person"])
71 73
 
72 74
     @classmethod
73
-    def get(cls, person_id: int) -> 'Person':
74
-        ''' Retrieve a Person by id. '''
75
-        req = requests.get(urljoin(SERVER_URL, f'/people/{person_id}'))
75
+    def get(cls, person_id: int) -> "Person":
76
+        """ Retrieve a Person by id. """
77
+        req = requests.get(urljoin(SERVER_URL, f"/people/{person_id}"))
76 78
 
77 79
         try:
78 80
             data = req.json()
79 81
 
80
-            if 'error' in data:
82
+            if "error" in data:
81 83
                 LOG.warning(
82
-                    'Could not get person %s (%s): %s',
83
-                    person_id, req.status_code, data
84
+                    "Could not get person %s (%s): %s", person_id, req.status_code, data
84 85
                 )
85 86
                 return None
86 87
 
87
-            return Person.from_dict(data['person'])
88
+            return Person.from_dict(data["person"])
88 89
 
89 90
         except ValueError:
90 91
             LOG.error(
91
-                'Did not get JSON from server on getting Person (%s): %s',
92
-                req.status_code, req.content
92
+                "Did not get JSON from server on getting Person (%s): %s",
93
+                req.status_code,
94
+                req.content,
93 95
             )
94 96
             return None
95 97
 
96 98
     @classmethod
97
-    def get_all(cls) -> ['Person']:
98
-        ''' Get all active People. '''
99
-        req = requests.get(urljoin(SERVER_URL, '/people'))
99
+    def get_all(cls) -> ["Person"]:
100
+        """ Get all active People. """
101
+        req = requests.get(urljoin(SERVER_URL, "/people"))
100 102
 
101 103
         try:
102 104
             data = req.json()
103 105
 
104
-            if 'error' in data:
105
-                LOG.warning(
106
-                    'Could not get people (%s): %s',
107
-                    req.status_code, data
108
-                )
106
+            if "error" in data:
107
+                LOG.warning("Could not get people (%s): %s", req.status_code, data)
109 108
 
110
-            return [Person.from_dict(item) for item in data['people']]
109
+            return [Person.from_dict(item) for item in data["people"]]
111 110
 
112 111
         except ValueError:
113 112
             LOG.error(
114
-                'Did not get JSON from server on getting People (%s): %s',
115
-                req.status_code, req.content
113
+                "Did not get JSON from server on getting People (%s): %s",
114
+                req.status_code,
115
+                req.content,
116 116
             )
117 117
             return None
118 118
 
119 119
     @classmethod
120
-    def from_dict(cls, data: dict) -> 'Person':
121
-        ''' Reconstruct a Person object from a dict. '''
120
+    def from_dict(cls, data: dict) -> "Person":
121
+        """ Reconstruct a Person object from a dict. """
122 122
         return Person(
123
-            name = data['name'],
124
-            person_id = data['person_id'],
125
-            consumptions = data['consumptions']
123
+            name=data["name"],
124
+            person_id=data["person_id"],
125
+            consumptions=data["consumptions"],
126 126
         )
127 127
 
128
+
128 129
 class ConsumptionType(NamedTuple):
129
-    ''' Represents a stored ConsumptionType. '''
130
+    """ Represents a stored ConsumptionType. """
130 131
 
131 132
     name: str
132 133
     consumption_type_id: int = None
133 134
     icon: str = None
134 135
 
135
-    def create(self) -> 'Person':
136
-        ''' Create a new ConsumptionType from the current attributes. As tuples
136
+    def create(self) -> "Person":
137
+        """ Create a new ConsumptionType from the current attributes. As tuples
137 138
         are immutable, a new ConsumptionType with the correct id is returned.
138
-        '''
139
+        """
139 140
         req = requests.post(
140
-            urljoin(SERVER_URL, "consumption_types"), json={"consumption_type":
141
-                {"name": self.name, "icon": self.icon}}
141
+            urljoin(SERVER_URL, "consumption_types"),
142
+            json={"consumption_type": {"name": self.name, "icon": self.icon}},
142 143
         )
143 144
 
144 145
         try:
145 146
             data = req.json()
146 147
         except ValueError:
147 148
             LOG.error(
148
-                'Did not get JSON on adding ConsumptionType (%s): %s',
149
-                req.status_code, req.content
149
+                "Did not get JSON on adding ConsumptionType (%s): %s",
150
+                req.status_code,
151
+                req.content,
150 152
             )
151 153
             return None
152 154
 
153
-        if 'error' in data or req.status_code != 201:
155
+        if "error" in data or req.status_code != 201:
154 156
             LOG.error(
155
-                'Could not create ConsumptionType (%s): %s',
156
-                req.status_code, data
157
+                "Could not create ConsumptionType (%s): %s", req.status_code, data
157 158
             )
158 159
             return None
159 160
 
160
-        return Person.from_dict(data['consumption_type'])
161
+        return Person.from_dict(data["consumption_type"])
161 162
 
162 163
     @classmethod
163
-    def get(cls, consumption_type_id: int) -> 'ConsumptionType':
164
-        ''' Retrieve a ConsumptionType by id. '''
165
-        req = requests.get(urljoin(SERVER_URL,
166
-            f'/consumption_types/{consumption_type_id}'))
164
+    def get(cls, consumption_type_id: int) -> "ConsumptionType":
165
+        """ Retrieve a ConsumptionType by id. """
166
+        req = requests.get(
167
+            urljoin(SERVER_URL, f"/consumption_types/{consumption_type_id}")
168
+        )
167 169
 
168 170
         try:
169 171
             data = req.json()
170 172
 
171
-            if 'error' in data:
173
+            if "error" in data:
172 174
                 LOG.warning(
173
-                    'Could not get consumption type %s (%s): %s',
174
-                    consumption_type_id, req.status_code, data
175
+                    "Could not get consumption type %s (%s): %s",
176
+                    consumption_type_id,
177
+                    req.status_code,
178
+                    data,
175 179
                 )
176 180
                 return None
177 181
 
178
-            return cls.from_dict(data['consumption_type'])
182
+            return cls.from_dict(data["consumption_type"])
179 183
 
180 184
         except ValueError:
181 185
             LOG.error(
182
-                'Did not get JSON from server on getting consumption type (%s): %s',
183
-                req.status_code, req.content
186
+                "Did not get JSON from server on getting consumption type (%s): %s",
187
+                req.status_code,
188
+                req.content,
184 189
             )
185 190
             return None
186 191
 
187 192
     @classmethod
188
-    def get_all(cls) -> ['ConsumptionType']:
189
-        ''' Get all active ConsumptionTypes. '''
190
-        req = requests.get(urljoin(SERVER_URL, '/consumption_types'))
193
+    def get_all(cls) -> ["ConsumptionType"]:
194
+        """ Get all active ConsumptionTypes. """
195
+        req = requests.get(urljoin(SERVER_URL, "/consumption_types"))
191 196
 
192 197
         try:
193 198
             data = req.json()
194 199
 
195
-            if 'error' in data:
200
+            if "error" in data:
196 201
                 LOG.warning(
197
-                    'Could not get consumption types (%s): %s',
198
-                    req.status_code, data
202
+                    "Could not get consumption types (%s): %s", req.status_code, data
199 203
                 )
200 204
 
201
-            return [cls.from_dict(item) for item in data['consumption_types']]
205
+            return [cls.from_dict(item) for item in data["consumption_types"]]
202 206
 
203 207
         except ValueError:
204 208
             LOG.error(
205
-                'Did not get JSON from server on getting ConsumptionTypes (%s): %s',
206
-                req.status_code, req.content
209
+                "Did not get JSON from server on getting ConsumptionTypes (%s): %s",
210
+                req.status_code,
211
+                req.content,
207 212
             )
208 213
             return None
209 214
 
210 215
     @classmethod
211
-    def from_dict(cls, data: dict) -> 'ConsumptionType':
212
-        ''' Reconstruct a ConsumptionType from a dict. '''
216
+    def from_dict(cls, data: dict) -> "ConsumptionType":
217
+        """ Reconstruct a ConsumptionType from a dict. """
213 218
         return ConsumptionType(
214
-            name = data['name'],
215
-            consumption_type_id = data['consumption_type_id'],
216
-            icon = data.get('icon')
219
+            name=data["name"],
220
+            consumption_type_id=data["consumption_type_id"],
221
+            icon=data.get("icon"),
217 222
         )

+ 7 - 7
piket_server/__init__.py

@@ -185,6 +185,7 @@ def add_consumption(person_id: int):
185 185
 
186 186
     return jsonify(person=person.as_dict, consumption=consumption.as_dict), 201
187 187
 
188
+
188 189
 @app.route("/people/<int:person_id>/add_consumption/<int:ct_id>", methods=["POST"])
189 190
 def add_consumption2(person_id: int, ct_id: int):
190 191
     person = Person.query.get_or_404(person_id)
@@ -207,17 +208,19 @@ def add_consumption2(person_id: int, ct_id: int):
207 208
 # ConsumptionType
208 209
 @app.route("/consumption_types", methods=["GET"])
209 210
 def get_consumption_types():
210
-    ''' Return a list of currently active consumption types. '''
211
+    """ Return a list of currently active consumption types. """
211 212
     ctypes = ConsumptionType.query.all()
212 213
     result = [ct.as_dict for ct in ctypes]
213 214
     return jsonify(consumption_types=result)
214 215
 
216
+
215 217
 @app.route("/consumption_types/<int:consumption_type_id>", methods=["GET"])
216 218
 def get_consumption_type(consumption_type_id: int):
217 219
     ct = ConsumptionType.query.get_or_404(consumption_type_id)
218 220
 
219 221
     return jsonify(consumption_type=ct.as_dict)
220 222
 
223
+
221 224
 @app.route("/consumption_types", methods=["POST"])
222 225
 def add_consumption_type():
223 226
     """ Add a new ConsumptionType.  """
@@ -226,16 +229,13 @@ def add_consumption_type():
226 229
     if not json:
227 230
         return jsonify({"error": "Could not parse JSON."}), 400
228 231
 
229
-    data = json.get('consumption_type') or {}
230
-    ct = ConsumptionType(
231
-        name=data.get('name'),
232
-        icon=data.get('icon')
233
-    )
232
+    data = json.get("consumption_type") or {}
233
+    ct = ConsumptionType(name=data.get("name"), icon=data.get("icon"))
234 234
 
235 235
     try:
236 236
         db.session.add(ct)
237 237
         db.session.commit()
238 238
     except SQLAlchemyError:
239
-        return jsonify({'error': 'Invalid arguments for ConsumptionType.'}), 400
239
+        return jsonify({"error": "Invalid arguments for ConsumptionType."}), 400
240 240
 
241 241
     return jsonify(consumption_type=ct.as_dict), 201

+ 9 - 9
piket_server/alembic/env.py

@@ -17,6 +17,7 @@ fileConfig(config.config_file_name)
17 17
 # from myapp import mymodel
18 18
 # target_metadata = mymodel.Base.metadata
19 19
 import piket_server
20
+
20 21
 target_metadata = piket_server.db.Model.metadata
21 22
 
22 23
 # other values from the config, defined by the needs of env.py,
@@ -24,9 +25,10 @@ target_metadata = piket_server.db.Model.metadata
24 25
 # my_important_option = config.get_main_option("my_important_option")
25 26
 # ... etc.
26 27
 from piket_server import CONFIG_DIR, DB_URL
28
+
27 29
 os.makedirs(os.path.expanduser(CONFIG_DIR), mode=0o744, exist_ok=True)
28 30
 
29
-config.file_config['alembic']['sqlalchemy.url'] = DB_URL
31
+config.file_config["alembic"]["sqlalchemy.url"] = DB_URL
30 32
 
31 33
 
32 34
 def run_migrations_offline():
@@ -42,8 +44,7 @@ def run_migrations_offline():
42 44
 
43 45
     """
44 46
     url = config.get_main_option("sqlalchemy.url")
45
-    context.configure(
46
-        url=url, target_metadata=target_metadata, literal_binds=True)
47
+    context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
47 48
 
48 49
     with context.begin_transaction():
49 50
         context.run_migrations()
@@ -58,18 +59,17 @@ def run_migrations_online():
58 59
     """
59 60
     connectable = engine_from_config(
60 61
         config.get_section(config.config_ini_section),
61
-        prefix='sqlalchemy.',
62
-        poolclass=pool.NullPool)
62
+        prefix="sqlalchemy.",
63
+        poolclass=pool.NullPool,
64
+    )
63 65
 
64 66
     with connectable.connect() as connection:
65
-        context.configure(
66
-            connection=connection,
67
-            target_metadata=target_metadata
68
-        )
67
+        context.configure(connection=connection, target_metadata=target_metadata)
69 68
 
70 69
         with context.begin_transaction():
71 70
             context.run_migrations()
72 71
 
72
+
73 73
 if context.is_offline_mode():
74 74
     run_migrations_offline()
75 75
 else:

+ 13 - 12
piket_server/alembic/versions/491bb980d1d7_add_settlement_allow_null_person_on_.py

@@ -10,30 +10,31 @@ import sqlalchemy as sa
10 10
 
11 11
 
12 12
 # revision identifiers, used by Alembic.
13
-revision = '491bb980d1d7'
14
-down_revision = 'de101d627237'
13
+revision = "491bb980d1d7"
14
+down_revision = "de101d627237"
15 15
 branch_labels = None
16 16
 depends_on = None
17 17
 
18 18
 
19 19
 def upgrade():
20 20
     op.create_table(
21
-        'settlements',
22
-        sa.Column('settlement_id', sa.Integer, primary_key=True),
23
-        sa.Column('name', sa.String, nullable=False)
21
+        "settlements",
22
+        sa.Column("settlement_id", sa.Integer, primary_key=True),
23
+        sa.Column("name", sa.String, nullable=False),
24 24
     )
25 25
 
26 26
     op.add_column(
27
-        'consumptions',
27
+        "consumptions",
28 28
         sa.Column(
29
-            'settlement_id', sa.Integer,
30
-            sa.ForeignKey('settlements.settlement_id'),
31
-            nullable=True
32
-        )
29
+            "settlement_id",
30
+            sa.Integer,
31
+            sa.ForeignKey("settlements.settlement_id"),
32
+            nullable=True,
33
+        ),
33 34
     )
34 35
 
35 36
 
36 37
 def downgrade():
37
-    op.drop_column('consumptions', 'settlement_id')
38
+    op.drop_column("consumptions", "settlement_id")
38 39
 
39
-    op.drop_table('settlements')
40
+    op.drop_table("settlements")

+ 5 - 5
piket_server/alembic/versions/a09086bfe84c_create_persons_table.py

@@ -10,7 +10,7 @@ import sqlalchemy as sa
10 10
 
11 11
 
12 12
 # revision identifiers, used by Alembic.
13
-revision = 'a09086bfe84c'
13
+revision = "a09086bfe84c"
14 14
 down_revision = None
15 15
 branch_labels = None
16 16
 depends_on = None
@@ -18,11 +18,11 @@ depends_on = None
18 18
 
19 19
 def upgrade():
20 20
     op.create_table(
21
-        'people',
22
-        sa.Column('person_id', sa.Integer, primary_key=True),
23
-        sa.Column('name', sa.String, nullable=False)
21
+        "people",
22
+        sa.Column("person_id", sa.Integer, primary_key=True),
23
+        sa.Column("name", sa.String, nullable=False),
24 24
     )
25 25
 
26 26
 
27 27
 def downgrade():
28
-    op.drop_table('people')
28
+    op.drop_table("people")

+ 18 - 17
piket_server/alembic/versions/de101d627237_create_consumptions_consumption_types.py

@@ -10,33 +10,34 @@ import sqlalchemy as sa
10 10
 
11 11
 
12 12
 # revision identifiers, used by Alembic.
13
-revision = 'de101d627237'
14
-down_revision = 'a09086bfe84c'
13
+revision = "de101d627237"
14
+down_revision = "a09086bfe84c"
15 15
 branch_labels = None
16 16
 depends_on = None
17 17
 
18 18
 
19 19
 def upgrade():
20 20
     op.create_table(
21
-        'consumption_types',
22
-        sa.Column('consumption_type_id', sa.Integer, nullable=False,
23
-            primary_key=True),
24
-        sa.Column('name', sa.String, nullable=False),
25
-        sa.Column('icon', sa.String, nullable=True),
21
+        "consumption_types",
22
+        sa.Column("consumption_type_id", sa.Integer, nullable=False, primary_key=True),
23
+        sa.Column("name", sa.String, nullable=False),
24
+        sa.Column("icon", sa.String, nullable=True),
26 25
     )
27 26
 
28 27
     op.create_table(
29
-        'consumptions',
30
-        sa.Column('consumption_id', sa.Integer, primary_key=True),
31
-        sa.Column('person_id', sa.Integer, nullable=True),
32
-        sa.Column('consumption_type_id', sa.Integer, nullable=False),
33
-        sa.Column('created_at', sa.DateTime, nullable=False),
34
-        sa.ForeignKeyConstraint(['consumption_type_id'], ['consumption_types.consumption_type_id'], ),
35
-        sa.ForeignKeyConstraint(['person_id'], ['people.person_id'], ),
36
-        sa.PrimaryKeyConstraint('consumption_id')
28
+        "consumptions",
29
+        sa.Column("consumption_id", sa.Integer, primary_key=True),
30
+        sa.Column("person_id", sa.Integer, nullable=True),
31
+        sa.Column("consumption_type_id", sa.Integer, nullable=False),
32
+        sa.Column("created_at", sa.DateTime, nullable=False),
33
+        sa.ForeignKeyConstraint(
34
+            ["consumption_type_id"], ["consumption_types.consumption_type_id"]
35
+        ),
36
+        sa.ForeignKeyConstraint(["person_id"], ["people.person_id"]),
37
+        sa.PrimaryKeyConstraint("consumption_id"),
37 38
     )
38 39
 
39 40
 
40 41
 def downgrade():
41
-    op.drop_table('consumptions')
42
-    op.drop_table('consumption_types')
42
+    op.drop_table("consumptions")
43
+    op.drop_table("consumption_types")