# -*- coding: utf-8 -*- import yaml import yaml.representer import datetime yaml.Dumper.add_representer(unicode, yaml.representer.SafeRepresenter.represent_unicode) class _YamlUnicodeConstruct(yaml.YAMLObject): @classmethod def from_yaml(cls, loader, node): return cls(**{ k: unicode(v) if isinstance(v, str) else v for (k, v) in loader.construct_mapping(node, deep = True).items() }) class Figure(_YamlUnicodeConstruct): yaml_tag = u"!figure" def __init__(self, value, unit): self.value = value self.unit = unit def get_value(self): return self._value def set_value(self, value): self._value = value """ use property() instead of decorator to enable overriding """ value = property(get_value, set_value) def get_unit(self): return self._unit def set_unit(self, unit): assert type(unit) is unicode self._unit = unit """ use property() instead of decorator to enable overriding """ unit = property(get_unit, set_unit) def __eq__(self, other): return type(self) == type(other) and self.value == other.value and self.unit == other.unit def __ne__(self, other): return not (self == other) @classmethod def to_yaml(cls, dumper, figure): return dumper.represent_mapping( cls.yaml_tag, {'unit': figure.get_unit(), 'value': figure.get_value()}, ) class ScalarFigure(Figure): yaml_tag = u'!scalar' def get_value(self): return super(ScalarFigure, self).get_value() def set_value(self, value): assert type(value) is float super(ScalarFigure, self).set_value(value) """ use property() instead of decorator to enable overriding """ value = property(get_value, set_value) @classmethod def from_yaml(cls, loader, node): attr = loader.construct_scalar(node).split(' ') return cls( value = float(attr[0]), unit = attr[1], ) @classmethod def to_yaml(cls, dumper, s): return dumper.represent_scalar( cls.yaml_tag, '%s %s' % (repr(s.value), s.unit), ) class Distance(ScalarFigure): yaml_tag = u'!distance' @property def metres(self): if self.unit == 'km': return self.value * 1000 else: raise Exception() class Sum(ScalarFigure): yaml_tag = u'!sum' def __init__(self, value, currency): super(Sum, self).__init__(value, currency) @property def currency(self): return self.unit def get_unit(self): return super(Sum, self).get_unit() def set_unit(self, currency): if currency == u'€': currency = u'EUR' assert type(currency) is unicode assert currency in [u'EUR'] super(Sum, self).set_unit(currency) """ use property() instead of decorator to enable overriding """ unit = property(get_unit, set_unit) @classmethod def from_yaml(cls, loader, node): attr = loader.construct_scalar(node).split(' ') return cls( value = float(attr[0]), currency = attr[1], ) class Discount(yaml.YAMLObject): yaml_tag = u'!discount' def __init__( self, name = None, amount = None, ): assert type(name) is unicode self.name = name assert type(amount) is Sum assert amount.value >= 0 self.amount = amount def dict_repr(self): return { 'name': self.name, 'value': self.amount.value, 'value_currency': self.amount.currency, } @staticmethod def from_dict(attr): return Discount( name = attr['name'], amount = Sum(attr['value'], attr['value_currency']), ) def __eq__(self, other): return type(self) == type(other) and self.name == other.name and self.amount == other.amount def __ne__(self, other): return not (self == other) yaml.SafeDumper.add_representer(Discount, lambda dumper, discount: dumper.represent_dict(discount.dict_repr())) class Order(_YamlUnicodeConstruct): yaml_tag = u'!order' def __init__(self, platform, order_id, order_date, customer_id = None, items = None, discounts = None, ): assert type(platform) is unicode self.platform = platform assert type(order_id) is unicode self.order_id = order_id if type(order_date) is datetime.datetime: order_date = order_date.date() assert type(order_date) is datetime.date self.order_date = order_date assert customer_id is None or type(customer_id) is unicode self.customer_id = customer_id if items is None: items = [] assert type(items) is list self.items = items if discounts is None: discounts = [] assert type(discounts) is list self.discounts = discounts def dict_repr(self): return {k: v for (k, v) in { 'articles': self.items, 'customer_id': self.customer_id, 'discounts': self.discounts, 'order_date': self.order_date.strftime('%Y-%m-%d'), 'order_id': self.order_id, 'platform': self.platform, }.items() if v is not None} @staticmethod def from_dict(attr): order = Order( platform = attr['platform'], order_id = attr['order_id'], order_date = datetime.datetime.strptime(attr['order_date'], '%Y-%m-%d'), ) if 'customer_id' in attr: order.customer_id = attr['customer_id'] for item in attr['articles']: if type(item) is dict: item = Item.from_dict(item) assert isinstance(item, Item) order.items.append(item) for discount in attr['discounts']: if type(discount) is dict: discount = Discount.from_dict(discount) assert isinstance(discount, Discount) order.discounts.append(discount) return order def __eq__(self, other): return (type(self) == type(other) and vars(self) == vars(other)) def __ne__(self, other): return not (self == other) yaml.SafeDumper.add_representer(Order, lambda dumper, order: dumper.represent_dict(order.dict_repr())) class Item(_YamlUnicodeConstruct): yaml_tag = u'!item' def __init__( self, name = None, price_brutto = None, ): assert type(name) is unicode self.name = name assert type(price_brutto) is Sum self.price_brutto = price_brutto def dict_repr(self): return { 'name': self.name, 'price_brutto': self.price_brutto.value, 'price_brutto_currency': self.price_brutto.currency, } @staticmethod def from_dict(attr): return Item( name = attr['name'], price_brutto = Sum(attr['price_brutto'], attr['price_brutto_currency']), ) def __eq__(self, other): return (type(self) == type(other) and vars(self) == vars(other)) def __ne__(self, other): return not (self == other) yaml.SafeDumper.add_representer(Item, lambda dumper, item: dumper.represent_dict(item.dict_repr())) class Article(Item): yaml_tag = u'!article' def __init__( self, authors = [], delivery_date = None, quantity = None, reseller = None, shipper = None, state = None, **kwargs ): super(Article, self).__init__(**kwargs) assert type(quantity) is int self.quantity = quantity assert type(authors) is list self.authors = authors assert state is None or type(state) is unicode self.state = state assert reseller is None or type(reseller) is unicode self.reseller = reseller assert shipper is None or type(shipper) is unicode self.shipper = shipper assert delivery_date is None or type(delivery_date) is datetime.date self.delivery_date = delivery_date def dict_repr(self): attr = super(Article, self).dict_repr() attr.update({ 'delivery_date': self.delivery_date, 'quantity': self.quantity, 'reseller': self.reseller, 'shipper': self.shipper, 'state': self.state, }) if len(self.authors) > 0: attr['authors'] = self.authors return attr yaml.SafeDumper.add_representer(Article, lambda dumper, article: dumper.represent_dict(article.dict_repr())) class Transportation(Item): yaml_tag = u'!transportation' def __init__( self, departure_point = None, destination_point = None, distance = None, route_map = None, **kwargs ): super(Transportation, self).__init__(**kwargs) assert type(departure_point) is unicode self.departure_point = departure_point assert type(destination_point) is unicode self.destination_point = destination_point assert distance is None or type(distance) is Distance self.distance = distance assert route_map is None or type(route_map) is str self.route_map = route_map def dict_repr(self): attr = super(Transportation, self).dict_repr() attr.update({ 'departure_point': self.departure_point, 'destination_point': self.destination_point, 'distance_metres': self.distance.metres if self.distance else None, 'route_map': self.route_map, }) return attr yaml.SafeDumper.add_representer(Transportation, lambda dumper, transportation: dumper.represent_dict(transportation.dict_repr())) class TaxiRide(Transportation): yaml_tag = u'!taxi-ride' def __init__( self, arrival_time = None, departure_time = None, driver = None, name = None, **kwargs ): if name is None: name = u'Taxi Ride' super(TaxiRide, self).__init__(name = name, **kwargs) assert type(driver) is unicode self.driver = driver assert arrival_time is None or type(arrival_time) is datetime.datetime self.arrival_time = arrival_time assert departure_time is None or type(departure_time) is datetime.datetime self.departure_time = departure_time def dict_repr(self): attr = super(TaxiRide, self).dict_repr() attr.update({ 'arrival_time': self.arrival_time.strftime('%Y-%m-%d %H:%M') if self.arrival_time else None, 'departure_time': self.departure_time.strftime('%Y-%m-%d %H:%M') if self.departure_time else None, 'driver': self.driver, }) return attr yaml.SafeDumper.add_representer(TaxiRide, lambda dumper, taxi_ride: dumper.represent_dict(taxi_ride.dict_repr()))