| 
				
			 | 
			
			
				@@ -25,7 +25,7 @@ db = SQLAlchemy(app) 
			 | 
		
	
		
			
			| 
				25
			 | 
			
				25
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				26
			 | 
			
				26
			 | 
			
			
				 # ---------- Models ---------- 
			 | 
		
	
		
			
			| 
				27
			 | 
			
				27
			 | 
			
			
				 class Person(db.Model): 
			 | 
		
	
		
			
			| 
				28
			 | 
			
				
			 | 
			
			
				-    """ Represents a person to be shown on the lists. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				28
			 | 
			
			
				+    """Represents a person to be shown on the lists.""" 
			 | 
		
	
		
			
			| 
				29
			 | 
			
				29
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				30
			 | 
			
				30
			 | 
			
			
				     __tablename__ = "people" 
			 | 
		
	
		
			
			| 
				31
			 | 
			
				31
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -56,7 +56,7 @@ class Person(db.Model): 
			 | 
		
	
		
			
			| 
				56
			 | 
			
				56
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				57
			 | 
			
				57
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				58
			 | 
			
				58
			 | 
			
			
				 class Export(db.Model): 
			 | 
		
	
		
			
			| 
				59
			 | 
			
				
			 | 
			
			
				-    """ Represents a set of exported Settlements. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				59
			 | 
			
			
				+    """Represents a set of exported Settlements.""" 
			 | 
		
	
		
			
			| 
				60
			 | 
			
				60
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				61
			 | 
			
				61
			 | 
			
			
				     __tablename__ = "exports" 
			 | 
		
	
		
			
			| 
				62
			 | 
			
				62
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -77,7 +77,7 @@ class Export(db.Model): 
			 | 
		
	
		
			
			| 
				77
			 | 
			
				77
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				78
			 | 
			
				78
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				79
			 | 
			
				79
			 | 
			
			
				 class Settlement(db.Model): 
			 | 
		
	
		
			
			| 
				80
			 | 
			
				
			 | 
			
			
				-    """ Represents a settlement of the list. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				80
			 | 
			
			
				+    """Represents a settlement of the list.""" 
			 | 
		
	
		
			
			| 
				81
			 | 
			
				81
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				82
			 | 
			
				82
			 | 
			
			
				     __tablename__ = "settlements" 
			 | 
		
	
		
			
			| 
				83
			 | 
			
				83
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -159,7 +159,7 @@ class Settlement(db.Model): 
			 | 
		
	
		
			
			| 
				159
			 | 
			
				159
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				160
			 | 
			
				160
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				161
			 | 
			
				161
			 | 
			
			
				 class ConsumptionType(db.Model): 
			 | 
		
	
		
			
			| 
				162
			 | 
			
				
			 | 
			
			
				-    """ Represents a type of consumption to be counted. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				162
			 | 
			
			
				+    """Represents a type of consumption to be counted.""" 
			 | 
		
	
		
			
			| 
				163
			 | 
			
				163
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				164
			 | 
			
				164
			 | 
			
			
				     __tablename__ = "consumption_types" 
			 | 
		
	
		
			
			| 
				165
			 | 
			
				165
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -183,7 +183,7 @@ class ConsumptionType(db.Model): 
			 | 
		
	
		
			
			| 
				183
			 | 
			
				183
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				184
			 | 
			
				184
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				185
			 | 
			
				185
			 | 
			
			
				 class Consumption(db.Model): 
			 | 
		
	
		
			
			| 
				186
			 | 
			
				
			 | 
			
			
				-    """ Represent one consumption to be counted. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				186
			 | 
			
			
				+    """Represent one consumption to be counted.""" 
			 | 
		
	
		
			
			| 
				187
			 | 
			
				187
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				188
			 | 
			
				188
			 | 
			
			
				     __tablename__ = "consumptions" 
			 | 
		
	
		
			
			| 
				189
			 | 
			
				189
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -222,13 +222,13 @@ class Consumption(db.Model): 
			 | 
		
	
		
			
			| 
				222
			 | 
			
				222
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				223
			 | 
			
				223
			 | 
			
			
				 @app.route("/ping") 
			 | 
		
	
		
			
			| 
				224
			 | 
			
				224
			 | 
			
			
				 def ping() -> None: 
			 | 
		
	
		
			
			| 
				225
			 | 
			
				
			 | 
			
			
				-    """ Return a status ping. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				225
			 | 
			
			
				+    """Return a status ping.""" 
			 | 
		
	
		
			
			| 
				226
			 | 
			
				226
			 | 
			
			
				     return "Pong" 
			 | 
		
	
		
			
			| 
				227
			 | 
			
				227
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				228
			 | 
			
				228
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				229
			 | 
			
				229
			 | 
			
			
				 @app.route("/status") 
			 | 
		
	
		
			
			| 
				230
			 | 
			
				230
			 | 
			
			
				 def status() -> None: 
			 | 
		
	
		
			
			| 
				231
			 | 
			
				
			 | 
			
			
				-    """ Return a status dict with info about the database. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				231
			 | 
			
			
				+    """Return a status dict with info about the database.""" 
			 | 
		
	
		
			
			| 
				232
			 | 
			
				232
			 | 
			
			
				     unsettled_q = Consumption.query.filter_by(settlement=None).filter_by(reversed=False) 
			 | 
		
	
		
			
			| 
				233
			 | 
			
				233
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				234
			 | 
			
				234
			 | 
			
			
				     unsettled = unsettled_q.count() 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -253,7 +253,7 @@ def status() -> None: 
			 | 
		
	
		
			
			| 
				253
			 | 
			
				253
			 | 
			
			
				 # Person 
			 | 
		
	
		
			
			| 
				254
			 | 
			
				254
			 | 
			
			
				 @app.route("/people", methods=["GET"]) 
			 | 
		
	
		
			
			| 
				255
			 | 
			
				255
			 | 
			
			
				 def get_people(): 
			 | 
		
	
		
			
			| 
				256
			 | 
			
				
			 | 
			
			
				-    """ Return a list of currently known people. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				256
			 | 
			
			
				+    """Return a list of currently known people.""" 
			 | 
		
	
		
			
			| 
				257
			 | 
			
				257
			 | 
			
			
				     q = Person.query.order_by(Person.name) 
			 | 
		
	
		
			
			| 
				258
			 | 
			
				258
			 | 
			
			
				     if request.args.get("active"): 
			 | 
		
	
		
			
			| 
				259
			 | 
			
				259
			 | 
			
			
				         active_status = request.args.get("active", type=int) 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -261,7 +261,7 @@ def get_people(): 
			 | 
		
	
		
			
			| 
				261
			 | 
			
				261
			 | 
			
			
				     people = q.all() 
			 | 
		
	
		
			
			| 
				262
			 | 
			
				262
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				263
			 | 
			
				263
			 | 
			
			
				     engine = db.get_engine() 
			 | 
		
	
		
			
			| 
				264
			 | 
			
				
			 | 
			
			
				-    query = ''' 
			 | 
		
	
		
			
			| 
				
			 | 
			
				264
			 | 
			
			
				+    query = """ 
			 | 
		
	
		
			
			| 
				265
			 | 
			
				265
			 | 
			
			
				         SELECT 
			 | 
		
	
		
			
			| 
				266
			 | 
			
				266
			 | 
			
			
				             consumptions.person_id, 
			 | 
		
	
		
			
			| 
				267
			 | 
			
				267
			 | 
			
			
				             consumptions.consumption_type_id, 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -273,10 +273,10 @@ def get_people(): 
			 | 
		
	
		
			
			| 
				273
			 | 
			
				273
			 | 
			
			
				             consumptions.settlement_id IS NULL 
			 | 
		
	
		
			
			| 
				274
			 | 
			
				274
			 | 
			
			
				             AND consumptions.reversed = 0 
			 | 
		
	
		
			
			| 
				275
			 | 
			
				275
			 | 
			
			
				         GROUP BY consumptions.person_id, consumptions.consumption_type_id; 
			 | 
		
	
		
			
			| 
				276
			 | 
			
				
			 | 
			
			
				-    ''' 
			 | 
		
	
		
			
			| 
				
			 | 
			
				276
			 | 
			
			
				+    """ 
			 | 
		
	
		
			
			| 
				277
			 | 
			
				277
			 | 
			
			
				     raw_counts = engine.execute(query) 
			 | 
		
	
		
			
			| 
				278
			 | 
			
				278
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				279
			 | 
			
				
			 | 
			
			
				-    counts: 'Dict[int, Dict[str, int]]' = defaultdict(dict) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				279
			 | 
			
			
				+    counts: "Dict[int, Dict[str, int]]" = defaultdict(dict) 
			 | 
		
	
		
			
			| 
				280
			 | 
			
				280
			 | 
			
			
				     for person_id, consumption_type_id, count in raw_counts: 
			 | 
		
	
		
			
			| 
				281
			 | 
			
				281
			 | 
			
			
				         counts[person_id][str(consumption_type_id)] = count 
			 | 
		
	
		
			
			| 
				282
			 | 
			
				282
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -285,7 +285,7 @@ def get_people(): 
			 | 
		
	
		
			
			| 
				285
			 | 
			
				285
			 | 
			
			
				             "name": person.name, 
			 | 
		
	
		
			
			| 
				286
			 | 
			
				286
			 | 
			
			
				             "active": person.active, 
			 | 
		
	
		
			
			| 
				287
			 | 
			
				287
			 | 
			
			
				             "person_id": person.person_id, 
			 | 
		
	
		
			
			| 
				288
			 | 
			
				
			 | 
			
			
				-            "consumptions": counts[person.person_id] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				288
			 | 
			
			
				+            "consumptions": counts[person.person_id], 
			 | 
		
	
		
			
			| 
				289
			 | 
			
				289
			 | 
			
			
				         } 
			 | 
		
	
		
			
			| 
				290
			 | 
			
				290
			 | 
			
			
				         for person in people 
			 | 
		
	
		
			
			| 
				291
			 | 
			
				291
			 | 
			
			
				     ] 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -381,7 +381,7 @@ def add_consumption2(person_id: int, ct_id: int): 
			 | 
		
	
		
			
			| 
				381
			 | 
			
				381
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				382
			 | 
			
				382
			 | 
			
			
				 @app.route("/consumptions/<int:consumption_id>", methods=["DELETE"]) 
			 | 
		
	
		
			
			| 
				383
			 | 
			
				383
			 | 
			
			
				 def reverse_consumption(consumption_id: int): 
			 | 
		
	
		
			
			| 
				384
			 | 
			
				
			 | 
			
			
				-    """ Reverse a consumption. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				384
			 | 
			
			
				+    """Reverse a consumption.""" 
			 | 
		
	
		
			
			| 
				385
			 | 
			
				385
			 | 
			
			
				     consumption = Consumption.query.get_or_404(consumption_id) 
			 | 
		
	
		
			
			| 
				386
			 | 
			
				386
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				387
			 | 
			
				387
			 | 
			
			
				     if consumption.reversed: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -409,7 +409,7 @@ def reverse_consumption(consumption_id: int): 
			 | 
		
	
		
			
			| 
				409
			 | 
			
				409
			 | 
			
			
				 # ConsumptionType 
			 | 
		
	
		
			
			| 
				410
			 | 
			
				410
			 | 
			
			
				 @app.route("/consumption_types", methods=["GET"]) 
			 | 
		
	
		
			
			| 
				411
			 | 
			
				411
			 | 
			
			
				 def get_consumption_types(): 
			 | 
		
	
		
			
			| 
				412
			 | 
			
				
			 | 
			
			
				-    """ Return a list of currently active consumption types. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				412
			 | 
			
			
				+    """Return a list of currently active consumption types.""" 
			 | 
		
	
		
			
			| 
				413
			 | 
			
				413
			 | 
			
			
				     ctypes = ConsumptionType.query.filter_by(active=True).all() 
			 | 
		
	
		
			
			| 
				414
			 | 
			
				414
			 | 
			
			
				     result = [ct.as_dict for ct in ctypes] 
			 | 
		
	
		
			
			| 
				415
			 | 
			
				415
			 | 
			
			
				     return jsonify(consumption_types=result) 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -424,7 +424,7 @@ def get_consumption_type(consumption_type_id: int): 
			 | 
		
	
		
			
			| 
				424
			 | 
			
				424
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				425
			 | 
			
				425
			 | 
			
			
				 @app.route("/consumption_types", methods=["POST"]) 
			 | 
		
	
		
			
			| 
				426
			 | 
			
				426
			 | 
			
			
				 def add_consumption_type(): 
			 | 
		
	
		
			
			| 
				427
			 | 
			
				
			 | 
			
			
				-    """ Add a new ConsumptionType.  """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				427
			 | 
			
			
				+    """Add a new ConsumptionType.""" 
			 | 
		
	
		
			
			| 
				428
			 | 
			
				428
			 | 
			
			
				     json = request.get_json() 
			 | 
		
	
		
			
			| 
				429
			 | 
			
				429
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				430
			 | 
			
				430
			 | 
			
			
				     if not json: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -445,14 +445,14 @@ def add_consumption_type(): 
			 | 
		
	
		
			
			| 
				445
			 | 
			
				445
			 | 
			
			
				 # Settlement 
			 | 
		
	
		
			
			| 
				446
			 | 
			
				446
			 | 
			
			
				 @app.route("/settlements", methods=["GET"]) 
			 | 
		
	
		
			
			| 
				447
			 | 
			
				447
			 | 
			
			
				 def get_settlements(): 
			 | 
		
	
		
			
			| 
				448
			 | 
			
				
			 | 
			
			
				-    """ Return a list of the active Settlements. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				448
			 | 
			
			
				+    """Return a list of the active Settlements.""" 
			 | 
		
	
		
			
			| 
				449
			 | 
			
				449
			 | 
			
			
				     result = Settlement.query.all() 
			 | 
		
	
		
			
			| 
				450
			 | 
			
				450
			 | 
			
			
				     return jsonify(settlements=[s.as_dict for s in result]) 
			 | 
		
	
		
			
			| 
				451
			 | 
			
				451
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				452
			 | 
			
				452
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				453
			 | 
			
				453
			 | 
			
			
				 @app.route("/settlements/<int:settlement_id>", methods=["GET"]) 
			 | 
		
	
		
			
			| 
				454
			 | 
			
				454
			 | 
			
			
				 def get_settlement(settlement_id: int): 
			 | 
		
	
		
			
			| 
				455
			 | 
			
				
			 | 
			
			
				-    """ Show full details for a single Settlement. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				455
			 | 
			
			
				+    """Show full details for a single Settlement.""" 
			 | 
		
	
		
			
			| 
				456
			 | 
			
				456
			 | 
			
			
				     s = Settlement.query.get_or_404(settlement_id) 
			 | 
		
	
		
			
			| 
				457
			 | 
			
				457
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				458
			 | 
			
				458
			 | 
			
			
				     per_person = s.per_person 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -462,7 +462,7 @@ def get_settlement(settlement_id: int): 
			 | 
		
	
		
			
			| 
				462
			 | 
			
				462
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				463
			 | 
			
				463
			 | 
			
			
				 @app.route("/settlements", methods=["POST"]) 
			 | 
		
	
		
			
			| 
				464
			 | 
			
				464
			 | 
			
			
				 def add_settlement(): 
			 | 
		
	
		
			
			| 
				465
			 | 
			
				
			 | 
			
			
				-    """ Create a Settlement, and link all un-settled Consumptions to it. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				465
			 | 
			
			
				+    """Create a Settlement, and link all un-settled Consumptions to it.""" 
			 | 
		
	
		
			
			| 
				466
			 | 
			
				466
			 | 
			
			
				     json = request.get_json() 
			 | 
		
	
		
			
			| 
				467
			 | 
			
				467
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				468
			 | 
			
				468
			 | 
			
			
				     if not json: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -486,14 +486,14 @@ def add_settlement(): 
			 | 
		
	
		
			
			| 
				486
			 | 
			
				486
			 | 
			
			
				 # Export 
			 | 
		
	
		
			
			| 
				487
			 | 
			
				487
			 | 
			
			
				 @app.route("/exports", methods=["GET"]) 
			 | 
		
	
		
			
			| 
				488
			 | 
			
				488
			 | 
			
			
				 def get_exports(): 
			 | 
		
	
		
			
			| 
				489
			 | 
			
				
			 | 
			
			
				-    """ Return a list of the created Exports. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				489
			 | 
			
			
				+    """Return a list of the created Exports.""" 
			 | 
		
	
		
			
			| 
				490
			 | 
			
				490
			 | 
			
			
				     result = Export.query.all() 
			 | 
		
	
		
			
			| 
				491
			 | 
			
				491
			 | 
			
			
				     return jsonify(exports=[e.as_dict for e in result]) 
			 | 
		
	
		
			
			| 
				492
			 | 
			
				492
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				493
			 | 
			
				493
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				494
			 | 
			
				494
			 | 
			
			
				 @app.route("/exports/<int:export_id>", methods=["GET"]) 
			 | 
		
	
		
			
			| 
				495
			 | 
			
				495
			 | 
			
			
				 def get_export(export_id: int): 
			 | 
		
	
		
			
			| 
				496
			 | 
			
				
			 | 
			
			
				-    """ Return an overview for the given Export. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				496
			 | 
			
			
				+    """Return an overview for the given Export.""" 
			 | 
		
	
		
			
			| 
				497
			 | 
			
				497
			 | 
			
			
				     e = Export.query.get_or_404(export_id) 
			 | 
		
	
		
			
			| 
				498
			 | 
			
				498
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				499
			 | 
			
				499
			 | 
			
			
				     ss = [s.as_dict for s in e.settlements] 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -503,7 +503,7 @@ def get_export(export_id: int): 
			 | 
		
	
		
			
			| 
				503
			 | 
			
				503
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				504
			 | 
			
				504
			 | 
			
			
				 @app.route("/exports", methods=["POST"]) 
			 | 
		
	
		
			
			| 
				505
			 | 
			
				505
			 | 
			
			
				 def add_export(): 
			 | 
		
	
		
			
			| 
				506
			 | 
			
				
			 | 
			
			
				-    """ Create an Export, and link all un-exported Settlements to it. """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				506
			 | 
			
			
				+    """Create an Export, and link all un-exported Settlements to it.""" 
			 | 
		
	
		
			
			| 
				507
			 | 
			
				507
			 | 
			
			
				     # Assert that there are Settlements to be exported. 
			 | 
		
	
		
			
			| 
				508
			 | 
			
				508
			 | 
			
			
				     s_count = Settlement.query.filter_by(export=None).count() 
			 | 
		
	
		
			
			| 
				509
			 | 
			
				509
			 | 
			
			
				     if s_count == 0: 
			 |