"""Basic base model intended to be overridden. Subclasses can override the `fields`, `int_fields`, `float_fields`, and `required_fields` properties to represent a data object. Example: class Fox(BaseModel): fields = { 'color': 'red' } my_fox = Fox() my_fox.set_value('color', 'blue') print(my_fox.get_value('color')) > prints 'blue' """ class DataIntegrityException(Exception): """Raised when accounting data is not what we expected. Represents a state where the input data is not in the format we expect. """ pass class BaseModel(): """Base model representation. Base model supplies common getter/setter/validation scaffolding. The model is concerned solely with the state of the data being added to it. Class Properties: fields (dict): int_fields (list): float_fields (list): required_fields (list): """ fields = {} int_fields = [] float_fields = [] boolean_fields = [] required_fields = [] def __init__(self, **kwargs): """Initialize object fields. Copy the class property `fields` into `self._instance_fields`. Keyword arguments are set as values in _instance_fields Attributes: _instance_fields (dict): """ self._instance_fields = self.fields.copy() for key in kwargs: self.set_value(key, kwargs[key]) def get_value(self, field_name): """Get a value from the _instance_fields dict. Args: field_name (str): Expected key in _instance_fields. Returns: str|None: The return value of _instance_fields.get(field_name). """ return self._instance_fields.get(field_name) def set_value(self, field_name, value): """Set a key's value in _instance_fields. Args: field_name (str): Key name. value (mixed): Value. Raises: DataIntegrityException: Field is not present in model definition. """ if field_name not in self._instance_fields: raise DataIntegrityException( 'Can not set field "{field}" on {model}'.format( field=field_name, model=type(self).__name__)) if field_name in self.int_fields: value = int(value) elif field_name in self.float_fields: value = float(value) elif field_name in self.boolean_fields: value = bool(value) self._instance_fields[field_name] = value def validate(self, errors=None): """Validate model. Basic validation is performed on the required fields for the model. Models that need more validation should override this method. Args: errors (str[], optional): a list of error messages. Raises: DataIntegrityException: The list of error messages accumulated during validation. """ if not errors: errors = [] for field in self.required_fields: if not self._instance_fields[field]: errors.append('"{field}" is not set'.format(field=field)) if errors: raise DataIntegrityException('\n'.join(errors)) def to_tsv(self): """Return fields as a tsv row.""" tsv_representation = '' for key in self._instance_fields.keys(): value = str(self.get_value(key)) if key in self.float_fields: value = '{:.6f}'.format(self.get_value(key)) if key in self.boolean_fields: value = '0' if self.get_value(key): value = '1' tsv_representation = tsv_representation + value + '\t' return tsv_representation[:-1] def to_dict(self): """Return the data dict.""" return self._instance_fields