| 
				
			 | 
			
			
				@@ -1,14 +1,15 @@ 
			 | 
		
	
		
			
			| 
				1
			 | 
			
				1
			 | 
			
			
				 """ 
			 | 
		
	
		
			
			| 
				2
			 | 
			
				2
			 | 
			
			
				 Provides access to the models stored in the database, via the server. 
			 | 
		
	
		
			
			| 
				3
			 | 
			
				3
			 | 
			
			
				 """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				4
			 | 
			
			
				+from __future__ import annotations 
			 | 
		
	
		
			
			| 
				
			 | 
			
				5
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				4
			 | 
			
				6
			 | 
			
			
				 import datetime 
			 | 
		
	
		
			
			| 
				5
			 | 
			
				7
			 | 
			
			
				 import logging 
			 | 
		
	
		
			
			| 
				6
			 | 
			
				
			 | 
			
			
				-from typing import NamedTuple, Sequence, Tuple, Any, Optional 
			 | 
		
	
		
			
			| 
				
			 | 
			
				8
			 | 
			
			
				+from typing import Any, List, NamedTuple, Optional, Sequence, Tuple 
			 | 
		
	
		
			
			| 
				7
			 | 
			
				9
			 | 
			
			
				 from urllib.parse import urljoin 
			 | 
		
	
		
			
			| 
				8
			 | 
			
				10
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				9
			 | 
			
				11
			 | 
			
			
				 import requests 
			 | 
		
	
		
			
			| 
				10
			 | 
			
				12
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				11
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				12
			 | 
			
				13
			 | 
			
			
				 LOG = logging.getLogger(__name__) 
			 | 
		
	
		
			
			| 
				13
			 | 
			
				14
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				14
			 | 
			
				15
			 | 
			
			
				 SERVER_URL = "http://127.0.0.1:5000" 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -60,7 +61,7 @@ class Person(NamedTuple): 
			 | 
		
	
		
			
			| 
				60
			 | 
			
				61
			 | 
			
			
				     def name(self) -> str: 
			 | 
		
	
		
			
			| 
				61
			 | 
			
				62
			 | 
			
			
				         return self.display_name or self.full_name 
			 | 
		
	
		
			
			| 
				62
			 | 
			
				63
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				63
			 | 
			
				
			 | 
			
			
				-    def add_consumption(self, type_id: str) -> bool: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				64
			 | 
			
			
				+    def add_consumption(self, type_id: str) -> Optional[Consumption]: 
			 | 
		
	
		
			
			| 
				64
			 | 
			
				65
			 | 
			
			
				         """ Register a consumption for this Person. """ 
			 | 
		
	
		
			
			| 
				65
			 | 
			
				66
			 | 
			
			
				         req = requests.post( 
			 | 
		
	
		
			
			| 
				66
			 | 
			
				67
			 | 
			
			
				             urljoin(SERVER_URL, f"people/{self.person_id}/add_consumption/{type_id}") 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -75,7 +76,7 @@ class Person(NamedTuple): 
			 | 
		
	
		
			
			| 
				75
			 | 
			
				76
			 | 
			
			
				                     req.status_code, 
			 | 
		
	
		
			
			| 
				76
			 | 
			
				77
			 | 
			
			
				                     data, 
			 | 
		
	
		
			
			| 
				77
			 | 
			
				78
			 | 
			
			
				                 ) 
			 | 
		
	
		
			
			| 
				78
			 | 
			
				
			 | 
			
			
				-                return False 
			 | 
		
	
		
			
			| 
				
			 | 
			
				79
			 | 
			
			
				+                return None 
			 | 
		
	
		
			
			| 
				79
			 | 
			
				80
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				80
			 | 
			
				81
			 | 
			
			
				             self.consumptions.update(data["person"]["consumptions"]) 
			 | 
		
	
		
			
			| 
				81
			 | 
			
				82
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -86,9 +87,9 @@ class Person(NamedTuple): 
			 | 
		
	
		
			
			| 
				86
			 | 
			
				87
			 | 
			
			
				                 req.status_code, 
			 | 
		
	
		
			
			| 
				87
			 | 
			
				88
			 | 
			
			
				                 req.content, 
			 | 
		
	
		
			
			| 
				88
			 | 
			
				89
			 | 
			
			
				             ) 
			 | 
		
	
		
			
			| 
				89
			 | 
			
				
			 | 
			
			
				-            return False 
			 | 
		
	
		
			
			| 
				
			 | 
			
				90
			 | 
			
			
				+            return None 
			 | 
		
	
		
			
			| 
				90
			 | 
			
				91
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				91
			 | 
			
				
			 | 
			
			
				-    def create(self) -> "Person": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				92
			 | 
			
			
				+    def create(self) -> Optional[Person]: 
			 | 
		
	
		
			
			| 
				92
			 | 
			
				93
			 | 
			
			
				         """ Create a new Person from the current attributes. As tuples are 
			 | 
		
	
		
			
			| 
				93
			 | 
			
				94
			 | 
			
			
				         immutable, a new Person with the correct id is returned. """ 
			 | 
		
	
		
			
			| 
				94
			 | 
			
				95
			 | 
			
			
				         req = requests.post( 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -112,7 +113,7 @@ class Person(NamedTuple): 
			 | 
		
	
		
			
			| 
				112
			 | 
			
				113
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				113
			 | 
			
				114
			 | 
			
			
				         return Person.from_dict(data["person"]) 
			 | 
		
	
		
			
			| 
				114
			 | 
			
				115
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				115
			 | 
			
				
			 | 
			
			
				-    def set_active(self, new_state=True) -> "Person": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				116
			 | 
			
			
				+    def set_active(self, new_state=True) -> Optional[Person]: 
			 | 
		
	
		
			
			| 
				116
			 | 
			
				117
			 | 
			
			
				         req = requests.patch( 
			 | 
		
	
		
			
			| 
				117
			 | 
			
				118
			 | 
			
			
				             urljoin(SERVER_URL, f"people/{self.person_id}"), 
			 | 
		
	
		
			
			| 
				118
			 | 
			
				119
			 | 
			
			
				             json={"person": {"active": new_state}}, 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -135,7 +136,7 @@ class Person(NamedTuple): 
			 | 
		
	
		
			
			| 
				135
			 | 
			
				136
			 | 
			
			
				         return Person.from_dict(data["person"]) 
			 | 
		
	
		
			
			| 
				136
			 | 
			
				137
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				137
			 | 
			
				138
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				138
			 | 
			
				
			 | 
			
			
				-    def get(cls, person_id: int) -> "Person": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				139
			 | 
			
			
				+    def get(cls, person_id: int) -> Optional[Person]: 
			 | 
		
	
		
			
			| 
				139
			 | 
			
				140
			 | 
			
			
				         """ Retrieve a Person by id. """ 
			 | 
		
	
		
			
			| 
				140
			 | 
			
				141
			 | 
			
			
				         req = requests.get(urljoin(SERVER_URL, f"/people/{person_id}")) 
			 | 
		
	
		
			
			| 
				141
			 | 
			
				142
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -159,7 +160,7 @@ class Person(NamedTuple): 
			 | 
		
	
		
			
			| 
				159
			 | 
			
				160
			 | 
			
			
				             return None 
			 | 
		
	
		
			
			| 
				160
			 | 
			
				161
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				161
			 | 
			
				162
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				162
			 | 
			
				
			 | 
			
			
				-    def get_all(cls, active=None) -> ["Person"]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				163
			 | 
			
			
				+    def get_all(cls, active=None) -> Optional[List[Person]]: 
			 | 
		
	
		
			
			| 
				163
			 | 
			
				164
			 | 
			
			
				         """ Get all active People. """ 
			 | 
		
	
		
			
			| 
				164
			 | 
			
				165
			 | 
			
			
				         params = {} 
			 | 
		
	
		
			
			| 
				165
			 | 
			
				166
			 | 
			
			
				         if active is not None: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -212,7 +213,7 @@ class Export(NamedTuple): 
			 | 
		
	
		
			
			| 
				212
			 | 
			
				213
			 | 
			
			
				         ) 
			 | 
		
	
		
			
			| 
				213
			 | 
			
				214
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				214
			 | 
			
				215
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				215
			 | 
			
				
			 | 
			
			
				-    def get_all(cls) -> ["Export"]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				216
			 | 
			
			
				+    def get_all(cls) -> Optional[List[Export]]: 
			 | 
		
	
		
			
			| 
				216
			 | 
			
				217
			 | 
			
			
				         """ Get a list of all existing Exports. """ 
			 | 
		
	
		
			
			| 
				217
			 | 
			
				218
			 | 
			
			
				         req = requests.get(urljoin(SERVER_URL, "exports")) 
			 | 
		
	
		
			
			| 
				218
			 | 
			
				219
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -233,7 +234,7 @@ class Export(NamedTuple): 
			 | 
		
	
		
			
			| 
				233
			 | 
			
				234
			 | 
			
			
				         return [cls.from_dict(e) for e in data["exports"]] 
			 | 
		
	
		
			
			| 
				234
			 | 
			
				235
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				235
			 | 
			
				236
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				236
			 | 
			
				
			 | 
			
			
				-    def get(cls, export_id: int) -> "Export": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				237
			 | 
			
			
				+    def get(cls, export_id: int) -> Optional[Export]: 
			 | 
		
	
		
			
			| 
				237
			 | 
			
				238
			 | 
			
			
				         """ Retrieve one Export. """ 
			 | 
		
	
		
			
			| 
				238
			 | 
			
				239
			 | 
			
			
				         req = requests.get(urljoin(SERVER_URL, f"exports/{export_id}")) 
			 | 
		
	
		
			
			| 
				239
			 | 
			
				240
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -256,7 +257,7 @@ class Export(NamedTuple): 
			 | 
		
	
		
			
			| 
				256
			 | 
			
				257
			 | 
			
			
				         return cls.from_dict(data["export"]) 
			 | 
		
	
		
			
			| 
				257
			 | 
			
				258
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				258
			 | 
			
				259
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				259
			 | 
			
				
			 | 
			
			
				-    def create(cls) -> "Export": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				260
			 | 
			
			
				+    def create(cls) -> Optional[Export]: 
			 | 
		
	
		
			
			| 
				260
			 | 
			
				261
			 | 
			
			
				         """ Create a new Export, containing all un-exported Settlements. """ 
			 | 
		
	
		
			
			| 
				261
			 | 
			
				262
			 | 
			
			
				         req = requests.post(urljoin(SERVER_URL, "exports")) 
			 | 
		
	
		
			
			| 
				262
			 | 
			
				263
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -283,10 +284,10 @@ class ConsumptionType(NamedTuple): 
			 | 
		
	
		
			
			| 
				283
			 | 
			
				284
			 | 
			
			
				     """ Represents a stored ConsumptionType. """ 
			 | 
		
	
		
			
			| 
				284
			 | 
			
				285
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				285
			 | 
			
				286
			 | 
			
			
				     name: str 
			 | 
		
	
		
			
			| 
				286
			 | 
			
				
			 | 
			
			
				-    consumption_type_id: int = None 
			 | 
		
	
		
			
			| 
				287
			 | 
			
				
			 | 
			
			
				-    icon: str = None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				287
			 | 
			
			
				+    consumption_type_id: Optional[int] = None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				288
			 | 
			
			
				+    icon: Optional[str] = None 
			 | 
		
	
		
			
			| 
				288
			 | 
			
				289
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				289
			 | 
			
				
			 | 
			
			
				-    def create(self) -> "ConsumptionType": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				290
			 | 
			
			
				+    def create(self) -> Optional[ConsumptionType]: 
			 | 
		
	
		
			
			| 
				290
			 | 
			
				291
			 | 
			
			
				         """ Create a new ConsumptionType from the current attributes. As tuples 
			 | 
		
	
		
			
			| 
				291
			 | 
			
				292
			 | 
			
			
				         are immutable, a new ConsumptionType with the correct id is returned. 
			 | 
		
	
		
			
			| 
				292
			 | 
			
				293
			 | 
			
			
				         """ 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -314,7 +315,7 @@ class ConsumptionType(NamedTuple): 
			 | 
		
	
		
			
			| 
				314
			 | 
			
				315
			 | 
			
			
				         return ConsumptionType.from_dict(data["consumption_type"]) 
			 | 
		
	
		
			
			| 
				315
			 | 
			
				316
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				316
			 | 
			
				317
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				317
			 | 
			
				
			 | 
			
			
				-    def get(cls, consumption_type_id: int) -> "ConsumptionType": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				318
			 | 
			
			
				+    def get(cls, consumption_type_id: int) -> Optional[ConsumptionType]: 
			 | 
		
	
		
			
			| 
				318
			 | 
			
				319
			 | 
			
			
				         """ Retrieve a ConsumptionType by id. """ 
			 | 
		
	
		
			
			| 
				319
			 | 
			
				320
			 | 
			
			
				         req = requests.get( 
			 | 
		
	
		
			
			| 
				320
			 | 
			
				321
			 | 
			
			
				             urljoin(SERVER_URL, f"/consumption_types/{consumption_type_id}") 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -343,7 +344,7 @@ class ConsumptionType(NamedTuple): 
			 | 
		
	
		
			
			| 
				343
			 | 
			
				344
			 | 
			
			
				             return None 
			 | 
		
	
		
			
			| 
				344
			 | 
			
				345
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				345
			 | 
			
				346
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				346
			 | 
			
				
			 | 
			
			
				-    def get_all(cls) -> ["ConsumptionType"]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				347
			 | 
			
			
				+    def get_all(cls) -> Optional[List[ConsumptionType]]: 
			 | 
		
	
		
			
			| 
				347
			 | 
			
				348
			 | 
			
			
				         """ Get all active ConsumptionTypes. """ 
			 | 
		
	
		
			
			| 
				348
			 | 
			
				349
			 | 
			
			
				         req = requests.get(urljoin(SERVER_URL, "/consumption_types")) 
			 | 
		
	
		
			
			| 
				349
			 | 
			
				350
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -383,7 +384,7 @@ class Consumption(NamedTuple): 
			 | 
		
	
		
			
			| 
				383
			 | 
			
				384
			 | 
			
			
				     consumption_type_id: int 
			 | 
		
	
		
			
			| 
				384
			 | 
			
				385
			 | 
			
			
				     created_at: datetime.datetime 
			 | 
		
	
		
			
			| 
				385
			 | 
			
				386
			 | 
			
			
				     reversed: bool = False 
			 | 
		
	
		
			
			| 
				386
			 | 
			
				
			 | 
			
			
				-    settlement_id: int = None 
			 | 
		
	
		
			
			| 
				
			 | 
			
				387
			 | 
			
			
				+    settlement_id: Optional[int] = None 
			 | 
		
	
		
			
			| 
				387
			 | 
			
				388
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				388
			 | 
			
				389
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				389
			 | 
			
				390
			 | 
			
			
				     def from_dict(cls, data: dict) -> "Consumption": 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -397,7 +398,7 @@ class Consumption(NamedTuple): 
			 | 
		
	
		
			
			| 
				397
			 | 
			
				398
			 | 
			
			
				             reversed=data["reversed"], 
			 | 
		
	
		
			
			| 
				398
			 | 
			
				399
			 | 
			
			
				         ) 
			 | 
		
	
		
			
			| 
				399
			 | 
			
				400
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				400
			 | 
			
				
			 | 
			
			
				-    def reverse(self) -> "Consumption": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				401
			 | 
			
			
				+    def reverse(self) -> Optional[Consumption]: 
			 | 
		
	
		
			
			| 
				401
			 | 
			
				402
			 | 
			
			
				         """ Reverse this consumption. """ 
			 | 
		
	
		
			
			| 
				402
			 | 
			
				403
			 | 
			
			
				         req = requests.delete( 
			 | 
		
	
		
			
			| 
				403
			 | 
			
				404
			 | 
			
			
				             urljoin(SERVER_URL, f"/consumptions/{self.consumption_id}") 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -413,7 +414,7 @@ class Consumption(NamedTuple): 
			 | 
		
	
		
			
			| 
				413
			 | 
			
				414
			 | 
			
			
				                     req.status_code, 
			 | 
		
	
		
			
			| 
				414
			 | 
			
				415
			 | 
			
			
				                     data, 
			 | 
		
	
		
			
			| 
				415
			 | 
			
				416
			 | 
			
			
				                 ) 
			 | 
		
	
		
			
			| 
				416
			 | 
			
				
			 | 
			
			
				-                return False 
			 | 
		
	
		
			
			| 
				
			 | 
			
				417
			 | 
			
			
				+                return None 
			 | 
		
	
		
			
			| 
				417
			 | 
			
				418
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				418
			 | 
			
				419
			 | 
			
			
				             return Consumption.from_dict(data["consumption"]) 
			 | 
		
	
		
			
			| 
				419
			 | 
			
				420
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -423,7 +424,7 @@ class Consumption(NamedTuple): 
			 | 
		
	
		
			
			| 
				423
			 | 
			
				424
			 | 
			
			
				                 req.status_code, 
			 | 
		
	
		
			
			| 
				424
			 | 
			
				425
			 | 
			
			
				                 req.content, 
			 | 
		
	
		
			
			| 
				425
			 | 
			
				426
			 | 
			
			
				             ) 
			 | 
		
	
		
			
			| 
				426
			 | 
			
				
			 | 
			
			
				-            return False 
			 | 
		
	
		
			
			| 
				
			 | 
			
				427
			 | 
			
			
				+            return None 
			 | 
		
	
		
			
			| 
				427
			 | 
			
				428
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				428
			 | 
			
				429
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				429
			 | 
			
				430
			 | 
			
			
				 class Settlement(NamedTuple): 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -452,7 +453,7 @@ class Settlement(NamedTuple): 
			 | 
		
	
		
			
			| 
				452
			 | 
			
				453
			 | 
			
			
				         return cls.from_dict(req.json()["settlement"]) 
			 | 
		
	
		
			
			| 
				453
			 | 
			
				454
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				454
			 | 
			
				455
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				455
			 | 
			
				
			 | 
			
			
				-    def get(cls, settlement_id: int) -> "Settlement": 
			 | 
		
	
		
			
			| 
				
			 | 
			
				456
			 | 
			
			
				+    def get(cls, settlement_id: int) -> Optional[Settlement]: 
			 | 
		
	
		
			
			| 
				456
			 | 
			
				457
			 | 
			
			
				         req = requests.get(urljoin(SERVER_URL, f"/settlements/{settlement_id}")) 
			 | 
		
	
		
			
			| 
				457
			 | 
			
				458
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				458
			 | 
			
				459
			 | 
			
			
				         try: 
			 |