"""Data structures.""" import operator from availability.constants import models class ReleaseStatus: """Data structure to hold release status information. Should be used to pass reasonably valid data between application layers. """ # Specifying slots and using getters/setters for all fields access # helps to avoid the problem with attribute names spelling errors. __slots__ = ( '_store_release_id', '_store_release_status', '_db_release_status', '_live_countries') def __init__( self, *, store_release_id='', store_release_status='', db_release_status='', live_countries=None): """Initialise release status. All arguments are optional and can be accessed as instance attributes under the same name. Empty arguments that expect strings, are converted to None if not provided or are empty. Args: store_release_id (str): Store-specific release id. Could be an int in some stores, but an arbitrary string in others, so we keep it as string here and left the conversion to the persistence layer. store_release_status (str): Store-specific release status. db_release_status (str): Internal release status to keep in DB. live_countries (list): An iterable of country codes where the release is live. Country codes are not validated. """ # Note: the attributes below are always assigned through setters. self.store_release_id = store_release_id self.store_release_status = store_release_status self.db_release_status = db_release_status self.live_countries = live_countries store_release_id = property(operator.attrgetter('_store_release_id')) live_countries = property(operator.attrgetter('_live_countries')) db_release_status = property(operator.attrgetter('_db_release_status')) store_release_status = property(operator.attrgetter( '_store_release_status')) def __eq__(self, other): """Equality test function.""" return self.as_dict() == other.as_dict() def __repr__(self): """Representation function.""" items = ('{:s}={!r}'.format(k, v) for k, v in self.as_dict().items()) return '{:s}({:s})'.format(self.__class__.__name__, ', '.join(items)) @live_countries.setter def live_countries(self, value): """Setter: sort live_countries and assign to private attr.""" if not value: self._live_countries = [] else: # We keep live_countries attr sorted for easier status comparison. self._live_countries = list(sorted(value)) @db_release_status.setter def db_release_status(self, value): """Setter: validate db_release_status and assign to private attr.""" if not value: self._db_release_status = None return self._db_release_status = str(value) if self._db_release_status not in models.RELEASE_STATUSES_ENUM: raise ValueError('Unknown db_release_status value: "{}"'.format( self._db_release_status)) @store_release_status.setter def store_release_status(self, value): """Setter: validate store_release_status and assign to private attr.""" if not value: self._store_release_status = None else: self._store_release_status = str(value) @store_release_id.setter def store_release_id(self, value): """Setter: validate store_release_id and assign to private attr.""" if not value: self._store_release_id = None else: self._store_release_id = str(value) def as_dict(self): """Get status data as a dictionary. Returns: dict: A dictionary with keys matching __init__() parameters. """ public_attrs = (slot.lstrip('_') for slot in self.__slots__) return {attr: getattr(self, attr) for attr in public_attrs}