Source code for yandex_market_language.models.abstract

from abc import ABC, abstractmethod
from datetime import datetime
from typing import Optional, Union
from xml.etree import ElementTree as ET

from yandex_market_language.exceptions import ValidationError


XMLElement = ET.Element
XMLSubElement = ET.SubElement


[docs]class AbstractModel(ABC): """ Abstract model for creating child models. """
[docs] @abstractmethod def create_dict(self, **kwargs) -> dict: """ Must be inherited by each child class. Describes the logic for creating a dictionary with data from a model. """ raise NotImplementedError
[docs] @abstractmethod def create_xml(self, **kwargs) -> XMLElement: """ Must be inherited by each child class. Describes the logic for creating a xml with data from a model. """ raise NotImplementedError
[docs] @staticmethod def from_xml(el: XMLElement) -> "AbstractModel": """ Must be inherited by each child class. Describes the logic for creating a model from an xml element. """ raise NotImplementedError
[docs] def to_xml(self, root_el: XMLElement = None) -> XMLElement: """ Calls the inherited method to create the element and appends it to the parent, if it was set. """ el = self.create_xml() if root_el is not None: root_el.append(el) return el
[docs] def to_dict(self, clean: bool = False) -> dict: """ Calls the inherited method to create the dictionary and returns a clean dictionary if the parameter was set. """ return self.clean_dict if clean else self.create_dict()
@property def clean_dict(self) -> dict: """ A helper property to get clean dictionary with data. """ return dict(**{k: v for k, v in self.create_dict().items() if v}) @staticmethod def _is_valid_int( value, attr: str, allow_none: bool = False, convert_to_str: bool = True ) -> Optional[Union[int, str]]: """ A helper method for checking if a value is a valid number and returning a value if the check succeeds or raising an error. """ try: value = int(value) return str(value) if convert_to_str else value except (TypeError, ValueError): if value is None and allow_none: return None raise ValidationError("{a} must be a valid int".format(a=attr)) @staticmethod def _is_valid_bool( value, attr: str, allow_none: bool = False ) -> Optional[str]: """ A helper method for checking if a value is a valid bool and returning a value if the check succeeds or raising an error. """ if value in ["true", "false"]: return value elif value is True: return "true" elif value is False: return "false" elif value is None and allow_none: return None else: raise ValidationError( "The {attr} parameter should be boolean. " "Got {t} instead.".format(attr=attr, t=type(value)) ) @staticmethod def _is_valid_float( value, attr: str, allow_none: bool = False, convert_to_str: bool = True ) -> Optional[Union[float, str]]: """ A helper method for checking if a value is a valid float and returning a value if the check succeeds or raising an error. """ try: float(value) return str(value) if convert_to_str else value except (TypeError, ValueError): if value is None and allow_none: return None raise ValidationError("{a} must be a valid float".format(a=attr)) @staticmethod def _str_to_bool(value: str) -> Optional[bool]: """ Returns a boolean converted from a string or None. """ if value == "true": return True elif value == "false": return False else: return None @staticmethod def _is_valid_datetime( dt, dt_format, attr: str, allow_none: bool = False, ) -> Optional[Union[datetime, str]]: """ A helper method for checking if a value is a valid datetime and returning a value if the check succeeds or raising an error. """ if isinstance(dt, datetime): return dt.strftime(dt_format) elif isinstance(dt, str): try: datetime.strptime(dt, dt_format) except ValueError as e: raise ValidationError(e) return dt elif dt is None and allow_none: return None else: raise ValidationError( "{a} must be a valid datetime".format(a=attr) )