Browse Source

split finoex 0.1 from dingguo

Fabian Peter Hammerle 7 years ago
parent
commit
cdcd8caf48
48 changed files with 786 additions and 2981 deletions
  1. 0 548
      dingguo/__init__.py
  2. 0 53
      dingguo/parser/__init__.py
  3. 0 114
      dingguo/parser/amazon.py
  4. 0 88
      dingguo/parser/amazon_cn.py
  5. 0 76
      dingguo/parser/banggood.py
  6. 0 57
      dingguo/parser/hm.py
  7. 0 71
      dingguo/parser/ikea.py
  8. 0 75
      dingguo/parser/indiegogo.py
  9. 0 43
      dingguo/parser/ingdiba.py
  10. 0 71
      dingguo/parser/kickstarter.py
  11. 0 85
      dingguo/parser/lieferservice.py
  12. 0 79
      dingguo/parser/mytaxi.py
  13. 0 61
      dingguo/parser/namecheap.py
  14. 0 72
      dingguo/parser/oebb.py
  15. 0 46
      dingguo/parser/thomann.py
  16. 0 76
      dingguo/parser/uber.py
  17. 0 92
      dingguo/parser/wienerlinien.py
  18. 0 78
      dingguo/parser/yipbee.py
  19. 451 0
      finoex/__init__.py
  20. 0 33
      scripts/ingdiba-pdf-to-text
  21. 0 71
      scripts/order-confirmation-mail-parser
  22. 7 11
      setup.py
  23. 0 24
      tests/parser/test_amazon.py
  24. 0 24
      tests/parser/test_amazon_cn.py
  25. 0 24
      tests/parser/test_banggood.py
  26. 0 24
      tests/parser/test_indiegogo.py
  27. 0 132
      tests/test_.py
  28. 109 89
      tests/test_comparison.py
  29. 16 7
      tests/test_distance.py
  30. 0 27
      tests/test_figure.py
  31. 0 40
      tests/test_parser.py
  32. 0 24
      tests/test_parser_hm.py
  33. 0 24
      tests/test_parser_ikea.py
  34. 0 52
      tests/test_parser_ingdiba.py
  35. 0 24
      tests/test_parser_kickstarter.py
  36. 0 24
      tests/test_parser_lieferservice.py
  37. 0 24
      tests/test_parser_mytaxi.py
  38. 0 24
      tests/test_parser_namecheap.py
  39. 0 24
      tests/test_parser_oebb.py
  40. 0 24
      tests/test_parser_thomann.py
  41. 0 24
      tests/test_parser_uber.py
  42. 0 24
      tests/test_parser_wienerlinien.py
  43. 0 24
      tests/test_parser_yipbee.py
  44. 0 52
      tests/test_scalar_figure.py
  45. 0 137
      tests/test_script_order_confirmation_mail_parser.py
  46. 66 19
      tests/test_sum.py
  47. 24 0
      tests/test_sum_yaml.py
  48. 113 166
      tests/test_yaml.py

+ 0 - 548
dingguo/__init__.py

@@ -1,548 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import copy
-import datetime
-import ioex.datetimeex
-import pytz
-import re
-import yaml
-import yaml.representer
-
-ioex.register_yaml_str_as_unicode_constructor(yaml.Loader)
-ioex.register_yaml_unicode_as_str_representer(yaml.Dumper)
-ioex.datetimeex.register_yaml_timestamp_constructor(yaml.Loader)
-ioex.datetimeex.Duration.register_yaml_constructor(yaml.Loader)
-ioex.datetimeex.Duration.register_yaml_representer(yaml.Dumper)
-ioex.datetimeex.Period.register_yaml_constructor(yaml.Loader)
-ioex.datetimeex.Period.register_yaml_representer(yaml.Dumper)
-
-class _Object(object):
-
-    def __eq__(self, other):
-        return type(self) == type(other) and vars(self) == vars(other)
-
-    def __ne__(self, other):
-        return not (self == other)
-
-class _YamlInitConstructor(_Object, yaml.YAMLObject):
-
-    @classmethod
-    def from_yaml(cls, loader, node):
-        return cls(**loader.construct_mapping(node, deep = True))
-        # return cls(**{
-        #     k: unicode(v) if isinstance(v, str) else v
-        #         for (k, v) in loader.construct_mapping(node, deep = True).items()
-        #     })
-
-    def __eq__(self, other):
-        return (type(self) == type(other)
-                and vars(self) == vars(other))
-
-    def __ne__(self, other):
-        return not (self == other)
-
-class Figure(_YamlInitConstructor):
-
-    yaml_tag = u"!figure"
-
-    def __init__(self, value = None, unit = None):
-        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)
-
-    @classmethod
-    def to_yaml(cls, dumper, figure):
-        return dumper.represent_mapping(
-                cls.yaml_tag,
-                {'unit': figure.get_unit(), 'value': figure.get_value()},
-                )
-
-    def __sub__(self, other):
-        assert type(self) == type(other)
-        assert self.unit == other.unit
-        result = copy.deepcopy(self)
-        result.value -= other.value
-        return result
-
-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),
-                )
-
-    def __mul__(self, factor):
-        assert type(factor) in [int, float]
-        return self.__class__(
-                unit = self.unit,
-                value = self.value * factor,
-                )
-
-    def __div__(self, divisor):
-        assert type(divisor) in [int, float]
-        return self.__class__(
-                unit = self.unit,
-                value = self.value / float(divisor),
-                )
-
-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 = None, currency = None, unit = None):
-        if not currency is None and not unit is None:
-            raise ValueError('either specify currency or unit')
-        else:
-            unit = currency if currency else unit
-            super(Sum, self).__init__(
-                    value = value,
-                    unit = currency if currency else unit,
-                    )
-
-    @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'
-        elif currency == u'US$':
-            currency = u'USD'
-        elif currency == u'¥':
-            currency = u'CNY'
-        assert type(currency) is unicode
-        assert currency in [
-                u'CNY',
-                u'EUR',
-                u'USD',
-                ]
-        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],
-                )
-
-    @staticmethod
-    def parse_text(text):
-        match = re.search(
-            ur'^(?P<curr_pre>((US)?\$|€|¥)) *(?P<value>-?\d+([\.,]\d{2})?)( (?P<currency>USD))?$',
-            text,
-            re.UNICODE,
-            )
-        assert not match is None, text
-        attr = match.groupdict()
-        currency = attr['currency'] if attr['currency'] else attr['curr_pre']
-        if currency == u'$':
-            currency = u'USD'
-        return Sum(
-                value = float(attr['value'].replace(',', '.')),
-                currency = currency,
-                )
-
-class Discount(_YamlInitConstructor):
-
-    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
-
-class Order(_YamlInitConstructor):
-
-    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
-        if type(order_id) in [int]:
-            order_id = unicode(order_id)
-        assert type(order_id) is unicode
-        self.order_id = order_id
-        assert type(order_date) in [datetime.date, datetime.datetime]
-        if type(order_date) is datetime.datetime and order_date.tzinfo:
-            order_date = order_date.astimezone(pytz.utc)
-        self.order_date = order_date
-        if customer_id is not None:
-            assert type(customer_id) is unicode
-            self.customer_id = customer_id
-        if items is None:
-            self.items = []
-        else:
-            assert type(items) is list
-            assert all([isinstance(i, Item) for i in items])
-            self.items = items
-        if discounts is None:
-            self.discounts = []
-        else:
-            assert type(discounts) is list
-            assert all([isinstance(d, Discount) for d in discounts])
-            self.discounts = discounts
-
-class Item(_YamlInitConstructor):
-
-    yaml_tag = u'!item'
-
-    def __init__(
-            self,
-            name = None,
-            price_brutto = None,
-            ):
-        if not name is None:
-            assert type(name) is unicode
-            self.name = name
-        assert type(price_brutto) is Sum
-        self.price_brutto = price_brutto
-
-class Campaign(_YamlInitConstructor):
-
-    yaml_tag = u'!campaign'
-
-    def __init__(self,
-            end = None,
-            founder = None,
-            name = None,
-            website_url = None,
-            ):
-        assert type(name) is unicode
-        self.name = name
-        assert type(founder) is unicode
-        self.founder = founder
-        if not end is None:
-            assert type(end) is datetime.datetime
-            assert not end.tzinfo is None, '%r' % end
-            self.end = end
-        if not website_url is None:
-            assert type(website_url) is unicode
-            self.website_url = website_url
-
-class Pledge(Item):
-
-    yaml_tag = u'!pledge'
-
-    def __init__(self,
-            campaign = None,
-            reward = None,
-            **kwargs
-            ):
-        super(Pledge, self).__init__(**kwargs)
-        assert type(campaign) is Campaign
-        self.campaign = campaign
-        if not reward is None:
-            assert type(reward) is unicode
-            self.reward = reward
-
-class Contribution(Item):
-
-    yaml_tag = u'!contribution'
-
-    def __init__(self,
-            campaign = None,
-            reward = None,
-            **kwargs
-            ):
-        super(Contribution, self).__init__(**kwargs)
-        assert type(campaign) is Campaign
-        self.campaign = campaign
-        if not reward is None:
-            assert type(reward) is unicode
-            self.reward = reward
-
-class Article(Item):
-
-    yaml_tag = u'!article'
-
-    def __init__(
-            self,
-            authors = None,
-            color = None,
-            delivery_date = None,
-            depth = None,
-            features = None,
-            height  = None,
-            maximum_load = None,
-            option = None,
-            product_id = None,
-            quantity = None,
-            reseller = None,
-            shipper = None,
-            size = None,
-            state = None,
-            width = None,
-            **kwargs
-            ):
-        super(Article, self).__init__(**kwargs)
-        assert not self.name is None
-        assert type(quantity) is int
-        self.quantity = quantity
-        if authors is not None:
-            assert type(authors) is list
-            self.authors = authors
-        if state is not None:
-            assert type(state) is unicode
-            self.state = state
-        if reseller is not None:
-            assert type(reseller) is unicode
-            self.reseller = reseller
-        if shipper is not None:
-            assert type(shipper) is unicode
-            self.shipper = shipper
-        if product_id is not None:
-            if type(product_id) in [int]:
-                product_id = unicode(product_id)
-            assert type(product_id) is unicode
-            self.product_id = product_id
-        if option is not None:
-            assert type(option) is unicode
-            self.option = option
-        if color is not None:
-            assert type(color) is unicode
-            self.color = color
-        if size is not None:
-            assert type(size) is unicode
-            self.size = size
-        if width is not None:
-            assert type(width) is ScalarFigure
-            self.width = width
-        if depth is not None:
-            assert type(depth) is ScalarFigure
-            self.depth = depth
-        if height is not None:
-            assert type(height) is ScalarFigure
-            self.height = height
-        if maximum_load is not None:
-            assert type(maximum_load) is ScalarFigure
-            self.maximum_load = maximum_load
-        if features is not None:
-            assert type(features) is unicode
-            self.features = features
-        assert delivery_date is None or type(delivery_date) is datetime.date
-        self.delivery_date = delivery_date
-
-class Service(Item):
-
-    yaml_tag = u'!service'
-
-    def __init__(
-            self,
-            duration = None,
-            **kwargs
-            ):
-        super(Service, self).__init__(**kwargs)
-        if duration is not None:
-            assert type(duration) is ioex.datetimeex.Duration
-            self.duration = duration
-
-class Transportation(Item):
-
-    yaml_tag = u'!transportation'
-
-    def __init__(
-            self,
-            departure_point = None,
-            destination_point = None,
-            distance = None,
-            estimated_arrival_time = None,
-            passenger = None,
-            route_map = None,
-            ticket_url = None,
-            valid_from = None,
-            valid_until = None,
-            **kwargs
-            ):
-        super(Transportation, self).__init__(**kwargs)
-        if departure_point is not None:
-            assert type(departure_point) is unicode
-            self.departure_point = departure_point
-        if destination_point is not None:
-            assert type(destination_point) is unicode
-            self.destination_point = destination_point
-        if distance is not None:
-            assert type(distance) is Distance
-            self.distance = distance
-        if route_map is not None:
-            assert type(route_map) is str
-            self.route_map = route_map
-        if passenger is not None:
-            assert type(passenger) is Person
-            self.passenger = passenger
-        if valid_from is not None:
-            assert type(valid_from) is datetime.datetime
-            assert not valid_from.tzinfo is None
-            self.valid_from = valid_from
-        if valid_until is not None:
-            assert type(valid_until) is datetime.datetime
-            assert not valid_until.tzinfo is None
-            self.valid_until = valid_until
-        if ticket_url is not None:
-            assert type(ticket_url) is unicode
-            self.ticket_url = ticket_url
-        if estimated_arrival_time is not None:
-            assert type(estimated_arrival_time) is ioex.datetimeex.Period
-            assert not estimated_arrival_time.start.tzinfo is None
-            assert not estimated_arrival_time.end.tzinfo is None
-            self.estimated_arrival_time = estimated_arrival_time
-
-class Shipping(Transportation):
-
-    yaml_tag = u'!shipping'
-
-    def __init__(
-            self,
-            **kwargs
-            ):
-        super(Shipping, self).__init__(**kwargs)
-
-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
-
-class OrderRegistry(_Object, yaml.YAMLObject):
-
-    yaml_tag = u'!order-registry'
-
-    def __init__(self):
-        self.registry = {}
-
-    def register(self, order):
-        assert isinstance(order, Order)
-        if not order.platform in self.registry:
-            self.registry[order.platform] = {}
-        self.registry[order.platform][order.order_id] = order
-
-    @classmethod
-    def to_yaml(cls, dumper, self):
-        return dumper.represent_mapping(cls.yaml_tag, self.registry)
-
-    @classmethod
-    def from_yaml(cls, loader, node):
-        self = cls()
-        self.registry = loader.construct_mapping(node)
-        return self
-
-class Person(_YamlInitConstructor):
-
-    yaml_tag = u'!person'
-
-    def __init__(self, first_name = None, last_name = None):
-        self.first_name = first_name
-        self.last_name = last_name
-
-    @property
-    def first_name(self):
-        return self._first_name
-
-    @first_name.setter
-    def first_name(self, first_name):
-        assert first_name is None or type(first_name) is unicode
-        self._first_name = first_name
-
-    @property
-    def last_name(self):
-        return self._last_name
-
-    @last_name.setter
-    def last_name(self, last_name):
-        assert last_name is None or type(last_name) is unicode
-        self._last_name = last_name
-
-    @classmethod
-    def to_yaml(cls, dumper, person):
-        return dumper.represent_mapping(cls.yaml_tag, {
-            'first_name': person.first_name,
-            'last_name': person.last_name,
-            })
-
-    def __repr__(self):
-        return self.__class__.__name__ + '(%s)' % ', '.join([
-            '%s=%r' % (k, v) for k, v in vars(self).items()
-            ])

+ 0 - 53
dingguo/parser/__init__.py

@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import email
-import traceback
-
-import amazon
-import amazon_cn
-import banggood
-import hm
-import ikea
-import indiegogo
-import ingdiba
-import kickstarter
-import lieferservice
-import mytaxi
-import namecheap
-import oebb
-import thomann
-import uber
-import wienerlinien
-import yipbee
-
-order_confirmation_parsers = [
-    amazon.parse_order_confirmation_mail,
-    amazon_cn.parse_order_confirmation_mail,
-    banggood.parse_order_confirmation_mail,
-    hm.parse_order_confirmation_mail,
-    ikea.parse_order_confirmation_mail,
-    indiegogo.parse_order_confirmation_mail,
-    kickstarter.parse_order_confirmation_mail,
-    lieferservice.parse_order_confirmation_mail,
-    mytaxi.parse_order_confirmation_mail,
-    namecheap.parse_order_confirmation_mail,
-    oebb.parse_order_confirmation_mail,
-    thomann.parse_order_confirmation_mail,
-    uber.parse_order_confirmation_mail,
-    wienerlinien.parse_order_confirmation_mail,
-    yipbee.parse_order_confirmation_mail,
-    ]
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    tracebacks = {}
-
-    for parser in order_confirmation_parsers:
-        try:
-            return parser(mail = mail)
-        except:
-            tracebacks['%s.%s' % (parser.__module__, parser.__name__)] = traceback.format_exc()
-
-    raise Exception('\n'.join(['%s:\n%s' % (f, t) for (f, t) in tracebacks.items()]))

+ 0 - 114
dingguo/parser/amazon.py

@@ -1,114 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import datetime
-import dateutil
-import dateutil.tz
-import dingguo
-import email
-import ioex
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    msg_text = mail.get_payload()[0].get_payload(decode = True).decode('utf-8')
-
-    if not u'Amazon.de Bestellbestätigung' in msg_text:
-        raise Exception('no amazon order confirmation')
-
-    orders = []
-    order_texts = re.split(ur'={32,}', msg_text)[1:-1]
-
-    for order_text in order_texts:
-
-        order_id = re.search(r'Bestellnummer #(.+)', order_text).group(1)
-
-        order_date_formatted = re.search(ur'Aufgegeben am (.+)', order_text, re.UNICODE).group(1)
-        mail_date = dateutil.parser.parse(mail['date'])
-        with ioex.setlocale('de_DE.UTF-8'):
-            order_date = datetime.datetime.strptime(
-                    order_date_formatted.encode('utf-8'),
-                    '%d. %B %Y',
-                    )
-            assert order_date.date() == mail_date.date()
-
-        order = dingguo.Order(
-            u'amazon.de',
-            order_id,
-            order_date.date(),
-            )
-
-        for articles_text in re.findall(
-                    ur'Bestellte\(r\) Artikel:\s+'
-                        + ur'([\W\w]+?)\s+'
-                        + ur'(Lieferung \d|_{10,})',
-                    order_text,
-                    re.UNICODE,
-                    ):
-            for article_text in re.split(ur'\n\t*\n', articles_text[0]):
-                article_match = re.match(
-                    ur' *((?P<quantity>\d+) x )?(?P<name>.*)\n'
-                        + ur'( *von (?P<authors>.*)\n)?'
-                        + ur' *(?P<price_brutto_currency>[A-Z]+) (?P<price_brutto>\d+,\d+)\n'
-                        + ur'( *Zustand: (?P<state>.*)\n)?'
-                        + ur' *Verkauft von: (?P<reseller>.*)'
-                        + ur'(\n *Versand durch (?P<shipper>.*))?',
-                    article_text,
-                    re.MULTILINE | re.UNICODE
-                    )
-                assert article_match is not None, repr(article_text)
-                article = article_match.groupdict()
-                order.items.append(dingguo.Article(
-                    name = article['name'],
-                    price_brutto = dingguo.Sum(
-                        float(article['price_brutto'].replace(',', '.')),
-                        article['price_brutto_currency']
-                        ),
-                    quantity = int(article['quantity']) if article['quantity'] else 1,
-                    authors = article['authors'].split(',') if article['authors'] else None,
-                    state = article['state'],
-                    reseller = article['reseller'],
-                    shipper = article['shipper'],
-                    ))
-
-        assert re.search(
-                ur'Verpackung und Versand: \w+ (\d+,\d+)',
-                order_text,
-                ).group(1) == '0,00'
-        shipping_match = re.finditer(
-                ur'(Zustellung|Lieferung voraussichtlich):\s+(?P<t>[\W\w]+?)\s+'
-                    + ur'Versandart',
-                order_text if len(order_texts) > 1 else msg_text,
-                re.UNICODE,
-                )
-        for shipping_attr in [m.groupdict() for m in shipping_match]:
-            r = shipping_attr['t'].split('-')
-            with ioex.setlocale('de_AT.UTF-8'):
-                s = datetime.datetime.strptime(
-                        r[0].strip().encode('utf8'),
-                        '%A, %d. %B',
-                        ).replace(
-                            year = order.order_date.year,
-                            tzinfo = dateutil.tz.gettz('Europe/Berlin'),
-                            )
-                assert s.date() > order.order_date
-            if len(r) == 1:
-                e = s + datetime.timedelta(days = 1)
-            else:
-                with ioex.setlocale('de_AT.UTF-8'):
-                    e = datetime.datetime.strptime(
-                            r[1].strip().encode('utf8'),
-                            '%A, %d. %B',
-                            ).replace(year = s.year, tzinfo = s.tzinfo)
-                    e += datetime.timedelta(days = 1)
-            assert e > s
-            order.items.append(dingguo.Shipping(
-                price_brutto = dingguo.Sum(0.0, u'EUR'),
-                estimated_arrival_time = ioex.datetimeex.Period(start = s, end = e),
-                ))
-
-        orders.append(order)
-
-    return orders
-

+ 0 - 88
dingguo/parser/amazon_cn.py

@@ -1,88 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import BeautifulSoup
-import datetime
-import dateutil.parser
-import dingguo
-import email.message
-import ioex
-import pytz
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    html = mail.get_payload()[1].get_payload(decode = True).decode('utf-8')
-
-    if not u'订单详情' in html:
-        raise Exception()
-
-    doc = BeautifulSoup.BeautifulSoup(html)
-
-    for br in doc.findAll('br'):
-        br.replaceWith('\n')
-
-    def get_text(tag):
-        return u''.join([c if type(c) is BeautifulSoup.NavigableString else get_text(c) for c in tag.contents])
-
-    orders = []
-
-    order_id_label_tags = doc.findAll(text = re.compile(u'订单号'))
-    assert len(order_id_label_tags) % 2 == 0
-    order_id_label_tags = order_id_label_tags[len(order_id_label_tags)/2:]
-    for order_id_label_tag in order_id_label_tags:
-
-        order = dingguo.Order(
-            platform = u'amazon.cn',
-            order_id = order_id_label_tag.findNext().text,
-            order_date = dateutil.parser.parse(mail['date']),
-            )
-
-        order_detail_tag = (order_id_label_tag.findNext().findNext('table')
-            if len(order_id_label_tags) > 1
-            else order_id_label_tag.findParent('table').findPrevious('table'))
-        article_table = order_detail_tag.findNext('table')
-        if len(order_id_label_tags) == 1:
-            article_table = article_table.findNext('table')
-        for article_tag in article_table.find('tbody').findChildren(recursive = False):
-            image_col_tag, details_tag, price_tag = article_tag.findAll('td', recursive = False)
-            name_tag = details_tag.find('a')
-            order.items.append(dingguo.Article(
-                name = name_tag.text.strip(),
-                price_brutto = dingguo.Sum.parse_text(price_tag.text),
-                quantity = 1,
-                reseller = name_tag.findNext(text = re.compile(u'由')).findNext().text,
-                ))
-
-        del_date_str = order_detail_tag.find(text = re.compile(u'预计送达日期:')).findNext().text
-        def parse_date(s):
-            with ioex.setlocale('zh_CN.utf-8'):
-                return pytz.timezone('Asia/Shanghai').localize(
-                    datetime.datetime.strptime(
-                        s.encode('utf-8'),
-                        '%A, %m/%d',
-                        ).replace(year = order.order_date.year)
-                    )
-        del_date_range = [parse_date(s.strip()) for s in del_date_str.split('-')]
-        if len(del_date_range) == 1:
-            del_date_range.append(del_date_range[0])
-        order.items.append(dingguo.Shipping(
-           name = order_detail_tag.find(text = re.compile(u'送货方式:')).findNext('b').text,
-           price_brutto = dingguo.Sum.parse_text(
-               order_detail_tag.findNext(text = re.compile(u'配送费:')).findNext().text
-               ),
-            estimated_arrival_time = ioex.datetimeex.Period(
-               start = del_date_range[0],
-               end = (del_date_range[1] + datetime.timedelta(days = 1)),
-               ),
-           destination_point = '\n'.join([l.strip() for l in
-               get_text(
-                   order_detail_tag.find(text = re.compile(u'您的订单发送至:')).findNext('b')
-                   ).split('\n')
-               ]),
-            ))
-
-        orders.append(order)
-
-    return orders

+ 0 - 76
dingguo/parser/banggood.py

@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import BeautifulSoup
-import datetime
-import dingguo
-import email.message
-import ioex
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    html = mail.get_payload()[1].get_payload(decode = True).decode('utf-8')
-    doc = BeautifulSoup.BeautifulSoup(html)
-
-    """ Your Order#13998883(placed on Saturday 16 April, 2016)"""
-    order_attr = re.search(
-            ur'Your Order#(?P<id>\d+)\(placed on (?P<date>.*)\)',
-            doc.find(text = re.compile(r'placed on')).parent.text,
-            re.UNICODE,
-            ).groupdict()
-
-    with ioex.setlocale('en_US.UTF-8'):
-        order = dingguo.Order(
-                platform = u'banggood',
-                order_id = order_attr['id'],
-                order_date = datetime.datetime.strptime(order_attr['date'], '%A %d %B, %Y').date(),
-                customer_id = None,
-                )
-
-    """ 1 x 10X E27 LED Bulb 7W Warm White 36 SMD 5730 AC 220V Corn Light (10xPOA162664)€23.431 x 5X E14 7W Warm White 36 SMD 5730 LED Corn Light Lamp Bulbs AC 220V (5xPOA162668)€12.31 """
-    for price_tag in doc.find(text = re.compile(r'Subtotal of Items')).findParent('td').findAll('strong'):
-        article_match = re.search(
-            ur'^(?P<quantity>\d+) x (?P<name>.*)'
-                + ur' \((\d+x)?(?P<id>[^\)]+)\)',
-            price_tag.previousSibling.replace('&nbsp;', ' ').strip(),
-            re.UNICODE,
-            )
-        potential_option = price_tag.nextSibling.findNext(text = re.compile(ur'\w+')).replace('&nbsp;', ' ').strip()
-        if (not 'IMPORTANT' in potential_option
-                and not re.search(ur'^\d+ x', potential_option)):
-            option = potential_option
-        else:
-            option = None
-        attr = article_match.groupdict()
-        order.items.append(dingguo.Article(
-            name = attr['name'],
-            option = option,
-            price_brutto = dingguo.Sum.parse_text(price_tag.text) / float(attr['quantity']),
-            product_id = attr['id'],
-            quantity = int(attr['quantity']),
-            ))
-
-    shipping_line_tag = doc.find(text = re.compile('Sub-Total')).findNext('tr')
-    shipping_option_tag, shipping_price_tag = shipping_line_tag.findAll('td')
-    order.items.append(dingguo.Shipping(
-        name = shipping_option_tag.text,
-        price_brutto = dingguo.Sum.parse_text(shipping_price_tag.text),
-        ))
-
-    insurance_label_tag = shipping_line_tag.findNext(text = re.compile('Insurance'))
-    if insurance_label_tag:
-        order.items.append(dingguo.Service(
-            name = insurance_label_tag.parent.text,
-            price_brutto = dingguo.Sum.parse_text(insurance_label_tag.findNext().text),
-            ))
-
-    points_discount_label_tag = doc.find(text = re.compile('Discount Points'))
-    if points_discount_label_tag:
-        order.discounts.append(dingguo.Discount(
-            name = u'redeem %d banggood points' % int(points_discount_label_tag.split(':')[1]),
-            amount = dingguo.Sum.parse_text(points_discount_label_tag.findNext().text) * -1,
-            ))
-
-    return [order]

+ 0 - 57
dingguo/parser/hm.py

@@ -1,57 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import datetime
-import dingguo
-import string
-import email
-import ioex
-import re
-import BeautifulSoup
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    html_mail = mail.get_payload()[0].get_payload()[1].get_payload(decode = True)
-
-    doc = BeautifulSoup.BeautifulSoup(html_mail)
-
-    order_attr = re.search(
-            ur'Bestelldatum:(?P<date>\d+)'
-                + ur'Benutzername:(?P<user>.*)'
-                + ur'Name',
-            doc.find(text = 'Bestelldatum:').findParent('table').text,
-            re.UNICODE,
-            ).groupdict()
-    user = ''.join([c if (c in string.printable) else '' for c in order_attr['user']])
-    order = dingguo.Order(
-            customer_id = user,
-            order_date = datetime.datetime.strptime(order_attr['date'], '%d%m%y').date(),
-            order_id = u'%s-%s' % (user, order_attr['date']),
-            platform = u'hm',
-            )
-
-    for article_tag in [t.findParents('tr')[2] for t in doc.findAll(text = 'Art.-Nr.:')]:
-        article_subtags = article_tag.findAll(recursive = False)
-        article = re.search(
-            ur'Art.-Nr.:(?P<id>[\d-]+)'
-                + ur'Beschreibung:(?P<label>.*)'
-                + ur'Größe:(?P<size>.*)'
-                + ur'Farbe:(?P<color>[^\d]+)',
-            article_subtags[1].text,
-            re.UNICODE,
-            ).groupdict()
-        order.items.append(dingguo.Article(
-            color = article['color'],
-            name = article['label'],
-            price_brutto = dingguo.Sum(
-                float(article_subtags[3].text.replace(',', '.')), 
-                u'EUR',
-                ),
-            product_id = article['id'],
-            quantity = int(article_subtags[2].text),
-            reseller = u'H&M',
-            size = article['size'],
-            ))
-
-    return [order]

+ 0 - 71
dingguo/parser/ikea.py

@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import datetime
-import dingguo
-import email
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    mail_text = mail.get_payload()[0].get_payload(decode = True).decode('iso-8859-1')
-
-    if not u'Danke für deine Bestellung bei IKEA' in mail_text:
-        raise Exception('no ikea order confirmation')
-
-    order = dingguo.Order(
-        order_id = re.search(ur'Bestellnummer:\s+(\d+)', mail_text).group(1),
-        order_date = datetime.datetime.strptime(
-                re.search(ur'Bestelldatum:\s+(\d\d-\d\d-\d{4})', mail_text).group(1),
-                '%d-%m-%Y',
-                ).date(),
-        platform = u'ikea',
-        )
-
-    # Menge  1
-    # Produkt KOMPLEMENT
-    #  Bügel, ausziehbar , dunkelgrau
-    #  Tiefe: 58 cm
-    #  Max Belastung: 4 kg
-    # Artikelnummer: 202.624.87
-    # Einzelpreis ? 10,-
-    # Gesamtpreis ? 10,-
-
-    # Menge  1
-    # Produkt ÄPPLARÖ
-    #  Hocker/außen , braun faltbar braun las.
-    #  Breite: 40 cm
-    #  Tiefe: 38 cm
-    #  Höhe: 42 cm
-    # Artikelnummer: 202.049.25
-    # Einzelpreis ? 19,99
-    # Gesamtpreis ? 19,99
-
-    for article_text in re.findall(ur'Menge[\W\w]+?Gesamtpreis', mail_text):
-        article_attr = re.match(
-                ur'Menge\s+(?P<quantity>\d+)\s+'
-                    + ur'Produkt\s+(?P<name>.+)\s+'
-                    + ur'(?P<label>.+)\s+'
-                    + ur'(Breite: (?P<width>\d+) (?P<width_unit>[^\s]+)\s+)?'
-                    + ur'(Tiefe: (?P<depth>\d+) (?P<depth_unit>[^\s]+)\s+)?'
-                    + ur'(Höhe: (?P<height>\d+) (?P<height_unit>[^\s]+)\s+)?'
-                    + ur'(Max Belastung: (?P<maximum_load>\d+) (?P<maximum_load_unit>[^\s]+)\s+)?'
-                    + ur'Artikelnummer: (?P<product_id>[\d\.]+)\s+'
-                    + ur'Einzelpreis \? (?P<price>\d+,\d*)-?\s+'
-                    + ur'Gesamtpreis',
-                article_text,
-                re.UNICODE,
-                ).groupdict()
-        order.items.append(dingguo.Article(
-            depth = dingguo.ScalarFigure(float(article_attr['depth']), article_attr['depth_unit']) if article_attr['depth'] else None,
-            height = dingguo.ScalarFigure(float(article_attr['height']), article_attr['height_unit']) if article_attr['height'] else None,
-            maximum_load = dingguo.ScalarFigure(float(article_attr['maximum_load']), article_attr['maximum_load_unit']) if article_attr['maximum_load'] else None,
-            name = u'%s %s' % (article_attr['name'].strip(), article_attr['label'].strip().replace(' , ', ', ')),
-            price_brutto = dingguo.Sum(float(article_attr['price'].replace(',', '.')), u'EUR'),
-            product_id = article_attr['product_id'],
-            quantity = int(article_attr['quantity']),
-            width = dingguo.ScalarFigure(float(article_attr['width']), article_attr['width_unit']) if article_attr['width'] else None,
-            ))
-
-    return [order]

+ 0 - 75
dingguo/parser/indiegogo.py

@@ -1,75 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import BeautifulSoup
-import datetime
-import dateutil.parser
-import dingguo
-import email.message
-import ioex
-import pytz
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    html = mail.get_payload()[1].get_payload(decode = True).decode('utf-8')
-
-    if not 'indiegogo' in html:
-        raise Exception()
-
-    email_recipient_address = mail['delivered-to'].decode('utf8')
-    email_date = dateutil.parser.parse(mail['date'])
-    order = dingguo.Order(
-        platform = u'indiegogo',
-        order_id = '%s-%sZ' % (
-            email_recipient_address,
-            email_date.astimezone(pytz.utc).replace(tzinfo = None).isoformat(),
-            ),
-        order_date = email_date,
-        customer_id = email_recipient_address,
-        )
-
-    doc = BeautifulSoup.BeautifulSoup(html)
-
-    campaign_name_tag = doc.find(
-            text = re.compile(u'(vielen Dank für deinen Beitrag|thank you for contributing)')
-            ).findNext('a')
-    reward_label_tag = doc.find(text = re.compile(u'Perk:'))
-
-    order.items.append(dingguo.Contribution(
-        campaign = dingguo.Campaign(
-            name = campaign_name_tag.text,
-            founder = doc.find(
-                text = re.compile(u'(Kontaktiere den Kampagnenstarter|Contact the campaign owner)')
-                ).findNext().text,
-            website_url = campaign_name_tag['href'],
-            ),
-        price_brutto = dingguo.Sum.parse_text(
-            doc.find(text = re.compile(u'(Unterstützung|Contribution):')).findNext().text
-            ),
-        reward = reward_label_tag.findNext().text if reward_label_tag else None,
-        ))
-
-    shipping_costs_label_tag = doc.find(text = re.compile(u'(Versand|Shipping):'))
-    if shipping_costs_label_tag:
-        with ioex.setlocale('de_AT.utf8' if 'Versand' in shipping_costs_label_tag else 'en_US.utf8'):
-            estimated_arrival_start = datetime.datetime.strptime(
-                    doc.find(
-                        text = re.compile('(Voraussichtliche Lieferung|Estimated Delivery):')
-                        ).findNext().text,
-                    '%B %Y'
-                    ).replace(tzinfo = email_date.tzinfo)
-        order.items.append(dingguo.Shipping(
-            price_brutto = dingguo.Sum.parse_text(
-                shipping_costs_label_tag.findNext().text
-                ),
-            estimated_arrival_time = ioex.datetimeex.Period(
-                start = estimated_arrival_start,
-                end = (estimated_arrival_start + datetime.timedelta(days = 32)).replace(day = 1),
-                ),
-            destination_point = doc.find(text = re.compile('(Lieferadresse|Shipping Address):'))
-                .findNext().text.replace('\r\n', '\n'),
-            ))
-
-    return [order]

+ 0 - 43
dingguo/parser/ingdiba.py

@@ -1,43 +0,0 @@
-def ingdiba_pdf_file_to_text(pdf_file):
-
-    import pdfminer.pdfparser
-    parser = pdfminer.pdfparser.PDFParser(pdf_file)
-
-    import pdfminer.pdfdocument
-    doc = pdfminer.pdfdocument.PDFDocument(parser)
-    assert doc.is_extractable
-
-    import pdfminer.pdfinterp
-    resource_manager = pdfminer.pdfinterp.PDFResourceManager()
-    import pdfminer.converter
-    device = pdfminer.converter.PDFPageAggregator(resource_manager)
-    interpreter = pdfminer.pdfinterp.PDFPageInterpreter(resource_manager, device)
-    
-    import pdfminer.pdfpage
-    page = [p for p in pdfminer.pdfpage.PDFPage.create_pages(doc)][1]
-    
-    """ The bbox value is (x0,y0,x1,y1).
-    
-    x0: the distance from the left of the page to the left edge of the box.
-    y0: the distance from the bottom of the page to the lower edge of the box.
-    x1: the distance from the left of the page to the right edge of the box.
-    y1: the distance from the bottom of the page to the upper edge of the box.
-    
-    Remember in PDF the page origin is the *bottom left corner*.
-    So the bottom left is (0,0) and the top right corner is
-    somewhere like (612,792) in the case of A4 paper. 
-    
-    https://groups.google.com/forum/#!topic/pdfminer-users/wOvDSW23B4M
-    
-    """
-    
-    interpreter.process_page(page)
-
-    chars = {}
-    for ltchar in device.get_result():
-        if isinstance(ltchar, pdfminer.layout.LTChar):
-            if not ltchar.y0 in chars:
-                chars[ltchar.y0] = {}
-            chars[ltchar.y0][ltchar.x0] = ltchar.get_text()
-
-    return '\n'.join([''.join([chars[y0][x0] for x0 in sorted(chars[y0])]) for y0 in sorted(chars)[::-1]]) + '\n'

+ 0 - 71
dingguo/parser/kickstarter.py

@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import BeautifulSoup
-import datetime
-import dateutil.parser
-import dingguo
-import email.message
-import ioex
-import pytz
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    html = mail.get_payload()[1].get_payload(decode = True).decode('utf-8')
-
-    if not 'kickstarter' in html:
-        raise Exception()
-
-    email_recipient_address = mail['delivered-to'].decode('utf8')
-    email_date = dateutil.parser.parse(mail['date'])
-    order = dingguo.Order(
-        platform = u'kickstarter',
-        order_id = '%s-%sZ' % (
-            email_recipient_address,
-            email_date.astimezone(pytz.utc).replace(tzinfo = None).isoformat(),
-            ),
-        order_date = email_date,
-        customer_id = email_recipient_address,
-        )
-
-    doc = BeautifulSoup.BeautifulSoup(html)
-
-    def get_text(tag):
-        return u''.join([c if type(c) is BeautifulSoup.NavigableString else get_text(c) for c in tag.contents])
-
-    shipping_label_tag = doc.find(text = re.compile('Shipping'))
-    if shipping_label_tag:
-        shipping_attr = re.search(
-                ur'^Shipping(?P<dest>.*)\((?P<price>.*)\)$',
-                shipping_label_tag.parent.parent.text,
-                ).groupdict()
-        shipping = dingguo.Shipping(
-            destination_point = shipping_attr['dest'].strip(),
-            price_brutto = dingguo.Sum.parse_text(shipping_attr['price']),
-            )
-    else:
-        shipping = None
-
-    pledge_amount = dingguo.Sum.parse_text(
-            doc.find(text = re.compile('Amount pledged')).parent.parent.text.replace('Amount pledged', '')
-            )
-
-    order.items.append(dingguo.Pledge(
-        campaign = dingguo.Campaign(
-            name = doc.find('h2').text,
-            founder = doc.find('h2').findNext('p').text[len('By '):],
-            end = dateutil.parser.parse(
-                doc.find(text = re.compile('your card will be charged on')).findNext('time')['datetime']
-                ),
-            ),
-        price_brutto = pledge_amount - shipping.price_brutto if shipping else pledge_amount,
-        reward = get_text(doc.find(text = re.compile('Reward')).findPrevious('td'))
-            .strip()[len('Reward'):].strip().replace('\r\n', '\n'),
-        ))
-
-    if shipping:
-        order.items.append(shipping)
-
-    return [order]

+ 0 - 85
dingguo/parser/lieferservice.py

@@ -1,85 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import datetime
-import dateutil.parser
-import dingguo
-import email
-import email.utils
-import ioex
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    text = mail.get_payload()[0].get_payload(decode = True).decode('utf-8').replace('\r\n', '\n')
-    assert type(text) is unicode
-
-    if not 'Lieferservice.at' in text:
-        raise Exception('no lieferservice.at confirmation')
-    msg_text = mail.get_payload()[0].get_payload(decode = True).decode('utf-8')
-
-    order_match = re.search(
-            ur'(Your order|Ihre Bestellung) \(.+\) (at|bei) (?P<restaurant>.*)\s+'
-                + ur'(Your order reference is|Ihre Bestellnummer lautet): (?P<order_id>.*)\s+'
-                + ur'[\W\w]+'
-                + ur'(Your order|Ihre Bestellung)\s+'
-                + ur'(?P<orders_text>[\W\w]+)'
-                + ur'(Delivery costs|Lieferung):\s+(?P<delivery_costs>.*)\s+',
-            text,
-            re.UNICODE,
-            )
-    order_match_groups = order_match.groupdict()
-
-    order = dingguo.Order(
-        u'lieferservice.at',
-        order_match_groups['order_id'].strip(),
-        order_date = dateutil.parser.parse(mail['Date']),
-        )
-
-    restaurant = order_match_groups['restaurant'].strip('"')
-
-    for article_match in re.finditer(
-            ur'(?P<quantity>\d+)x\s'
-                + ur'(?P<name>.*)\s'
-                + ur'(?P<currency>.) (?P<price>-?\d+,\d+)\s',
-            order_match_groups['orders_text'],
-            re.UNICODE,
-            ):
-        article_match_groups = article_match.groupdict()
-        quantity = int(article_match_groups['quantity'])
-        assert quantity == 1
-        name = re.sub(ur' +', ' ', article_match_groups['name'])
-        price = dingguo.Sum(
-            float(article_match_groups['price'].replace(',', '.')),
-            article_match_groups['currency'],
-            )
-        if price.value < 0:
-            price.value *= -1
-            order.discounts.append(dingguo.Discount(
-                name = name,
-                amount = price,
-                ))
-        else:
-            order.items.append(dingguo.Article(
-                name = name,
-                quantity = 1,
-                price_brutto = price,
-                reseller = restaurant,
-                shipper = restaurant,
-                ))
-
-    delivery_costs = order_match_groups['delivery_costs'].strip()
-    if delivery_costs in ['FREE', 'GRATIS']:
-        order.items.append(dingguo.Item(
-            name = u'Delivery',
-            price_brutto = dingguo.Sum(0.0, u'EUR'),
-            ))
-    else:
-        unit, value = delivery_costs.split(' ')
-        order.items.append(dingguo.Item(
-            name = u'Delivery',
-            price_brutto = dingguo.Sum(float(value.replace(',', '.')), unit),
-            ))
-
-    return [order]

+ 0 - 79
dingguo/parser/mytaxi.py

@@ -1,79 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import datetime
-import dingguo
-import email
-import ioex
-import re
-import subprocess
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    if not 'mytaxi' in mail.get_payload()[0].get_payload()[0].get_payload(decode = True):
-        raise Exception('no mytaxi mail')
-
-    pdf_compressed = mail.get_payload()[1].get_payload(decode = True)
-    pdftk = subprocess.Popen(
-            ['pdftk - output - uncompress'],
-            shell = True,
-            stdin = subprocess.PIPE,
-            stdout = subprocess.PIPE,
-            )
-    pdf_uncompressed = pdftk.communicate(
-        input = pdf_compressed,
-        )[0].decode('latin-1')
-    assert type(pdf_uncompressed) is unicode
-
-    order_match = re.search(
-        ur'Rechnungsnummer:[^\(]+\((?P<order_id>\w+)\)',
-        pdf_uncompressed,
-        re.MULTILINE | re.UNICODE
-        )
-    order_id = order_match.groupdict()['order_id']
-
-    ride_match_groups = re.search(
-        ur'\(Bruttobetrag\)'
-            + ur'[^\(]+'
-            + ur'\((?P<price_brutto>\d+,\d+) (?P<price_brutto_currency>.+)\)'
-            + ur'[\w\W]+'
-            + ur'\((?P<driver>[^\(]+)\)'
-            + ur'[^\(]+'
-            + ur'\(\d+,\d+ .\)'
-            + ur'[^\(]+'
-            + ur'\((?P<name>Taxifahrt)'
-            + ur'[^\(]+'
-            + ur'\(von: (?P<departure_point>[^\)]+)'
-            + ur'[^\(]+'
-            + ur'\(nach: (?P<destination_point>[^\)]+)'
-            + ur'[\w\W]+'
-            + ur'Belegdatum \\\(Leistungszeitpunkt\\\):[^\(]+\((?P<arrival_time>\d\d.\d\d.\d\d \d\d:\d\d)\)',
-        pdf_uncompressed,
-        re.MULTILINE | re.UNICODE
-        ).groupdict()
-
-    arrival_time = datetime.datetime.strptime(
-        ride_match_groups['arrival_time'],
-        '%d.%m.%y %H:%M'
-        )
-
-    order = dingguo.Order(
-        u'mytaxi',
-        order_id,
-        order_date = arrival_time.date(),
-        )
-    with ioex.setlocale('en_US.UTF-8'):
-        order.items.append(dingguo.TaxiRide(
-            price_brutto = dingguo.Sum(
-                float(ride_match_groups['price_brutto'].replace(',', '.')),
-                # why 0x80 ?
-                u'EUR' if (ride_match_groups['price_brutto_currency'] == u'\x80')
-                    else ride_match_groups['price_brutto_currency'],
-                ),
-            departure_point = ride_match_groups['departure_point'],
-            destination_point = ride_match_groups['destination_point'],
-            driver = ride_match_groups['driver'],
-            arrival_time = arrival_time,
-            ))
-    return [order]

+ 0 - 61
dingguo/parser/namecheap.py

@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import BeautifulSoup
-import datetime
-import dateutil.parser
-import dingguo
-import email.message
-import ioex.datetimeex
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    html = mail.get_payload()[1].get_payload(decode = True).decode('utf-8')
-
-    if not 'Namecheap' in html:
-        raise Exception()
-
-    doc = BeautifulSoup.BeautifulSoup(html)
-
-    email_date = dateutil.parser.parse(mail['date'])
-    order_date = dateutil.parser.parse(
-        doc.find(text = re.compile('Order Date:')).findNext(text = re.compile('\d+'))
-        ).replace(tzinfo = email_date.tzinfo)
-    # ensure timezone is correct
-    assert (email_date - order_date) < datetime.timedelta(minutes = 30)
-
-    order = dingguo.Order(
-        platform = u'namecheap',
-        order_id = doc.find(text = re.compile('Order Number:')).findNext().findNext().text,
-        order_date = order_date,
-        customer_id = doc.find(text = re.compile('User Name:')).findNext().findNext().text,
-        )
-
-    for item_row_tag in doc.find(text = re.compile('TITLE')).findParent('table').findAll('tr')[1:-2]:
-        title_tag, quantity_tag, duration_tag, price_tag, subtotal_tag = item_row_tag.findAll('td')
-        assert int(quantity_tag.text.replace('&nbsp;', '')) == 1
-        item = dingguo.Service(
-            name = title_tag.text.replace('\r\n', '').replace('&nbsp;', '').strip(),
-            price_brutto = dingguo.Sum.parse_text(price_tag.text.replace('&nbsp;', '')),
-            duration = ioex.datetimeex.Duration(
-                years = int(duration_tag.text.replace(' year&nbsp;', '')),
-                ),
-            )
-        order.items.append(item)
-        fee_tag = subtotal_tag.find('small')
-        if fee_tag:
-            fee_name, fee_price = fee_tag.text.split('  ')
-            order.items.append(dingguo.Service(
-                name = '%s %s' % (item.name, fee_name),
-                price_brutto = dingguo.Sum.parse_text(fee_price),
-                duration = item.duration,
-                ))
-
-    total_price = dingguo.Sum.parse_text(
-            doc.find(text = re.compile('^TOTAL$')).findNext('strong').text
-            )
-    assert total_price.value == sum([i.price_brutto.value for i in order.items])
-
-    return [order]

+ 0 - 72
dingguo/parser/oebb.py

@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import datetime
-import dingguo
-import email
-import ioex
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    msg = mail.get_payload()[0]
-    if type(msg.get_payload()) is list:
-        msg = msg.get_payload()[0]
-    msg_text = msg.get_payload(decode = True).decode('utf8')
-
-    # msg_text = re.sub(
-    #     r'<[^>]+>',
-    #     '',
-    #     HTMLParser.HTMLParser().unescape(msg.get_payload(decode = True).decode('utf8'))
-    #     )
-
-    order_match = re.search(
-        ur'(?P<order_id_label>Booking code|Buchungscode):\s+(?P<order_id>[\d ]+)\s+'
-            + ur'(Customer number|Kundennummer):\s+(?P<customer_id>PV\d+)\s+'
-            + ur'(Booking date|Buchungsdatum):\s+(?P<order_date>.* \d{4})\s',
-        msg_text,
-        re.MULTILINE | re.UNICODE
-        )
-    order_match_groups = order_match.groupdict()
-
-    if order_match_groups['order_id_label'] == 'Buchungscode':
-        with ioex.setlocale('de_AT.UTF-8'):
-            order_date = datetime.datetime.strptime(
-                order_match_groups['order_date'],
-                '%d. %b %Y',
-                )
-    else:
-        with ioex.setlocale('en_US.UTF-8'):
-            order_date = datetime.datetime.strptime(
-                order_match_groups['order_date'],
-                '%b %d, %Y',
-                )
-
-    order = dingguo.Order(
-        u'oebb',
-        order_match_groups['order_id'],
-        order_date.date(),
-        customer_id = order_match_groups['customer_id'],
-        )
-
-    item_match = re.search(
-        ur'(?P<price_brutto_currency>.) ?(?P<price_brutto>\d+(\.|,)\d+)'
-            + ur'[\W\w]+'
-            + ur'(Your Bookings?|Ihre Buchung)\s+'
-            + ur'(?P<departure_point>.*)\s+>\s+(?P<destination_point>.*)',
-        msg_text,
-        re.MULTILINE | re.UNICODE
-        )
-    item = item_match.groupdict()
-    order.items.append(dingguo.Transportation(
-        name = u'Train Ticket',
-        price_brutto = dingguo.Sum(
-            float(item['price_brutto'].replace(',', '.')),
-            item['price_brutto_currency'],
-            ),
-        departure_point = item['departure_point'],
-        destination_point = item['destination_point'],
-        ))
-
-    return [order]

+ 0 - 46
dingguo/parser/thomann.py

@@ -1,46 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import BeautifulSoup
-import datetime
-import dingguo
-import email
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    doc = BeautifulSoup.BeautifulSoup(
-        mail.get_payload()[1].get_payload(decode = True).decode('utf-8')
-        )
-    
-    if doc.find(text = re.compile(ur'Thomann')) is None:
-        raise Exception('no thomann order confirmation')
-   
-    order = dingguo.Order(
-        customer_id = doc.find(text = re.compile(ur'Kundennummer')).parent.nextSibling.text,
-        order_id = doc.find(text = re.compile(ur'Auftragsnummer')).parent.nextSibling.text,
-        order_date = datetime.datetime.strptime(
-                doc.find(text = re.compile(ur'Auftragsdatum')).parent.nextSibling.text,
-                '%d.%m.%Y %H:%M',
-                ).date(),
-        platform = u'thomann',
-        )
-    
-    for product_id_label_tag in doc.findAll(text = re.compile(ur'Artikelnummer')):
-        article_tag = product_id_label_tag.findParents('td')[1]
-        name_quantity_tag = article_tag.find('strong')
-        quantity, name = re.match(ur'(\d)x (.*)', name_quantity_tag.text).groups()
-        currency, price = re.match(
-                ur'(.) (\d+,\d+)', 
-                product_id_label_tag.parent.nextSibling.text,
-                ).groups()
-        order.items.append(dingguo.Article(
-            features = name_quantity_tag.parent.nextSibling.text,
-            name = name, 
-            price_brutto = dingguo.Sum(float(price.replace(',', '.')), currency),
-            product_id = product_id_label_tag.nextSibling.text,
-            quantity = int(quantity),
-            ))
-
-    return [order]

+ 0 - 76
dingguo/parser/uber.py

@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import BeautifulSoup
-import datetime
-import dingguo
-import email
-import ioex
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    html = mail.get_payload()[0].get_payload(decode = True)
-
-    """ document in html2 has the same structure as the one in html.
-        only difference is that hyperlink urls in html2 have been
-        replaced by 'email.uber.com/wf/click?upn=.*' urls.
-        """
-    html2 = mail.get_payload()[1].get_payload()[0].get_payload(decode = True)
-
-    route_map = mail.get_payload()[1].get_payload()[1].get_payload(decode = True)
-
-    doc = BeautifulSoup.BeautifulSoup(
-        html,
-        convertEntities = BeautifulSoup.BeautifulSoup.HTML_ENTITIES,
-        )
-
-    # strptime
-    with ioex.setlocale('en_US.UTF-8'):
-
-        trip_id = re.search(
-            ur'[\da-f\-]{36}',
-            doc.find(text = 'Visit the trip page').parent['href'],
-            ).group(0)
-        order = dingguo.Order(
-            u'uber',
-            trip_id,
-            datetime.datetime.strptime(
-                doc.find(attrs = {'class': 'date'}).text,
-                '%B %d, %Y',
-                ).date(),
-            )
-
-        departure_time_tag = doc.find(attrs = {'class': 'from time'})
-        departure_time = datetime.datetime.strptime(
-            departure_time_tag.text,
-            '%I:%M%p',
-            ).time()
-
-        arrival_time_tag = doc.find(attrs = {'class': 'to time'})
-        arrival_time = datetime.datetime.strptime(
-            arrival_time_tag.text,
-            '%I:%M%p',
-            ).time()
-
-    distance = dingguo.Distance(
-        float(doc.find(text = 'kilometers').parent.parent.find(attrs = {'class': 'data'}).text),
-        u'km',
-        )
-
-    fare = doc.find(attrs = {'class': 'header-price'}).find(attrs = {'class': 'header-fare text-pad'}).text
-
-    order.items.append(dingguo.TaxiRide(
-        name = doc.find(text = 'CAR').parent.parent.find(attrs = {'class': 'data'}).text + ' Ride',
-        price_brutto = dingguo.Sum(float(fare[1:]), fare[0]),
-        arrival_time = datetime.datetime.combine(order.order_date, arrival_time),
-        departure_time = datetime.datetime.combine(order.order_date, departure_time),
-        departure_point = departure_time_tag.parent.find(attrs = {'class': 'address'}).text,
-        destination_point = arrival_time_tag.parent.find(attrs = {'class': 'address'}).text,
-        distance = distance,
-        driver = doc.find(attrs = {'class': 'driver-info'}).text[len('You rode with '):],
-        route_map = route_map,
-        ))
-
-    return [order]

+ 0 - 92
dingguo/parser/wienerlinien.py

@@ -1,92 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import datetime
-import dateutil.parser
-import dingguo
-import email
-import email.utils
-import ioex
-import pytz
-import re
-
-class UtcOffsetTimezone(datetime.tzinfo):
-
-    def __init__(self, seconds):
-        assert type(seconds) is int
-        self.seconds = seconds
-
-    def utcoffset(self, dt):
-        return datetime.timedelta(seconds = self.seconds)
-
-    def dst(self, dt):
-        return None
-
-    def tzname(self, dt):
-        return self.__class__.__name__ + '(seconds = %d)' % self.seconds
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    msg_text = mail.get_payload(decode = True).decode('utf8')
-    if not 'Wiener Linien' in msg_text:
-        raise Exception()
-
-    order_match = re.search(
-        r'(?P<name>.*Ticket.*)\s+'
-            + r'Valid from: (?P<valid_from>\d{1,2} \w+ \d{4} \d{1,2}:\d{2})\s+'
-            + r'(Valid until: (?P<valid_until>\d{1,2} \w+ \d{4} \d{1,2}:\d{2})\s+)?'
-            + r'Type: (?P<type>.* ticket)\s+'
-            + r'For\:\s+'
-            + r'(?P<passenger_first_name>Fabian Peter)\s+'
-            + r'(?P<passenger_last_name>Hammerle)\s+'
-            + ur'Price: (?P<currency>€)(?P<price>\d+.\d{2})\s+'
-            + r'Quantity: (?P<quantity>1)\s+'
-            ,
-        msg_text,
-        re.MULTILINE | re.UNICODE
-        )
-    if not order_match:
-        print(repr(msg_text))
-    order_match_groups = order_match.groupdict()
-
-    email_recipient_address = mail['delivered-to'].decode('utf8')
-    email_date = dateutil.parser.parse(mail['date'])
-    order = dingguo.Order(
-        platform = u'wiener_linien',
-        order_id = '%s-%sZ' % (
-            email_recipient_address,
-            email_date.astimezone(pytz.utc).replace(tzinfo = None).isoformat(),
-            ),
-        order_date = email_date,
-        customer_id = email_recipient_address,
-        )
-
-    assert int(order_match_groups['quantity']) == 1
-
-    ticket_url_match = re.search(
-        ur'following link:\s+(http[^\s]+)',
-        msg_text,
-        re.MULTILINE | re.UNICODE,
-        )
-
-    with ioex.setlocale('en_US.UTF-8'):
-        timezone = UtcOffsetTimezone(email.utils.parsedate_tz(mail['date'])[9])
-        order.items.append(dingguo.Transportation(
-            name = order_match_groups['name'],
-            price_brutto = dingguo.Sum(
-                float(order_match_groups['price']),
-                order_match_groups['currency'],
-                ),
-            passenger = dingguo.Person(
-                first_name = order_match_groups['passenger_first_name'],
-                last_name = order_match_groups['passenger_last_name'],
-                ),
-            valid_from = datetime.datetime.strptime(order_match_groups['valid_from'], '%d %B %Y %H:%M')
-                .replace(tzinfo = timezone),
-            valid_until = datetime.datetime.strptime(order_match_groups['valid_until'], '%d %B %Y %H:%M')
-                .replace(tzinfo = timezone) if order_match_groups['valid_until'] else None,
-            ticket_url = ticket_url_match.group(1) if ticket_url_match else None,
-            ))
-
-    return [order]

+ 0 - 78
dingguo/parser/yipbee.py

@@ -1,78 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import BeautifulSoup
-import datetime
-import dateutil.parser
-import dingguo
-import email
-import ioex
-import re
-
-def parse_order_confirmation_mail(mail):
-
-    assert isinstance(mail, email.message.Message)
-
-    text = mail.get_payload()[0].get_payload()[0].get_payload(decode = True).decode('utf-8')
-    if not u'Vielen Dank für deine Bestellung bei yipbee' in text:
-        raise Exception('no yipbee confirmation')
-
-    order_match_groups = re.search(
-        ur'[\W\w]+'
-            + ur'BESTELLUNG: (?P<order_id>\w+) vom (?P<order_time>\d\d.\d\d.\d{4} \d\d:\d\d:\d\d)'
-            + ur'[\W\w]+'
-            + ur'GESAMTPREIS\s+'
-            + ur'(?P<articles_and_discount_text>[\W\w]+)'
-            + ur'(?P<summary_text>ARTIKEL [\W\w]+)',
-        text,
-        re.UNICODE
-        ).groupdict()
-
-    order = dingguo.Order(
-        u'yipbee',
-        order_match_groups['order_id'],
-        datetime.datetime.strptime(order_match_groups['order_time'], '%d.%m.%Y %H:%M:%S')
-            .replace(tzinfo = dateutil.parser.parse(mail['Date']).tzinfo),
-        )
-
-    for article_match in re.finditer(
-            ur'(?P<name>[\w\-\.\:,%\(\) ]+ (Klasse \d|[\w\-\. ]+[^\d ]))'
-                + ur'(?P<total_price>\d+,\d\d) €(?P<quantity>\d)(?P<total_price_2>\d+,\d\d) €',
-            order_match_groups['articles_and_discount_text'].replace('\n', ' '),
-            re.UNICODE,
-            ):
-        article_match_groups = article_match.groupdict()
-        total_price = float(article_match_groups['total_price'].replace(',', '.'))
-        total_price_2 = float(article_match_groups['total_price_2'].replace(',', '.'))
-        assert abs(total_price - total_price_2) < 0.01, 'expected %f, received %f' % (total_price, total_price_2)
-        quantity = int(article_match_groups['quantity'])
-        order.items.append(dingguo.Article(
-            name = article_match_groups['name'],
-            price_brutto = dingguo.Sum(round(total_price / quantity, 2), u'EUR'),
-            quantity = quantity,
-            reseller = u'yipbee',
-            shipper = u'yipbee',
-            ))
-
-    articles_price = float(text.split('RABATTE')[0].split('ARTIKEL')[-1].strip().split(' ')[0].replace(',', '.'))
-    assert abs(articles_price - sum([a.price_brutto.value * a.quantity for a in order.items])) < 0.01
-
-    discount_tag = BeautifulSoup.BeautifulSoup(
-        order_match_groups['articles_and_discount_text'],
-        convertEntities = BeautifulSoup.BeautifulSoup.HTML_ENTITIES,
-        ).find('tr')
-    if discount_tag:
-        name_tag, value_tag = discount_tag.findAll('td', recursive = False)
-        value, currency = value_tag.text.split(' ')
-        order.discounts.append(dingguo.Discount(
-            name = name_tag.text,
-            amount = dingguo.Sum(float(value.replace(',', '.')) * -1, currency),
-            ))
-
-    delivery_price = order_match_groups['summary_text'].split('VERSAND')[1].split('STEUERN')[0].strip()
-    delivery_price_value, delivery_price_currency = delivery_price.split(' ')
-    order.items.append(dingguo.Item(
-        name = u'Delivery',
-        price_brutto = dingguo.Sum(float(delivery_price_value.replace(',', '.')), delivery_price_currency),
-        ))
-
-    return [order]

+ 451 - 0
finoex/__init__.py

@@ -0,0 +1,451 @@
+import datetime
+import ioex.calcex
+import locale
+import re
+import yaml
+
+
+class _Object(object):
+
+    def __eq__(self, other):
+        return type(self) == type(other) and vars(self) == vars(other)
+
+    def __ne__(self, other):
+        return not (self == other)
+
+
+class _YamlInitConstructor(yaml.YAMLObject):
+
+    @classmethod
+    def from_yaml(cls, loader, node):
+        return cls(**loader.construct_mapping(node, deep=True))
+        # return cls(**{
+        #     k: unicode(v) if isinstance(v, str) else v
+        #         for (k, v) in loader.construct_mapping(node, deep = True).items()
+        #     })
+
+    @classmethod
+    def register_yaml_constructor(cls, loader, tag=None):
+        loader.add_constructor(
+            cls.yaml_tag if tag is None else tag,
+            cls.from_yaml,
+        )
+
+
+class Sum(ioex.calcex.Figure):
+
+    currency_symbol_map = {
+        '€': 'EUR',
+        'US$': 'USD',
+        '¥': 'CNY',
+    }
+
+    yaml_tag = u'!sum'
+
+    def __init__(self, value=None, currency=None, unit=None):
+        if not currency is None and not unit is None:
+            raise ValueError('missing currency')
+        else:
+            unit = currency if currency else unit
+            super(Sum, self).__init__(
+                value=value,
+                unit=currency if currency else unit,
+            )
+
+    def get_value(self):
+        return super(Sum, self).get_value()
+
+    def set_value(self, value):
+        assert type(value) is float
+        super(Sum, self).set_value(value)
+
+    """ use property() instead of decorator to enable overriding """
+    value = property(get_value, set_value)
+
+    def get_unit(self):
+        return super(Sum, self).get_unit()
+
+    def set_unit(self, currency):
+        currency = Sum.currency_symbol_map.get(currency, currency)
+        assert type(currency) is str
+        super(Sum, self).set_unit(currency)
+
+    """ use property() instead of decorator to enable overriding """
+    unit = property(get_unit, set_unit)
+    currency = property(get_unit, set_unit)
+
+    value_regex = r"-?\d+([\.,]\d{2})?"
+    currency_regex = r"[^\d\s-]+"
+
+    @staticmethod
+    def parse_text(text):
+        match = re.search(
+            r'^\$?(?P<value>{}) (?P<curr>{})$'.format(
+                Sum.value_regex, Sum.currency_regex),
+            text,
+            re.UNICODE,
+        )
+        if not match:
+            match = re.search(
+                r'^(?P<curr>{}) ?(?P<value>{})$'.format(
+                    Sum.currency_regex, Sum.value_regex),
+                text,
+                re.UNICODE,
+            )
+        assert not match is None, text
+        attr = match.groupdict()
+        return Sum(
+            value=locale.atof(attr['value']),
+            currency=attr['curr'],
+        )
+
+
+class Order(_Object, _YamlInitConstructor):
+
+    yaml_tag = u'!order'
+
+    def __init__(self, platform, order_id, order_date,
+                 customer_id=None,
+                 items=None,
+                 discounts=None,
+                 ):
+        assert type(platform) is str
+        self.platform = platform
+        if type(order_id) in [int]:
+            order_id = str(order_id)
+        assert type(order_id) is str
+        self.order_id = order_id
+        assert type(order_date) in [datetime.date, datetime.datetime]
+        if type(order_date) is datetime.datetime and order_date.tzinfo:
+            order_date = order_date.astimezone(pytz.utc)
+        self.order_date = order_date
+        if customer_id is not None:
+            assert type(customer_id) is str
+            self.customer_id = customer_id
+        if items is None:
+            self.items = []
+        else:
+            assert type(items) is list
+            assert all([isinstance(i, Item) for i in items])
+            self.items = items
+        if discounts is None:
+            self.discounts = []
+        else:
+            assert type(discounts) is list
+            assert all([isinstance(d, Discount) for d in discounts])
+            self.discounts = discounts
+
+
+class Distance(ioex.calcex.Figure):
+
+    yaml_tag = u'!distance'
+
+    def get_value(self):
+        return super(Distance, self).get_value()
+
+    def set_value(self, value):
+        assert type(value) is float
+        super(Distance, self).set_value(value)
+
+    """ use property() instead of decorator to enable overriding """
+    value = property(get_value, set_value)
+
+    @property
+    def metres(self):
+        if self.unit == 'm':
+            return Distance(self.value, 'm')
+        elif self.unit == 'km':
+            return Distance(self.value * 1000, 'm')
+        else:
+            raise Exception()
+
+
+class Discount(_Object, _YamlInitConstructor):
+
+    yaml_tag = u'!discount'
+
+    def __init__(self, name=None, amount=None):
+        assert type(name) is str
+        self.name = name
+        assert type(amount) is Sum
+        assert amount.value >= 0
+        self.amount = amount
+
+
+class Item(_Object, _YamlInitConstructor):
+
+    yaml_tag = u'!item'
+
+    def __init__(self,
+                 name=None,
+                 price_brutto=None,
+                 ):
+        if not name is None:
+            assert type(name) is str
+            self.name = name
+        assert type(price_brutto) is Sum
+        self.price_brutto = price_brutto
+
+
+class Campaign(_Object, _YamlInitConstructor):
+
+    yaml_tag = u'!campaign'
+
+    def __init__(self,
+                 end=None,
+                 founder=None,
+                 name=None,
+                 website_url=None,
+                 ):
+        assert type(name) is str
+        self.name = name
+        assert type(founder) is str
+        self.founder = founder
+        if not end is None:
+            assert type(end) is datetime.datetime
+            assert not end.tzinfo is None, '%r' % end
+            self.end = end
+        if not website_url is None:
+            assert type(website_url) is str
+            self.website_url = website_url
+
+
+class Pledge(Item):
+
+    yaml_tag = u'!pledge'
+
+    def __init__(self,
+                 campaign=None,
+                 reward=None,
+                 **kwargs
+                 ):
+        super(Pledge, self).__init__(**kwargs)
+        assert type(campaign) is Campaign
+        self.campaign = campaign
+        if not reward is None:
+            assert type(reward) is str
+            self.reward = reward
+
+
+class Contribution(Item):
+
+    yaml_tag = u'!contribution'
+
+    def __init__(self,
+                 campaign=None,
+                 reward=None,
+                 **kwargs
+                 ):
+        super(Contribution, self).__init__(**kwargs)
+        assert type(campaign) is Campaign
+        self.campaign = campaign
+        if not reward is None:
+            assert type(reward) is str
+            self.reward = reward
+
+
+class Article(Item):
+
+    yaml_tag = u'!article'
+
+    def __init__(self,
+                 authors=None,
+                 color=None,
+                 delivery_date=None,
+                 depth=None,
+                 features=None,
+                 height=None,
+                 maximum_load=None,
+                 option=None,
+                 product_id=None,
+                 quantity=None,
+                 reseller=None,
+                 shipper=None,
+                 size=None,
+                 state=None,
+                 width=None,
+                 **kwargs
+                 ):
+        super(Article, self).__init__(**kwargs)
+        assert not self.name is None
+        assert type(quantity) is int
+        self.quantity = quantity
+        if authors is not None:
+            assert type(authors) is list
+            self.authors = authors
+        if state is not None:
+            assert type(state) is str
+            self.state = state
+        if reseller is not None:
+            assert type(reseller) is str
+            self.reseller = reseller
+        if shipper is not None:
+            assert type(shipper) is str
+            self.shipper = shipper
+        if product_id is not None:
+            if type(product_id) in [int]:
+                product_id = str(product_id)
+            assert type(product_id) is str
+            self.product_id = product_id
+        if option is not None:
+            assert type(option) is str
+            self.option = option
+        if color is not None:
+            assert type(color) is str
+            self.color = color
+        if size is not None:
+            assert type(size) is str
+            self.size = size
+        if width is not None:
+            assert type(width) is Distance
+            self.width = width
+        if depth is not None:
+            assert type(depth) is Distance
+            self.depth = depth
+        if height is not None:
+            assert type(height) is Distance
+            self.height = height
+        if maximum_load is not None:
+            assert type(maximum_load) is ioex.calcex.Figure, type(maximum_load)
+            self.maximum_load = maximum_load
+        if features is not None:
+            assert type(features) is str
+            self.features = features
+        assert delivery_date is None or type(delivery_date) is datetime.date
+        self.delivery_date = delivery_date
+
+
+class Service(Item):
+
+    yaml_tag = u'!service'
+
+    def __init__(self,
+                 duration=None,
+                 **kwargs
+                 ):
+        super(Service, self).__init__(**kwargs)
+        if duration is not None:
+            assert type(duration) is ioex.datetimeex.Duration
+            self.duration = duration
+
+
+class Transportation(Item):
+
+    yaml_tag = u'!transportation'
+
+    def __init__(self,
+                 departure_point=None,
+                 destination_point=None,
+                 distance=None,
+                 estimated_arrival_time=None,
+                 passenger=None,
+                 route_map=None,
+                 ticket_url=None,
+                 valid_from=None,
+                 valid_until=None,
+                 **kwargs
+                 ):
+        super(Transportation, self).__init__(**kwargs)
+        if departure_point is not None:
+            assert type(departure_point) is str
+            self.departure_point = departure_point
+        if destination_point is not None:
+            assert type(destination_point) is str
+            self.destination_point = destination_point
+        if distance is not None:
+            assert type(distance) is Distance
+            self.distance = distance
+        if route_map is not None:
+            assert type(route_map) is str
+            self.route_map = route_map
+        if passenger is not None:
+            assert type(passenger) is Person
+            self.passenger = passenger
+        if valid_from is not None:
+            assert type(valid_from) is datetime.datetime
+            assert not valid_from.tzinfo is None
+            self.valid_from = valid_from
+        if valid_until is not None:
+            assert type(valid_until) is datetime.datetime
+            assert not valid_until.tzinfo is None
+            self.valid_until = valid_until
+        if ticket_url is not None:
+            assert type(ticket_url) is str
+            self.ticket_url = ticket_url
+        if estimated_arrival_time is not None:
+            assert type(estimated_arrival_time) is ioex.datetimeex.Period
+            assert not estimated_arrival_time.start.tzinfo is None
+            assert not estimated_arrival_time.end.tzinfo is None
+            self.estimated_arrival_time = estimated_arrival_time
+
+
+class Shipping(Transportation):
+
+    yaml_tag = u'!shipping'
+
+    def __init__(self,
+                 **kwargs
+                 ):
+        super(Shipping, self).__init__(**kwargs)
+
+
+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 str
+        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
+
+
+class Person(_Object, _YamlInitConstructor):
+
+    yaml_tag = u'!person'
+
+    def __init__(self, first_name=None, last_name=None):
+        self.first_name = first_name
+        self.last_name = last_name
+
+    @property
+    def first_name(self):
+        return self._first_name
+
+    @first_name.setter
+    def first_name(self, first_name):
+        assert first_name is None or type(first_name) is str
+        self._first_name = first_name
+
+    @property
+    def last_name(self):
+        return self._last_name
+
+    @last_name.setter
+    def last_name(self, last_name):
+        assert last_name is None or type(last_name) is str
+        self._last_name = last_name
+
+    @classmethod
+    def to_yaml(cls, dumper, person):
+        return dumper.represent_mapping(cls.yaml_tag, {
+            'first_name': person.first_name,
+            'last_name': person.last_name,
+        })
+
+    def __repr__(self):
+        return self.__class__.__name__ + '(%s)' % ', '.join([
+            '%s=%r' % (k, v) for k, v in vars(self).items()
+        ])

+ 0 - 33
scripts/ingdiba-pdf-to-text

@@ -1,33 +0,0 @@
-#!/usr/bin/env python
-# PYTHON_ARGCOMPLETE_OK
-
-import sys
-import dingguo.parser.ingdiba
-
-def compute(file):
-    
-    sys.stdout.write(dingguo.parser.ingdiba.ingdiba_pdf_file_to_text(file).encode('utf-8'))
-
-def _init_argparser():
-
-    import argparse
-    argparser = argparse.ArgumentParser(description = None)
-    argparser.add_argument('file', type = argparse.FileType('rb'))
-    return argparser
-
-def main(argv):
-
-    argparser = _init_argparser()
-    try:
-        import argcomplete
-        argcomplete.autocomplete(argparser)
-    except ImportError:
-        pass
-    args = argparser.parse_args(argv)
-
-    compute(**vars(args))
-
-    return 0
-
-if __name__ == "__main__":
-    sys.exit(main(sys.argv[1:]))

+ 0 - 71
scripts/order-confirmation-mail-parser

@@ -1,71 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# PYTHON_ARGCOMPLETE_OK
-
-import dingguo.parser
-import email
-import os
-import shutil
-import sys
-import yaml
-
-def compute(mail_path, catalogue, register_path):
-
-    orders = []
-
-    if mail_path:
-        for p in mail_path:
-            with open(p, 'r') as mail:
-                mail_orders = dingguo.parser.parse_order_confirmation_mail(
-                        email.message_from_file(mail)
-                        )
-                orders += mail_orders
-            if catalogue:
-                for order in mail_orders:
-                    order_dir_path = os.path.join(order.platform, order.order_id)
-                    if not os.path.isdir(order_dir_path):
-                        os.makedirs(order_dir_path)
-                    shutil.copyfile(p, os.path.join(order_dir_path, os.path.basename(p)))
-                os.remove(p)
-    else:
-        msg = email.message_from_string(sys.stdin.read())
-        orders += dingguo.parser.parse_order_confirmation_mail(msg)
-
-    if register_path:
-        if os.path.exists(register_path):
-            with open(register_path, 'r') as register_file:
-                registry = yaml.load(register_file.read().decode('utf-8'))
-        else:
-            registry = dingguo.OrderRegistry()
-        for order in orders:
-            registry.register(order)
-        with open(register_path, 'w') as register:
-            register.write(yaml.dump(registry, default_flow_style = False, allow_unicode = True))
-        
-    print(yaml.dump(orders, default_flow_style = False, allow_unicode = True))
-
-def _init_argparser():
-
-    import argparse
-    argparser = argparse.ArgumentParser(description = None)
-    argparser.add_argument('--register', metavar = 'path', dest = 'register_path')
-    argparser.add_argument('--catalogue', action='store_true')
-    argparser.add_argument('mail_path', nargs = '*')
-    return argparser
-
-def main(argv):
-
-    argparser = _init_argparser()
-    try:
-        import argcomplete
-        argcomplete.autocomplete(argparser)
-    except ImportError:
-        pass
-    args = argparser.parse_args(argv)
-
-    compute(**vars(args))
-
-    return 0
-
-if __name__ == "__main__":
-    sys.exit(main(sys.argv[1:]))

+ 7 - 11
setup.py

@@ -3,23 +3,19 @@ from setuptools import setup
 import glob
 
 setup(
-    name = 'dingguo',
-    # version = '',
+    name = 'finoex',
+    version = '0.1',
     # description = '',
     author = 'Fabian Peter Hammerle',
     author_email = 'fabian.hammerle@gmail.com',
-    # url = '',
-    # download_url = '',
+    url = 'https://git.hammerle.me/fphammerle/finoex',
+    download_url = 'https://git.hammerle.me/fphammerle/finoex/archive/0.1.tar.gz',
     # keywords = [],
     # classifiers = [],
-    packages = [
-        'dingguo',
-        'dingguo.parser',
-        ],
-    scripts = glob.glob('scripts/*'),
+    packages = ['finoex'],
+    # scripts = glob.glob('scripts/*'),
     install_requires = [
-        'ioex>=0.7',
-        'pdfminer>=20140328', 
+        'ioex>=0.10.1',
         'pytz',
         ],
     tests_require = ['pytest'],

+ 0 - 24
tests/parser/test_amazon.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.amazon
-import email
-import glob
-import ioex
-import os
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'amazon')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.amazon.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            u'\n%s' % ioex.yaml_diff(expected_orders, parsed_orders, colors = True)

+ 0 - 24
tests/parser/test_amazon_cn.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.amazon_cn
-import email
-import glob
-import ioex
-import os
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'amazon.cn')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.amazon_cn.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            u'\n%s' % ioex.yaml_diff(expected_orders, parsed_orders, colors = True)

+ 0 - 24
tests/parser/test_banggood.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.banggood
-import email
-import glob
-import ioex
-import os
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'banggood')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.banggood.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            u'\n%s' % ioex.yaml_diff(expected_orders, parsed_orders, colors = True)

+ 0 - 24
tests/parser/test_indiegogo.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.indiegogo
-import email
-import glob
-import ioex
-import os
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'indiegogo')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.indiegogo.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            u'\n%s' % ioex.yaml_diff(expected_orders, parsed_orders, colors = True)

+ 0 - 132
tests/test_.py

@@ -1,132 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import copy
-import datetime
-import dingguo
-import os
-import yaml
-
-def test_sum_init_eur():
-    assert dingguo.Sum(1.23, u'EUR') == dingguo.Sum(1.23, u'€')
-
-def test_sum_init_usd():
-    assert dingguo.Sum(1.23, u'USD') == dingguo.Sum(1.23, u'US$')
-
-def test_distance_metres():
-    assert int(dingguo.Distance(1.23, u'km').metres) == 1230
-
-def test_person_first_name():
-    p = dingguo.Person()
-    p.first_name = u'äbc'
-    assert p.first_name == u'äbc'
-    p.first_name = u'qrßtuvw'
-    assert p.first_name == u'qrßtuvw'
-
-def test_person_first_name_string():
-    p = dingguo.Person()
-    with pytest.raises(Exception):
-        p.first_name = 'äbc'
-
-def test_person_last_name():
-    p = dingguo.Person()
-    p.last_name = u'歌曲'
-    assert p.last_name == u'歌曲'
-    p.last_name = u'bär'
-    assert p.last_name == u'bär'
-
-def test_person_last_name_string():
-    p = dingguo.Person()
-    with pytest.raises(Exception):
-        p.last_name = 'äbc'
-
-@pytest.mark.parametrize(('text', 'sum'), [
-    [u'$-1,23 USD',  dingguo.Sum(-1.23, u'USD')],
-    [u'$-1.23 USD',  dingguo.Sum(-1.23, u'USD')],
-    [u'$-30 USD',    dingguo.Sum(-30.0, u'USD')],
-    [u'$-30,00 USD', dingguo.Sum(-30.0, u'USD')],
-    [u'$-30.00 USD', dingguo.Sum(-30.0, u'USD')],
-    [u'$-8',         dingguo.Sum(-8.0,  u'USD')],
-    [u'$-8,00',      dingguo.Sum(-8.0,  u'USD')],
-    [u'$-8.00',      dingguo.Sum(-8.0,  u'USD')],
-    [u'$1,23 USD',   dingguo.Sum(1.23,  u'USD')],
-    [u'$1.23 USD',   dingguo.Sum(1.23,  u'USD')],
-    [u'$30 USD',     dingguo.Sum(30.0,  u'USD')],
-    [u'$30,00 USD',  dingguo.Sum(30.0,  u'USD')],
-    [u'$30.00 USD',  dingguo.Sum(30.0,  u'USD')],
-    [u'$8',          dingguo.Sum(8.0,   u'USD')],
-    [u'$8,00',       dingguo.Sum(8.0,   u'USD')],
-    [u'$8.00',       dingguo.Sum(8.0,   u'USD')],
-    [u'US$-0.50',    dingguo.Sum(-0.5,  u'USD')],
-    [u'US$0.50',     dingguo.Sum(0.5,   u'USD')],
-    [u'€-0.25',      dingguo.Sum(-0.25, u'EUR')],
-    [u'€1.20',       dingguo.Sum(1.2,   u'EUR')],
-    [u'¥ 4.27',     dingguo.Sum(4.27,  u'CNY')],
-    ])
-def test_sum_parse_text(text, sum):
-    assert dingguo.Sum.parse_text(text) == sum
-
-@pytest.mark.parametrize(('text'), [
-    u'pre$1.23 USD',
-    u'$1.23 USDpost',
-    u'$1#23 USD',
-    u'1#23 USD',
-    ])
-def test_sum_parse_text_fail(text):
-    with pytest.raises(Exception):
-        dingguo.Sum.parse_text(text)
-
-@pytest.mark.parametrize(('minuend', 'subtrahend', 'difference'), [
-    [dingguo.Sum(5.0, u'USD'), dingguo.Sum(2.0, u'USD'), dingguo.Sum(3.0, u'USD')],
-    [dingguo.ScalarFigure(5.0, u'cm'), dingguo.ScalarFigure(2.0, u'cm'), dingguo.ScalarFigure(3.0, u'cm')],
-    [dingguo.ScalarFigure(1.0, u'kg'), dingguo.ScalarFigure(2.0, u'kg'), dingguo.ScalarFigure(-1.0, u'kg')],
-    ])
-def test_figure_sub(minuend, subtrahend, difference):
-    minuend_copy = copy.deepcopy(minuend)
-    subtrahend_copy = copy.deepcopy(subtrahend)
-    assert (minuend - subtrahend) == difference
-    assert minuend_copy == minuend
-    assert subtrahend_copy == subtrahend
-
-@pytest.mark.parametrize(('minuend', 'subtrahend'), [
-    [dingguo.Sum(5.0, u'USD'), dingguo.Sum(2.0, u'EUR')],
-    [dingguo.ScalarFigure(5.0, u'cm'), dingguo.ScalarFigure(2.0, u'kg')],
-    ])
-def test_figure_sub_fail(minuend, subtrahend):
-    with pytest.raises(Exception):
-        (minuend - subtrahend)
-
-@pytest.mark.parametrize(('factor_a', 'factor_b', 'product'), [
-    [dingguo.Sum(5.0, u'USD'), 1.5, dingguo.Sum(7.5, u'USD')],
-    [dingguo.Sum(5.0, u'USD'), 2, dingguo.Sum(10.0, u'USD')],
-    [dingguo.ScalarFigure(5.0, u'cm'), -0.5, dingguo.ScalarFigure(-2.5, u'cm')],
-    [dingguo.ScalarFigure(1.0, u'kg'), 10, dingguo.ScalarFigure(10.0, u'kg')],
-    ])
-def test_scalar_figure_mul(factor_a, factor_b, product):
-    factor_a_copy = copy.deepcopy(factor_a)
-    factor_b_copy = copy.deepcopy(factor_b)
-    assert (factor_a * factor_b) == product
-    assert factor_a_copy == factor_a
-    assert factor_b_copy == factor_b
-
-@pytest.mark.parametrize(('dividend', 'divisor', 'quotient'), [
-    [dingguo.Sum(5.0, u'USD'), 2.5, dingguo.Sum(2.0, u'USD')],
-    [dingguo.Sum(5.0, u'USD'), 2, dingguo.Sum(2.5, u'USD')],
-    [dingguo.ScalarFigure(5.0, u'cm'), -0.5, dingguo.ScalarFigure(-10.0, u'cm')],
-    [dingguo.ScalarFigure(1.0, u'kg'), 10, dingguo.ScalarFigure(0.1, u'kg')],
-    ])
-def test_scalar_figure_div(dividend, divisor, quotient):
-    dividend_copy = copy.deepcopy(dividend)
-    divisor_copy = copy.deepcopy(divisor)
-    assert (dividend / divisor) == quotient
-    assert dividend_copy == dividend
-    assert divisor_copy == divisor
-
-@pytest.mark.parametrize(('factor_a', 'factor_b'), [
-    [dingguo.Sum(5.0, u'USD'), dingguo.Sum(2.0, u'EUR')],
-    [dingguo.ScalarFigure(5.0, u'cm'), dingguo.ScalarFigure(2.0, u'cm')],
-    ])
-def test_figure_mul_fail(factor_a, factor_b):
-    with pytest.raises(Exception):
-        (factor_a * factor_b)

+ 109 - 89
tests/test_comparison.py

@@ -3,58 +3,65 @@
 import pytest
 
 import datetime
-import dingguo
+import finoex
 import ioex.datetimeex
 import os
 import pprint
 import pytz
 import yaml
 
+
 def get_item_a():
-    return dingguo.Item(
-            name = u'item',
-            price_brutto = dingguo.Sum(1.0, u'EUR'),
-            )
+    return finoex.Item(
+        name=u'item',
+        price_brutto=finoex.Sum(1.0, u'EUR'),
+    )
+
 
 def get_item_b():
-    return dingguo.Item(
-            name = u'item',
-            price_brutto = dingguo.Sum(2.0, u'EUR'),
-            )
+    return finoex.Item(
+        name=u'item',
+        price_brutto=finoex.Sum(2.0, u'EUR'),
+    )
+
 
 def get_service_a():
-    return dingguo.Service(
-            name = u'service',
-            price_brutto = dingguo.Sum(1.0, u'EUR'),
-            duration = ioex.datetimeex.Duration(years = 2),
-            )
+    return finoex.Service(
+        name=u'service',
+        price_brutto=finoex.Sum(1.0, u'EUR'),
+        duration=ioex.datetimeex.Duration(years=2),
+    )
+
 
 def get_service_b():
-    return dingguo.Service(
-            name = u'service',
-            price_brutto = dingguo.Sum(1.0, u'EUR'),
-            duration = ioex.datetimeex.Duration(),
-            )
+    return finoex.Service(
+        name=u'service',
+        price_brutto=finoex.Sum(1.0, u'EUR'),
+        duration=ioex.datetimeex.Duration(),
+    )
+
 
 def get_discount_a():
-    return dingguo.Discount(
-            name = u'discount',
-            amount = dingguo.Sum(1.0, u'EUR'),
-            )
+    return finoex.Discount(
+        name=u'discount',
+        amount=finoex.Sum(1.0, u'EUR'),
+    )
+
 
 def get_discount_b():
-    return dingguo.Discount(
-            name = u'discount',
-            amount = dingguo.Sum(2.0, u'EUR'),
-            )
-
-def get_order_a(items = True):
-    order = dingguo.Order(
-            platform = u'platform',
-            order_id = u'id',
-            order_date = datetime.datetime(2016, 5, 8, 0, 18, 17),
-            customer_id = u'customer',
-            )
+    return finoex.Discount(
+        name=u'discount',
+        amount=finoex.Sum(2.0, u'EUR'),
+    )
+
+
+def get_order_a(items=True):
+    order = finoex.Order(
+        platform=u'platform',
+        order_id=u'id',
+        order_date=datetime.datetime(2016, 5, 8, 0, 18, 17),
+        customer_id=u'customer',
+    )
     if items:
         order.items.append(get_item_a())
         order.items.append(get_item_b())
@@ -62,24 +69,26 @@ def get_order_a(items = True):
     order.discounts.append(get_discount_b())
     return order
 
+
 def get_order_b():
-    order = dingguo.Order(
-            platform = u'platform',
-            order_id = u'id',
-            order_date = datetime.datetime(2016, 5, 8, 0, 18, 17),
-            )
+    order = finoex.Order(
+        platform=u'platform',
+        order_id=u'id',
+        order_date=datetime.datetime(2016, 5, 8, 0, 18, 17),
+    )
     order.items.append(get_item_a())
     order.items.append(get_item_b())
     order.discounts.append(get_discount_a())
     order.discounts.append(get_discount_b())
     return order
 
+
 def get_order_c():
-    order = dingguo.Order(
-            platform = u'platform',
-            order_id = u'id',
-            order_date = datetime.datetime(2016, 5, 8, 0, 18, 17),
-            )
+    order = finoex.Order(
+        platform=u'platform',
+        order_id=u'id',
+        order_date=datetime.datetime(2016, 5, 8, 0, 18, 17),
+    )
     order.items.append(get_item_a())
     order.items.append(get_item_b())
     order.items.append(get_item_b())
@@ -87,64 +96,73 @@ def get_order_c():
     order.discounts.append(get_discount_b())
     return order
 
+
 def get_campaign_a():
-    return dingguo.Campaign(
-            name = u'campaign a',
-            founder = u'company',
-            end = datetime.datetime(2016, 7, 23, 9, 23, 17, tzinfo = pytz.timezone('Europe/Vienna')),
-            )
+    return finoex.Campaign(
+        name=u'campaign a',
+        founder=u'company',
+        end=datetime.datetime(2016, 7, 23, 9, 23, 17, tzinfo=pytz.timezone('Europe/Vienna')),
+    )
+
 
 def get_campaign_b():
-    return dingguo.Campaign(
-            name = u'campaign b',
-            founder = u'company',
-            end = datetime.datetime(2016, 7, 23, 9, 23, 17, tzinfo = pytz.timezone('Europe/Vienna')),
-            )
+    return finoex.Campaign(
+        name = u'campaign b',
+        founder = u'company',
+        end = datetime.datetime(2016, 7, 23, 9, 23, 17, tzinfo = pytz.timezone('Europe/Vienna')),
+    )
 
 def get_pledge_a():
-    return dingguo.Pledge(
-            campaign = get_campaign_a(),
-            price_brutto = dingguo.Sum(10.0, u'EUR'),
-            )
+    return finoex.Pledge(
+        campaign=get_campaign_a(),
+        price_brutto=finoex.Sum(10.0, u'EUR'),
+    )
+
 
 def get_pledge_b():
-    return dingguo.Pledge(
-            campaign = get_campaign_a(),
-            price_brutto = dingguo.Sum(4.0, u'EUR'),
-            )
+    return finoex.Pledge(
+        campaign=get_campaign_a(),
+        price_brutto=finoex.Sum(4.0, u'EUR'),
+    )
+
 
 def get_contribution_a():
-    return dingguo.Contribution(
-            campaign = get_campaign_a(),
-            price_brutto = dingguo.Sum(10.0, u'EUR'),
-            )
+    return finoex.Contribution(
+        campaign=get_campaign_a(),
+        price_brutto=finoex.Sum(10.0, u'EUR'),
+    )
+
 
 def get_contribution_b():
-    return dingguo.Contribution(
-            campaign = get_campaign_a(),
-            price_brutto = dingguo.Sum(4.0, u'EUR'),
-            )
+    return finoex.Contribution(
+        campaign=get_campaign_a(),
+        price_brutto=finoex.Sum(4.0, u'EUR'),
+    )
+
 
 def get_person_a():
-    return dingguo.Person(
-        first_name = u'Fabian Peter',
-        last_name = u'Hammerle',
-        )
+    return finoex.Person(
+        first_name=u'Fabian Peter',
+        last_name=u'Hammerle',
+    )
+
 
 def get_person_b():
-    return dingguo.Person(
-        first_name = u'名字',
-        last_name = u'贵姓',
-        )
+    return finoex.Person(
+        first_name=u'名字',
+        last_name=u'贵姓',
+    )
+
 
 def get_shipping():
-    return dingguo.Shipping(
-        price_brutto = dingguo.Sum(10.0, u'EUR'),
-        destination_point = u'home',
-        )
+    return finoex.Shipping(
+        price_brutto=finoex.Sum(10.0, u'EUR'),
+        destination_point=u'home',
+    )
+
 
 @pytest.mark.parametrize('a,b', [
-    [dingguo.Figure(1, u'mm'), dingguo.Figure(1, u'mm')],
+    [ioex.calcex.Figure(1, u'mm'), ioex.calcex.Figure(1, u'mm')],
     [get_campaign_a(), get_campaign_a()],
     [get_campaign_b(), get_campaign_b()],
     [get_contribution_a(), get_contribution_a()],
@@ -163,26 +181,27 @@ def get_shipping():
     [get_service_a(), get_service_a()],
     [get_service_b(), get_service_b()],
     [get_shipping(), get_shipping()],
-    ])
+])
 def test_eq(a, b):
     assert a == b, '\n'.join([
         'a = %r' % a,
         'b = %r' % b,
         'vars(a) = %s' % pprint.pformat(vars(a)),
         'vars(b) = %s' % pprint.pformat(vars(b)),
-        ])
+    ])
     assert b == a
     assert not a != b
     assert not b != a
 
+
 @pytest.mark.parametrize('a,b', [
-    [dingguo.Figure(1, u'mm'), dingguo.Figure(2, u'mm')],
+    [ioex.calcex.Figure(1, u'mm'), ioex.calcex.Figure(2, u'mm')],
     [get_contribution_a(), get_contribution_b()],
     [get_contribution_a(), get_item_b()],
     [get_discount_a(), get_discount_b()],
     [get_discount_a(), get_item_b()],
     [get_item_a(), get_item_b()],
-    [get_order_a(), get_order_a(items = False)],
+    [get_order_a(), get_order_a(items=False)],
     [get_order_a(), get_order_b()],
     [get_order_a(), get_order_c()],
     [get_order_b(), get_order_c()],
@@ -191,13 +210,14 @@ def test_eq(a, b):
     [get_pledge_a(), get_item_b()],
     [get_pledge_a(), get_pledge_b()],
     [get_service_a(), get_service_b()],
-    ])
+])
 def test_neq(a, b):
     assert a != b
     assert b != a
     assert not a == b
     assert not b == a
 
+
 def test_order_ne_items_reversed():
     order = get_order_a()
     order.items = order.items[::-1]

+ 16 - 7
tests/test_distance.py

@@ -1,7 +1,7 @@
 import pytest
 
 import copy
-import dingguo
+import finoex
 
 @pytest.mark.parametrize(('params', 'kwargs', 'expected_value', 'expected_unit'), [
     [[1.0, u'dm'], {}, 1.0, u'dm'],
@@ -9,7 +9,7 @@ import dingguo
     [[], {'value': 2.3, 'unit': u'cm'}, 2.3, u'cm'],
     ])
 def test_init(params, kwargs, expected_value, expected_unit):
-    f = dingguo.Distance(*params, **kwargs)
+    f = finoex.Distance(*params, **kwargs)
     assert type(f.value) == type(expected_value)
     assert type(f.unit) == type(expected_unit)
     assert f.value == expected_value
@@ -30,11 +30,11 @@ def test_init(params, kwargs, expected_value, expected_unit):
     ])
 def test_init_fail(params, kwargs):
     with pytest.raises(Exception):
-        dingguo.Distance(*params, **kwargs)
+        finoex.Distance(*params, **kwargs)
 
 @pytest.mark.parametrize(('factor_a', 'factor_b', 'product'), [
-    [dingguo.Distance(5.0, u'cm'), -0.5, dingguo.Distance(-2.5, u'cm')],
-    [dingguo.Distance(1.0, u'kg'), 10, dingguo.Distance(10.0, u'kg')],
+    [finoex.Distance(5.0, u'cm'), -0.5, finoex.Distance(-2.5, u'cm')],
+    [finoex.Distance(1.0, u'kg'), 10, finoex.Distance(10.0, u'kg')],
     ])
 def test_scalar_figure_mul(factor_a, factor_b, product):
     factor_a_copy = copy.deepcopy(factor_a)
@@ -44,9 +44,18 @@ def test_scalar_figure_mul(factor_a, factor_b, product):
     assert factor_b_copy == factor_b
 
 @pytest.mark.parametrize(('factor_a', 'factor_b'), [
-    [dingguo.Distance(5.0, u'cm'), dingguo.Distance(2.0, u'cm')],
-    [dingguo.Distance(5.0, u'cm'), '23'],
+    [finoex.Distance(5.0, u'cm'), finoex.Distance(2.0, u'cm')],
+    [finoex.Distance(5.0, u'cm'), '23'],
     ])
 def test_figure_mul_fail(factor_a, factor_b):
     with pytest.raises(Exception):
         (factor_a * factor_b)
+
+@pytest.mark.parametrize(('dist', 'expected_metres'), [
+    [finoex.Distance(12.3, 'm'), 12.3],
+    [finoex.Distance(12.3, 'km'), 12300],
+    ])
+def test_metres(dist, expected_metres):
+    metres = dist.metres
+    assert 'm' == metres.unit
+    assert expected_metres == metres.value

+ 0 - 27
tests/test_figure.py

@@ -1,27 +0,0 @@
-import pytest
-
-import dingguo
-
-@pytest.mark.parametrize(('params', 'kwargs', 'expected_value', 'expected_unit'), [
-    [[1, u'cm'], {}, 1, u'cm'],
-    [[1], {'unit': u'cm'}, 1, u'cm'],
-    [[], {'unit': u'cm'}, None, u'cm'],
-    [[], {'value': 1.0, 'unit': u'cm'}, 1.0, u'cm'],
-    [[], {'value': (3, -1), 'unit': u'cm'}, (3, -1), u'cm'],
-    [[(-1, 3), u'cm'], {}, (-1, 3), u'cm'],
-    ])
-def test_init(params, kwargs, expected_value, expected_unit):
-    f = dingguo.Figure(*params, **kwargs)
-    assert type(f.value) == type(expected_value)
-    assert type(f.unit) == type(expected_unit)
-    assert f.value == expected_value
-    assert f.unit == expected_unit
-
-@pytest.mark.parametrize(('params', 'kwargs'), [
-    [[], {}],
-    [[1, 'cm'], {}],
-    [[1], {'unit': 'cm'}],
-    ])
-def test_init_fail(params, kwargs):
-    with pytest.raises(Exception):
-        dingguo.Figure(*params, **kwargs)

+ 0 - 40
tests/test_parser.py

@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser
-import email
-import os
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data')
-
-@pytest.mark.parametrize('platform,mail_path', [
-    ('amazon.de', os.path.join(test_data_path, 'amazon', 'mail_1.eml')),
-    ('amazon.cn', os.path.join(test_data_path, 'amazon.cn', '1.eml')),
-    ('banggood', os.path.join(test_data_path, 'banggood', '1.eml')),
-    ('hm', os.path.join(test_data_path, 'hm', '1.eml')),
-    ('ikea', os.path.join(test_data_path, 'ikea', '1.eml')),
-    ('indiegogo', os.path.join(test_data_path, 'indiegogo', 'mail_without_reward.eml')),
-    ('kickstarter', os.path.join(test_data_path, 'kickstarter', 'mail_orbitkey.eml')),
-    ('lieferservice.at', os.path.join(test_data_path, 'lieferservice.at', 'mail_1.eml')),
-    ('mytaxi', os.path.join(test_data_path, 'mytaxi', 'mail_1.eml')),
-    ('namecheap', os.path.join(test_data_path, 'namecheap', 'mail_1_year_domain_renewal.eml')),
-    ('oebb', os.path.join(test_data_path, 'oebb', 'mail_1.eml')),
-    ('thomann', os.path.join(test_data_path, 'thomann', '1.eml')),
-    ('uber', os.path.join(test_data_path, 'uber', 'mail_1.eml')),
-    ('wiener_linien', os.path.join(test_data_path, 'wienerlinien', 'mail_single_ticket.eml')),
-    ('yipbee', os.path.join(test_data_path, 'yipbee', 'mail_1.eml')),
-    ])
-def test_parse_confirmation_mail_platform(platform, mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    for order in parsed_orders:
-        assert order.platform == platform
-
-def test_parse_confirmation_mail_failure():
-    mail = email.message_from_string('empty mail')
-    with pytest.raises(Exception):
-        dingguo.parser.parse_order_confirmation_mail(mail)

+ 0 - 24
tests/test_parser_hm.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.hm
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'hm')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.hm.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_ikea.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.ikea
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'ikea')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.ikea.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 52
tests/test_parser_ingdiba.py

@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import os
-import dingguo.parser.ingdiba
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'ing-diba')
-
-def test_ingdiba_pdf_file_to_text_purchase():
-    pdf_name = 'ING-DiBa_Postbox_2013-03-25_WP-Kauf - 60631041.pdf'
-    with open(os.path.join(test_data_path, pdf_name)) as pdf_file:
-        with open(os.path.join(test_data_path, os.path.splitext(pdf_name)[0] + '.txt')) as text_file:
-            assert dingguo.parser.ingdiba.ingdiba_pdf_file_to_text(pdf_file) == text_file.read().decode('utf-8')
-
-def test_ingdiba_pdf_file_to_text_tax():
-    pdf_name = 'ING-DiBa_Postbox_2013-12-27_KESt auf Erträge ausländischer Fonds.pdf'
-    with open(os.path.join(test_data_path, pdf_name)) as pdf_file:
-        with open(os.path.join(test_data_path, os.path.splitext(pdf_name)[0] + '.txt')) as text_file:
-            assert dingguo.parser.ingdiba.ingdiba_pdf_file_to_text(pdf_file) == text_file.read().decode('utf-8')
-
-def test_ingdiba_pdf_file_to_text_statement():
-    pdf_name = 'ING-DiBa_Postbox_2014-01-01_Depotauszug - 60631041.pdf'
-    with open(os.path.join(test_data_path, pdf_name)) as pdf_file:
-        with open(os.path.join(test_data_path, os.path.splitext(pdf_name)[0] + '.txt')) as text_file:
-            assert dingguo.parser.ingdiba.ingdiba_pdf_file_to_text(pdf_file) == text_file.read().decode('utf-8')
-
-def test_ingdiba_pdf_file_to_text_divestiture():
-    pdf_name = 'ING-DiBa_Postbox_2014-02-07_WP-Verkauf - 60631041.pdf'
-    with open(os.path.join(test_data_path, pdf_name)) as pdf_file:
-        with open(os.path.join(test_data_path, os.path.splitext(pdf_name)[0] + '.txt')) as text_file:
-            assert dingguo.parser.ingdiba.ingdiba_pdf_file_to_text(pdf_file) == text_file.read().decode('utf-8')
-
-def test_ingdiba_pdf_file_to_text_loss_compensation():
-    pdf_name = 'ING-DiBa_Postbox_2014-04-03_Verlustausgleich 2013.pdf'
-    with open(os.path.join(test_data_path, pdf_name)) as pdf_file:
-        with open(os.path.join(test_data_path, os.path.splitext(pdf_name)[0] + '.txt')) as text_file:
-            assert dingguo.parser.ingdiba.ingdiba_pdf_file_to_text(pdf_file) == text_file.read().decode('utf-8')
-
-@pytest.mark.xfail(raises=IndexError)
-def test_ingdiba_pdf_file_to_text_loss_compensation_single_page():
-    pdf_name = 'ING-DiBa_Postbox_2016-04-28_Verlustausgleich.pdf'
-    with open(os.path.join(test_data_path, pdf_name)) as pdf_file:
-        with open(os.path.join(test_data_path, os.path.splitext(pdf_name)[0] + '.txt')) as text_file:
-            assert dingguo.parser.ingdiba.ingdiba_pdf_file_to_text(pdf_file) == text_file.read().decode('utf-8')
-
-def test_ingdiba_pdf_file_to_text_dividend_distribution():
-    pdf_name = 'ING-DiBa_Postbox_2014-04-09_Jährl. Bestätigung Ausschüttung Fonds.pdf'
-    with open(os.path.join(test_data_path, pdf_name)) as pdf_file:
-        with open(os.path.join(test_data_path, os.path.splitext(pdf_name)[0] + '.txt')) as text_file:
-            assert dingguo.parser.ingdiba.ingdiba_pdf_file_to_text(pdf_file) == text_file.read().decode('utf-8')

+ 0 - 24
tests/test_parser_kickstarter.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.kickstarter
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'kickstarter')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.kickstarter.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_lieferservice.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.lieferservice
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'lieferservice.at')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.lieferservice.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_mytaxi.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.mytaxi
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'mytaxi')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.mytaxi.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_namecheap.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.namecheap
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'namecheap')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.namecheap.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_oebb.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.oebb
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'oebb')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.oebb.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_thomann.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.thomann
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'thomann')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.thomann.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_uber.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.uber
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'uber')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.uber.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_wienerlinien.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.wienerlinien
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'wienerlinien')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.wienerlinien.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 24
tests/test_parser_yipbee.py

@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pytest
-
-import dingguo.parser.yipbee
-import email
-import glob
-import os
-import test_yaml
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-test_data_path = os.path.join(project_root_path, 'tests', 'data', 'yipbee')
-
-@pytest.mark.parametrize('mail_path', glob.glob(os.path.join(test_data_path, '*.eml')))
-def test_parse_confirmation_mail(mail_path):
-    with open(mail_path) as mail:
-        parsed_orders = dingguo.parser.yipbee.parse_order_confirmation_mail(
-                email.message_from_file(mail)
-                )
-    with open(mail_path.replace('.eml', '.yml')) as yaml_file:
-        expected_orders = yaml.load(yaml_file.read())
-    assert expected_orders == parsed_orders, \
-            test_yaml.yaml_diff(expected_orders, parsed_orders)

+ 0 - 52
tests/test_scalar_figure.py

@@ -1,52 +0,0 @@
-import pytest
-
-import copy
-import dingguo
-
-@pytest.mark.parametrize(('params', 'kwargs', 'expected_value', 'expected_unit'), [
-    [[1.0, u'cm'], {}, 1.0, u'cm'],
-    [[-4.0], {'unit': u'cm'}, -4.0, u'cm'],
-    [[], {'value': 2.3, 'unit': u'cm'}, 2.3, u'cm'],
-    ])
-def test_init(params, kwargs, expected_value, expected_unit):
-    f = dingguo.ScalarFigure(*params, **kwargs)
-    assert type(f.value) == type(expected_value)
-    assert type(f.unit) == type(expected_unit)
-    assert f.value == expected_value
-    assert f.unit == expected_unit
-
-@pytest.mark.parametrize(('params', 'kwargs'), [
-    [[], {}],
-    [[None], {}],
-    [[None, u'kg'], {}],
-    [[1, 'cm'], {}],
-    [[], {'unit': u'cm'}],
-    [[1], {'unit': 'cm'}],
-    [[1, u'cm'], {}],
-    [[1], {'unit': u'cm'}],
-    [[(-1, 3), u'cm'], {}],
-    [[], {'value': (3, -1), 'unit': u'cm'}],
-    [[1.2], {'value': 2.3, 'unit': u'cm'}],
-    ])
-def test_init_fail(params, kwargs):
-    with pytest.raises(Exception):
-        dingguo.ScalarFigure(*params, **kwargs)
-
-@pytest.mark.parametrize(('factor_a', 'factor_b', 'product'), [
-    [dingguo.ScalarFigure(5.0, u'cm'), -0.5, dingguo.ScalarFigure(-2.5, u'cm')],
-    [dingguo.ScalarFigure(1.0, u'kg'), 10, dingguo.ScalarFigure(10.0, u'kg')],
-    ])
-def test_scalar_figure_mul(factor_a, factor_b, product):
-    factor_a_copy = copy.deepcopy(factor_a)
-    factor_b_copy = copy.deepcopy(factor_b)
-    assert (factor_a * factor_b) == product
-    assert factor_a_copy == factor_a
-    assert factor_b_copy == factor_b
-
-@pytest.mark.parametrize(('factor_a', 'factor_b'), [
-    [dingguo.ScalarFigure(5.0, u'cm'), dingguo.ScalarFigure(2.0, u'cm')],
-    [dingguo.ScalarFigure(5.0, u'cm'), '23'],
-    ])
-def test_figure_mul_fail(factor_a, factor_b):
-    with pytest.raises(Exception):
-        (factor_a * factor_b)

+ 0 - 137
tests/test_script_order_confirmation_mail_parser.py

@@ -1,137 +0,0 @@
-import pytest
-
-import dingguo.parser
-import email
-import os
-import shutil
-import subprocess
-import yaml
-
-project_root_path = os.path.realpath(os.path.join(__file__, '..', '..'))
-script_path = os.path.join(project_root_path, 'scripts', 'order-confirmation-mail-parser')
-test_data_path = os.path.join(project_root_path, 'tests', 'data')
-
-def get_mail_path_single_order_platform_a():
-    return os.path.join(test_data_path, 'amazon', 'mail_1.eml')
-
-def get_mail_path_single_order_platform_b():
-    return os.path.join(test_data_path, 'banggood', '1.eml')
-
-def get_mail_path_two_orders():
-    return os.path.join(test_data_path, 'amazon', 'mail_2.eml')
-
-@pytest.mark.parametrize('mail_path', [
-    get_mail_path_single_order_platform_a(),
-    get_mail_path_two_orders(),
-    ])
-def test_stdin(mail_path):
-    process = subprocess.Popen([script_path], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
-    with open(mail_path, 'r') as mail_file:
-        script_stdout, script_stderr = process.communicate(input = mail_file.read())
-    parsed_orders = yaml.load(script_stdout)
-    with open(mail_path.replace('.eml', '.yml'), 'r') as expected_result_file:
-        expected_orders = yaml.load(expected_result_file.read())
-    assert expected_orders == parsed_orders
-
-@pytest.mark.parametrize('mail_paths', [
-    [
-        get_mail_path_single_order_platform_a(),
-        ],
-    [
-        get_mail_path_two_orders(),
-        ],
-    [
-        get_mail_path_single_order_platform_a(),
-        get_mail_path_two_orders(),
-        ],
-    ])
-def test_path(mail_paths):
-    parsed_orders = yaml.load(subprocess.check_output([script_path] + mail_paths))
-    expected_orders = []
-    for mail_path in mail_paths:
-        with open(mail_path.replace('.eml', '.yml'), 'r') as expected_result_file:
-            expected_orders += yaml.load(expected_result_file.read())
-    assert expected_orders == parsed_orders
-
-@pytest.mark.parametrize('mail_paths', [
-    [
-        get_mail_path_single_order_platform_a(),
-        get_mail_path_single_order_platform_b(),
-        get_mail_path_two_orders(),
-        ],
-    ])
-def test_catalogue(tmpdir, mail_paths):
-    os.chdir(tmpdir.strpath)
-    for mail_index, mail_path in enumerate(mail_paths):
-        shutil.copyfile(mail_path, '%d.eml' % mail_index)
-    assert len(os.listdir('.')) == len(mail_paths)
-    orders = yaml.load(subprocess.check_output(
-        [script_path, '--catalogue'] + os.listdir('.'),
-        ))
-    assert set(os.listdir('.')) == set([o.platform for o in orders])
-    for dir_name in os.listdir('.'):
-        assert set(os.listdir(dir_name)) \
-                == set([o.order_id for o in orders if o.platform == dir_name])
-
-@pytest.mark.parametrize('mail_paths', [
-    [
-        get_mail_path_single_order_platform_a(),
-        ],
-    [
-        get_mail_path_two_orders(),
-        ],
-    [
-        get_mail_path_single_order_platform_a(),
-        get_mail_path_two_orders(),
-        ],
-    ])
-def test_register_create(tmpdir, mail_paths):
-    os.chdir(tmpdir.strpath)
-    subprocess.check_output(
-        [script_path, '--register', 'registry.yml'] + mail_paths,
-        )
-    with open('registry.yml') as registry_file:
-        registry = yaml.load(registry_file.read())
-    orders = []
-    for mail_path in mail_paths:
-        with open(mail_path) as mail:
-            orders += dingguo.parser.parse_order_confirmation_mail(
-                    email.message_from_file(mail)
-                    )
-    for order in orders:
-        assert order == registry.registry[order.platform][order.order_id]
-    for platform in registry.registry:
-        for order_id in registry.registry[platform]:
-            assert registry.registry[platform][order_id] in orders
-
-@pytest.mark.parametrize('pre_mail_paths,post_mail_paths', [
-    (
-        [get_mail_path_single_order_platform_a()], 
-        [get_mail_path_single_order_platform_b()],
-        ),
-    (
-        [get_mail_path_single_order_platform_a(), get_mail_path_single_order_platform_b()], 
-        [get_mail_path_two_orders()],
-        ),
-    ])
-def test_register_update(tmpdir, pre_mail_paths, post_mail_paths):
-    os.chdir(tmpdir.strpath)
-    subprocess.check_output(
-        [script_path, '--register', 'registry.yml'] + pre_mail_paths,
-        )
-    subprocess.check_output(
-        [script_path, '--register', 'registry.yml'] + post_mail_paths,
-        )
-    with open('registry.yml') as registry_file:
-        registry = yaml.load(registry_file.read())
-    orders = []
-    for mail_path in (pre_mail_paths + post_mail_paths):
-        with open(mail_path) as mail:
-            orders += dingguo.parser.parse_order_confirmation_mail(
-                    email.message_from_file(mail)
-                    )
-    for order in orders:
-        assert order == registry.registry[order.platform][order.order_id]
-    for platform in registry.registry:
-        for order_id in registry.registry[platform]:
-            assert registry.registry[platform][order_id] in orders

+ 66 - 19
tests/test_sum.py

@@ -1,9 +1,9 @@
-# -*- coding: utf-8 -*-
-
 import pytest
 
 import copy
-import dingguo
+import finoex
+import ioex
+
 
 @pytest.mark.parametrize(('params', 'kwargs', 'expected_value', 'expected_currency'), [
     [[1.0, u'US$'], {}, 1.0, u'USD'],
@@ -11,46 +11,47 @@ import dingguo
     [[-4.0], {'unit': u'EUR'}, -4.0, u'EUR'],
     [[-4.0], {'unit': u'€'}, -4.0, u'EUR'],
     [[-4.0], {'currency': u'EUR'}, -4.0, u'EUR'],
-    [[-4.0], {'currency': u'€'}, -4.0, u'EUR'],
+    [[-4.0], {'currency': u'¥'}, -4.0, u'CNY'],
     [[], {'value': 2.3, 'unit': u'EUR'}, 2.3, u'EUR'],
     [[], {'value': 2.3, 'unit': u'€'}, 2.3, u'EUR'],
     [[], {'value': 2.3, 'currency': u'EUR'}, 2.3, u'EUR'],
     [[], {'value': 2.3, 'currency': u'€'}, 2.3, u'EUR'],
-    ])
+])
 def test_init(params, kwargs, expected_value, expected_currency):
-    f = dingguo.Sum(*params, **kwargs)
+    f = finoex.Sum(*params, **kwargs)
     assert type(f.value) == type(expected_value)
     assert type(f.unit) == type(expected_currency)
     assert f.value == expected_value
     assert f.unit == expected_currency
     assert f.currency == expected_currency
 
+
 @pytest.mark.parametrize(('params', 'kwargs'), [
     [[(-1, 3), u'EUR'], {}],
     [[1, 'EUR'], {}],
-    [[1, u'EUR'], {}],
+    [[1.0, ('EUR',)], {}],
     [[1.0, u'USD', u'EUR'], {}],
     [[1.2], {'value': 2.3, 'unit': u'EUR'}],
     [[1], {'unit': 'EUR'}],
     [[1], {'unit': u'EUR'}],
     [[None, u'EUR'], {}],
-    [[None, u'kg'], {}],
     [[None], {}],
     [[], {'unit': u'EUR'}],
     [[], {'value': (3, -1), 'unit': u'EUR'}],
     [[], {'value': 2.3, 'unit': u'EUR', 'currency': u'EUR'}],
     [[], {}],
-    ])
+])
 def test_init_fail(params, kwargs):
     with pytest.raises(Exception):
-        dingguo.Sum(*params, **kwargs)
+        finoex.Sum(*params, **kwargs)
+
 
 @pytest.mark.parametrize(('factor_a', 'factor_b', 'product'), [
-    [dingguo.Sum(5.0, u'USD'), 1.5, dingguo.Sum(7.5, u'USD')],
-    [dingguo.Sum(5.0, u'USD'), 2, dingguo.Sum(10.0, u'USD')],
-    [dingguo.Sum(5.0, u'EUR'), 1.5, dingguo.Sum(7.5, u'EUR')],
-    [dingguo.Sum(5.0, u'EUR'), 2, dingguo.Sum(10.0, u'EUR')],
-    ])
+    [finoex.Sum(5.0, u'USD'), 1.5, finoex.Sum(7.5, u'USD')],
+    [finoex.Sum(5.0, u'USD'), 2, finoex.Sum(10.0, u'USD')],
+    [finoex.Sum(5.0, u'EUR'), 1.5, finoex.Sum(7.5, u'EUR')],
+    [finoex.Sum(5.0, u'EUR'), 2, finoex.Sum(10.0, u'EUR')],
+])
 def test_mul(factor_a, factor_b, product):
     factor_a_copy = copy.deepcopy(factor_a)
     factor_b_copy = copy.deepcopy(factor_b)
@@ -58,11 +59,57 @@ def test_mul(factor_a, factor_b, product):
     assert factor_a_copy == factor_a
     assert factor_b_copy == factor_b
 
+
 @pytest.mark.parametrize(('factor_a', 'factor_b'), [
-    [dingguo.ScalarFigure(5.0, u'USD'), dingguo.ScalarFigure(2.0, u'USD')],
-    [dingguo.ScalarFigure(5.0, u'EUR'), dingguo.ScalarFigure(2.0, u'USD')],
-    [dingguo.ScalarFigure(5.0, u'USD'), '23'],
-    ])
+    [finoex.Sum(5.0, u'USD'), finoex.Sum(2.0, u'USD')],
+    [finoex.Sum(5.0, u'EUR'), finoex.Sum(2.0, u'USD')],
+    [finoex.Sum(5.0, u'USD'), '23'],
+])
 def test_mul_fail(factor_a, factor_b):
     with pytest.raises(Exception):
         (factor_a * factor_b)
+
+
+@pytest.mark.parametrize(('dividend', 'divisor', 'quotient'), [
+    [finoex.Sum(5.0, u'USD'), 2.0, finoex.Sum(2.5, u'USD')],
+    [finoex.Sum(5.0, u'USD'), 2, finoex.Sum(2.5, u'USD')],
+    [finoex.Sum(5.0, u'EUR'), -10, finoex.Sum(-0.5, u'EUR')],
+])
+def test_mul(dividend, divisor, quotient):
+    dividend_copy = copy.deepcopy(dividend)
+    divisor_copy = copy.deepcopy(divisor)
+    assert quotient == (dividend / divisor)
+    assert dividend_copy == dividend
+    assert divisor_copy == divisor
+
+
+@pytest.mark.parametrize(('loc', 'text', 'expected_sum'), [
+    ['de_AT.UTF-8', "-1,23 USD", finoex.Sum(-1.23, 'USD')],
+    ['de_AT.UTF-8', "2,50 EUR", finoex.Sum(2.5, 'EUR')],
+    ['de_AT.UTF-8', "2,50 €", finoex.Sum(2.5, 'EUR')],
+    ['de_AT.UTF-8', "EUR 1234,56", finoex.Sum(1234.56, 'EUR')],
+    ['de_AT.UTF-8', "US$ 0,50", finoex.Sum(0.5, 'USD')],
+    ['de_AT.UTF-8', "US$0,50", finoex.Sum(0.5, 'USD')],
+    ['en_US.UTF-8', "$-1.23 USD", finoex.Sum(-1.23, 'USD')],
+    ['en_US.UTF-8', "$1.23 USD", finoex.Sum(1.23, 'USD')],
+    ['en_US.UTF-8', "-1.23 US$", finoex.Sum(-1.23, 'USD')],
+    ['en_US.UTF-8', "-1.23 USD", finoex.Sum(-1.23, 'USD')],
+    ['en_US.UTF-8', "1.23 ¥", finoex.Sum(1.23, 'CNY')],
+    ['en_US.UTF-8', "2.50 EUR", finoex.Sum(2.5, 'EUR')],
+    ['en_US.UTF-8', "2.50 €", finoex.Sum(2.5, 'EUR')],
+    ['en_US.UTF-8', "US$-0.50", finoex.Sum(-0.5, 'USD')],
+    ['en_US.UTF-8', "¥1.23", finoex.Sum(1.23, 'CNY')],
+])
+def test_parse_text(loc, text, expected_sum):
+    with ioex.setlocale(loc):
+        assert expected_sum == finoex.Sum.parse_text(text)
+
+
+@pytest.mark.parametrize(('text'), [
+    'pre$1.23 USD',
+    '$1#23 USD',
+    '1#23 USD',
+])
+def test_parse_text_fail(text):
+    with pytest.raises(Exception):
+        finoex.Sum.parse_text(text)

+ 24 - 0
tests/test_sum_yaml.py

@@ -0,0 +1,24 @@
+import pytest
+
+import finoex
+import yaml
+
+@pytest.mark.parametrize(('source_object', 'expected_yaml'), [
+    [finoex.Sum(1.23, 'EUR'), "!sum '1.23 EUR'\n"],
+    [finoex.Sum(20.45, 'US$'), "!sum '20.45 USD'\n"],
+    ])
+def test_to_yaml(source_object, expected_yaml):
+    class Dumper(yaml.Dumper):
+        pass
+    finoex.Sum.register_yaml_representer(Dumper)
+    assert expected_yaml == yaml.dump(source_object, Dumper = Dumper)
+
+@pytest.mark.parametrize(('expected_object', 'source_yaml'), [
+    [finoex.Sum(1.23, 'EUR'), "!sum 1.23 EUR"],
+    [finoex.Sum(20.45, 'US$'), "!sum 20.45 USD"],
+    ])
+def test_from_yaml(expected_object, source_yaml):
+    class Loader(yaml.Loader):
+        pass
+    finoex.Sum.register_yaml_constructor(Loader)
+    assert expected_object == yaml.load(source_yaml, Loader = Loader)

+ 113 - 166
tests/test_yaml.py

@@ -4,107 +4,108 @@ import pytest
 
 import datetime
 import difflib
-import dingguo
+import finoex
+import ioex.calcex
 import ioex.datetimeex
 import os
 import pytz
-import yaml
+yaml = pytest.importorskip('yaml')
 
 def get_figure_a():
-    return dingguo.Figure(12.3, u'km')
+    return ioex.calcex.Figure(12.3, u'km')
 
 def get_figure_b():
-    return dingguo.Figure(12300, u'米')
+    return ioex.calcex.Figure(12300, u'米')
 
 def get_sum_a():
-    return dingguo.Sum(1.23, u'EUR')
+    return finoex.Sum(1.23, u'EUR')
 
 def get_sum_b():
-    return dingguo.Sum(20.45, u'€')
+    return finoex.Sum(20.45, u'€')
 
 def get_item_a():
-    return dingguo.Item(
-            name = u'item a',
-            price_brutto = get_sum_a(),
-            )
+    return finoex.Item(
+        name = u'item a',
+        price_brutto = get_sum_a(),
+        )
 
 def get_item_b():
-    return dingguo.Item(
-            name = u'item β',
-            price_brutto = get_sum_b(),
-            )
+    return finoex.Item(
+        name = u'item β',
+        price_brutto = get_sum_b(),
+        )
 
 def get_service():
-    return dingguo.Service(
-            name = u'service',
-            price_brutto = dingguo.Sum(1.0, u'EUR'),
-            duration = ioex.datetimeex.Duration(years = 2),
-            )
+    return finoex.Service(
+        name = u'service',
+        price_brutto = finoex.Sum(1.0, u'EUR'),
+        duration = ioex.datetimeex.Duration(years = 2),
+        )
 
 def get_person_a():
-    return dingguo.Person(
+    return finoex.Person(
         first_name = u'Fabian Peter',
         last_name = u'Hammerle',
         )
 
 def get_person_b():
-    return dingguo.Person(
+    return finoex.Person(
         first_name = u'名字',
         last_name = u'贵姓',
         )
 
 def get_campaign_a():
-    return dingguo.Campaign(
-            name = u'campaign a',
-            founder = u'company',
-            end = datetime.datetime(2016, 7, 23, 9, 23, 17, tzinfo = pytz.timezone('Europe/Vienna')),
-            )
+    return finoex.Campaign(
+        name = u'campaign a',
+        founder = u'company',
+        end = datetime.datetime(2016, 7, 23, 9, 23, 17, tzinfo = pytz.timezone('Europe/Vienna')),
+        )
 
 def get_campaign_b():
-    return dingguo.Campaign(
-            founder = u'company',
-            name = u'campaign without end',
-            website_url = u'http://campaign.com',
-            )
+    return finoex.Campaign(
+        founder = u'company',
+        name = u'campaign without end',
+        website_url = u'http://campaign.com',
+        )
 
 def get_pledge():
-    return dingguo.Pledge(
-            campaign = get_campaign_a(),
-            price_brutto = dingguo.Sum(10.0, u'EUR'),
-            reward = u'great',
-            )
+    return finoex.Pledge(
+        campaign = get_campaign_a(),
+        price_brutto = finoex.Sum(10.0, u'EUR'),
+        reward = u'great',
+        )
 
 def get_contribution():
-    return dingguo.Contribution(
-            campaign = get_campaign_a(),
-            price_brutto = dingguo.Sum(10.0, u'EUR'),
-            reward = u'product',
-            )
+    return finoex.Contribution(
+        campaign = get_campaign_a(),
+        price_brutto = finoex.Sum(10.0, u'EUR'),
+        reward = u'product',
+        )
 
 def get_article():
-    return dingguo.Article(
-            authors = ['a', 'b'],
-            depth = dingguo.ScalarFigure(12.3, u'dm'),
-            features = u'supergeil',
-            height = dingguo.ScalarFigure(123., u'cm'),
-            maximum_load = dingguo.ScalarFigure(40., u'kg'),
-            name = u'article name',
-            price_brutto = get_sum_a(),
-            product_id = u'0815',
-            quantity = 1,
-            reseller = u'seller',
-            shipper = u'shipper',
-            state = u'goood',
-            width = dingguo.ScalarFigure(1.23, u'm'),
-            )
+    return finoex.Article(
+        authors = ['a', 'b'],
+        depth = finoex.Distance(12.3, u'dm'),
+        features = u'supergeil',
+        height = finoex.Distance(123., u'cm'),
+        maximum_load = ioex.calcex.Figure(40., u'kg'),
+        name = u'article name',
+        price_brutto = get_sum_a(),
+        product_id = u'0815',
+        quantity = 1,
+        reseller = u'seller',
+        shipper = u'shipper',
+        state = u'goood',
+        width = finoex.Distance(1.23, u'm'),
+        )
 
 def get_transportation():
-    return dingguo.Transportation(
+    return finoex.Transportation(
             name = u'ticket',
             price_brutto = get_sum_a(),
             departure_point = u'home',
             destination_point = u'city',
-            distance = dingguo.Distance(3.21, u'km'),
+            distance = finoex.Distance(3.21, u'km'),
             passenger = get_person_a(),
             valid_from = datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.timezone('Europe/Vienna')),
             valid_until = datetime.datetime(2016, 7, 14, 18, 50, 4, 0, tzinfo = pytz.utc),
@@ -116,37 +117,37 @@ def get_transportation():
             )
 
 def get_shipping():
-    return dingguo.Shipping(
+    return finoex.Shipping(
             price_brutto = get_sum_a(),
             destination_point = u'home',
             )
 
 def get_taxi_ride():
-    return dingguo.TaxiRide(
+    return finoex.TaxiRide(
             name = u'taxi ride',
             price_brutto = get_sum_a(),
             departure_point = u'home',
             destination_point = u'city',
-            distance = dingguo.Distance(3.21, u'km'),
+            distance = finoex.Distance(3.21, u'km'),
             driver = u'driver',
             arrival_time = datetime.datetime(2016, 5, 2, 18, 10, tzinfo = pytz.timezone('Europe/Vienna')),
             departure_time = datetime.datetime(2016, 5, 2, 18, 25, tzinfo = pytz.timezone('Europe/Vienna')),
             )
 
 def get_discount_a():
-    return dingguo.Discount(
+    return finoex.Discount(
             name = u'discount a',
             amount = get_sum_a(),
             )
 
 def get_discount_b():
-    return dingguo.Discount(
+    return finoex.Discount(
             name = u'discount β',
             amount = get_sum_b(),
             )
 
 def get_order_a(items = True, discounts = True):
-    order = dingguo.Order(
+    order = finoex.Order(
             platform = u'platformπ',
             order_id = u'id',
             order_date = datetime.datetime(2016, 5, 8, 0, 18, 17),
@@ -161,7 +162,7 @@ def get_order_a(items = True, discounts = True):
     return order
 
 def get_order_b():
-    order = dingguo.Order(
+    order = finoex.Order(
             platform = u'platformπ',
             order_id = u'order_b',
             order_date = datetime.datetime(2015, 5, 8, 0, 18, 17),
@@ -169,7 +170,7 @@ def get_order_b():
     return order
 
 def get_order_c():
-    order = dingguo.Order(
+    order = finoex.Order(
             platform = u'γάμμα',
             order_id = u'order_βήτα',
             order_date = datetime.datetime(2014, 5, 8, 0, 18, 17),
@@ -178,17 +179,22 @@ def get_order_c():
     return order
 
 def get_distance():
-    return dingguo.Distance(2.4142, u'km')
-
-def get_order_registry():
-    registry = dingguo.OrderRegistry()
-    registry.register(get_order_a(items = False, discounts = False))
-    registry.register(get_order_b())
-    registry.register(get_order_c())
-    return registry
+    return finoex.Distance(2.4142, u'km')
 
 def to_yaml(data):
-    return yaml.dump(data, default_flow_style = False, allow_unicode = True).decode('utf-8')
+    class Dumper(yaml.Dumper):
+        pass
+    ioex.datetimeex.Duration.register_yaml_representer(yaml.Dumper)
+    ioex.datetimeex.Period.register_yaml_representer(yaml.Dumper)
+    ioex.calcex.Figure.register_yaml_representer(yaml.Dumper)
+    finoex.Sum.register_yaml_representer(yaml.Dumper)
+    finoex.Distance.register_yaml_representer(yaml.Dumper)
+    return yaml.dump(
+        data, 
+        default_flow_style = False, 
+        allow_unicode = True,
+        Dumper = Dumper,
+    ) #.decode('utf-8')
 
 def yaml_diff(a, b):
     return '\n'.join(difflib.ndiff(
@@ -200,18 +206,16 @@ def yaml_diff(a, b):
     [datetime.datetime(2016, 7, 14, 13, 50, 4, 0), '2016-07-14 13:50:04\n...\n'],
     [datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.timezone('Europe/Vienna')), '2016-07-14 13:50:04+01:05\n...\n'],
     [datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.utc), '2016-07-14 13:50:04+00:00\n...\n'],
-    [dingguo.Distance(1.34, u'km'), u"!distance '1.34 km'\n"],
-    [dingguo.ScalarFigure(1.34, u'μm'), u"!scalar '1.34 μm'\n"],
+    [finoex.Distance(1.34, u'km'), u"!distance '1.34 km'\n"],
+    [finoex.Distance(1.34, u'μm'), u"!distance '1.34 μm'\n"],
     [get_discount_a(), u"!discount\namount: !sum '1.23 EUR'\nname: discount a\n"],
     [get_discount_b(), u"!discount\namount: !sum '20.45 EUR'\nname: discount β\n"],
-    [get_figure_a(), u'!figure\nunit: km\nvalue: 12.3\n'],
-    [get_figure_b(), u'!figure\nunit: 米\nvalue: 12300\n'],
+    [get_figure_a(), u"!figure '12.3 km'\n"],
+    [get_figure_b(), u"!figure '12300 米'\n"],
     [get_item_a(), u"!item\nname: item a\nprice_brutto: !sum '1.23 EUR'\n"],
     [get_item_b(), u"!item\nname: item β\nprice_brutto: !sum '20.45 EUR'\n"],
     [get_person_a(), u'!person\nfirst_name: Fabian Peter\nlast_name: Hammerle\n'],
     [get_person_b(), u'!person\nfirst_name: 名字\nlast_name: 贵姓\n'],
-    [get_sum_a(), u"!sum '1.23 EUR'\n"],
-    [get_sum_b(), u"!sum '20.45 EUR'\n"],
     [get_campaign_a(), u"""!campaign
 end: 2016-07-23 09:23:17+01:05
 founder: company
@@ -269,10 +273,10 @@ authors:
 - a
 - b
 delivery_date: null
-depth: !scalar '12.3 dm'
+depth: !distance '12.3 dm'
 features: supergeil
-height: !scalar '123.0 cm'
-maximum_load: !scalar '40.0 kg'
+height: !distance '123.0 cm'
+maximum_load: !figure '40.0 kg'
 name: article name
 price_brutto: !sum '1.23 EUR'
 product_id: 0815
@@ -280,7 +284,7 @@ quantity: 1
 reseller: seller
 shipper: shipper
 state: goood
-width: !scalar '1.23 m'
+width: !distance '1.23 m'
 """],
     [get_transportation(), u"""!transportation
 departure_point: home
@@ -311,41 +315,17 @@ distance: !distance '3.21 km'
 driver: driver
 name: taxi ride
 price_brutto: !sum '1.23 EUR'
-"""],
-    [get_order_registry(), u"""!order-registry
-platformπ:
-  id: !order
-    customer_id: customer
-    discounts: []
-    items: []
-    order_date: 2016-05-08 00:18:17
-    order_id: id
-    platform: platformπ
-  order_b: !order
-    discounts: []
-    items: []
-    order_date: 2015-05-08 00:18:17
-    order_id: order_b
-    platform: platformπ
-γάμμα:
-  order_βήτα: !order
-    customer_id: ρώ
-    discounts: []
-    items: []
-    order_date: 2014-05-08 00:18:17
-    order_id: order_βήτα
-    platform: γάμμα
 """],
     ])
 def test_to_yaml(source_object, expected_yaml):
-    assert to_yaml(source_object) == expected_yaml
+    assert expected_yaml == to_yaml(source_object)
 
 @pytest.mark.parametrize('expected_object,source_yaml', [
     [datetime.datetime(2016, 7, 14, 13, 50, 4, 0), '2016-07-14 13:50:04'],
     [datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.timezone('Europe/Vienna')), '2016-07-14 13:50:04+01:05'],
     [datetime.datetime(2016, 7, 14, 13, 50, 4, 0, tzinfo = pytz.utc), '2016-07-14 13:50:04+00:00'],
-    [dingguo.Distance(1.34, u'km'), u"!distance '1.34 km'\n"],
-    [dingguo.ScalarFigure(1.34, u'μm'), u"!scalar '1.34 μm'"],
+    [finoex.Distance(1.34, u'km'), u"!distance '1.34 km'\n"],
+    [finoex.Distance(1.34, u'μm'), u"!distance '1.34 μm'"],
     [get_discount_a(), u"!discount\nname: discount a\namount: !sum '1.23 EUR'\n"],
     [get_discount_b(), u"!discount\nname: discount β\namount: !sum '20.45 EUR'\n"],
     [get_figure_a(), '!figure\nunit: km\nvalue: 12.3\n'],
@@ -359,10 +339,6 @@ def test_to_yaml(source_object, expected_yaml):
     [get_person_a(), '!person\nfirst_name: Fabian Peter\nlast_name: Hammerle\n'],
     [get_person_a(), u'!person\nfirst_name: Fabian Peter\nlast_name: Hammerle\n'],
     [get_person_b(), u'!person\nfirst_name: 名字\nlast_name: 贵姓\n'],
-    [get_sum_a(), "!sum 1.23 €"],
-    [get_sum_a(), u"!sum '1.23 EUR'"],
-    [get_sum_a(), u"!sum '1.23 EUR'"],
-    [get_sum_a(), u"!sum 1.23 EUR"],
     [get_campaign_a(), u"""!campaign
 name: campaign a
 founder: company
@@ -395,7 +371,6 @@ campaign: !campaign
 price_brutto: !sum '10.0 EUR'
 reward: product
 """],
-    [get_sum_a(), u"!sum 1.23 €"],
     [[get_person_a(), get_person_b()], u"""
 - !person
   first_name: Fabian Peter
@@ -424,13 +399,7 @@ estimated_arrival_time: !period
 destination_point: home
 price_brutto: !sum 1.23 EUR
 """],
-    ])
-def test_from_yaml(expected_object, source_yaml):
-    loaded_object = yaml.load(source_yaml)
-    assert loaded_object == expected_object
-
-def test_order_from_yaml():
-    order_loaded = yaml.load(u"""!order
+    [get_order_a(), u"""!order
 customer_id: customer
 discounts:
 - !discount
@@ -449,12 +418,8 @@ items:
 order_date: 2016-05-08 00:18:17
 order_id: id
 platform: platformπ
-""")
-    order_expected = get_order_a()
-    assert order_expected == order_loaded, yaml_diff(order_expected, order_loaded)
-
-def test_article_from_yaml():
-    assert get_article() == yaml.load(u"""!article
+"""],
+    [get_article(), u"""!article
 authors:
 - a
 - b
@@ -467,14 +432,12 @@ quantity: 1
 reseller: seller
 shipper: shipper
 state: goood
-depth: !scalar 12.3 dm
-height: !scalar 123.0 cm
-maximum_load: !scalar 40.0 kg
-width: !scalar 1.23 m
-""")
-
-def test_taxi_ride_from_yaml():
-    assert get_taxi_ride() == yaml.load(u"""!taxi-ride
+depth: !distance 12.3 dm
+height: !distance 123.0 cm
+maximum_load: !figure 40.0 kg
+width: !distance 1.23 m
+"""],
+    [get_taxi_ride(), u"""!taxi-ride
 arrival_time: 2016-05-02 18:10:00+01:05
 departure_point: home
 departure_time: 2016-05-02 18:25:00+01:05
@@ -484,31 +447,15 @@ driver: driver
 name: taxi ride
 price_brutto: !sum '1.23 EUR'
 route_map: null
-""")
-
-def test_order_registry_from_yaml():
-    expected = get_order_registry()
-    loaded = yaml.load(u"""!order-registry
-platformπ:
-  id: !order
-    customer_id: customer
-    discounts: []
-    order_date: 2016-05-08 00:18:17
-    order_id: id
-    platform: platformπ
-  order_b: !order
-    customer_id: null
-    items: []
-    order_date: 2015-05-08 00:18:17
-    order_id: order_b
-    platform: platformπ
-γάμμα:
-  order_βήτα: !order
-    customer_id: ρώ
-    discounts: []
-    items: []
-    order_date: 2014-05-08 00:18:17
-    order_id: order_βήτα
-    platform: γάμμα
-""")
-    assert expected == loaded, yaml_diff(expected, loaded)
+"""],
+    ])
+def test_from_yaml(expected_object, source_yaml):
+    class Loader(yaml.Loader):
+        pass
+    ioex.datetimeex.Duration.register_yaml_constructor(Loader)
+    ioex.datetimeex.Period.register_yaml_constructor(Loader)
+    ioex.calcex.Figure.register_yaml_constructor(Loader)
+    finoex.Sum.register_yaml_constructor(Loader)
+    finoex.Distance.register_yaml_constructor(Loader)
+    loaded_object = yaml.load(source_yaml, Loader=Loader)
+    assert loaded_object == expected_object