|  | @@ -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 |          )
 |