From 458b4e7a1135df8859e94b10e316d3f8c5c5a561 Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Wed, 13 Feb 2013 12:54:53 +0100 Subject: Remove dependency on elixir --- model.py | 124 ++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 45 deletions(-) (limited to 'model.py') diff --git a/model.py b/model.py index cf8c593..7a07ff0 100644 --- a/model.py +++ b/model.py @@ -1,42 +1,68 @@ -import elixir -from elixir import Field, ManyToOne, OneToMany, OneToOne, ColumnProperty, using_options, using_options_defaults from sqlalchemy import types as T -from sqlalchemy import sql, Index +from sqlalchemy import sql, Index, Column, ForeignKey, create_engine +from sqlalchemy.orm import relationship, backref, scoped_session, sessionmaker,\ + column_property +from sqlalchemy.ext.declarative import declarative_base, declared_attr +from sqlalchemy.ext.hybrid import hybrid_property import datetime +import decimal from functools import partial from collections import namedtuple -__all__ = ["Category", "SingleExpense", "ConstExpense", "CatExpense", "MonthExpense"] +__all__ = ["Category", "SingleExpense", "ConstExpense", "CatExpense", "MonthExpense", + "Session"] # # DB Setup # -elixir.metadata.bind = "sqlite:///test.sqlite" -elixir.metadata.bind.echo = True +engine = create_engine("sqlite:///test.sqlite") +engine.echo = True + +Session = scoped_session(sessionmaker(bind=engine)) # # Global definitions # -ReqField = partial(Field, required = True) -ExpNum = T.Numeric(scale = 2, precision = 10) +class Base(object): + @declared_attr + def __tablename__ (cls): + return cls.__name__.lower() + + id = Column(T.Integer, primary_key=True) + + query = Session.query_property() + + @classmethod + def get_by (cls, *args, **kwargs): + return cls.query.filter_by(*args, **kwargs).first() + + @classmethod + def get (cls, *args, **kwargs): + return cls.query.get(*args, **kwargs) + -class Entity (elixir.Entity): - using_options(abstract = True) +Base = declarative_base(cls=Base) + +ReqColumn = partial(Column, nullable = False) +ExpNum = T.Numeric(scale = 2, precision = 10) - using_options_defaults(shortnames = True) +def to_exp(d): + """Converts decimal into expense""" + return d.quantize(decimal.Decimal('.01'), rounding = decimal.ROUND_UP) # # Database Entities # -class Category (Entity): - name = Field(T.Unicode(50), unique = True) +class Category (Base): + name = ReqColumn(T.Unicode(50), unique = True) + parent_id = Column(T.Integer, ForeignKey('category.id')) - parent = ManyToOne('Category') - children = OneToMany('Category') + children = relationship('Category', + backref=backref('parent', remote_side="Category.id")) def __repr__ (self): if self.parent: @@ -44,17 +70,24 @@ class Category (Entity): else: return '' % self.name -class Expense (Entity): - using_options(abstract = True) +class Expense (Base): + __abstract__ = True - description = Field(T.Unicode(50)) - expense = ReqField(ExpNum) - category = ManyToOne('Category', required = True, innerjoin = True) + description = Column(T.Unicode(50)) + expense = ReqColumn(ExpNum) + + @declared_attr + def category_id(cls): + return ReqColumn(T.Integer, ForeignKey(Category.id)) + + @declared_attr + def category(cls): + return relationship(Category, innerjoin = True) class SingleExpense (Expense): - year = ReqField(T.Integer) - month = ReqField(T.SmallInteger) - day = ReqField(T.SmallInteger) + year = ReqColumn(T.Integer) + month = ReqColumn(T.SmallInteger) + day = ReqColumn(T.SmallInteger) @classmethod def of_month (cls, month, year): @@ -75,14 +108,17 @@ class SingleExpense (Expense): self.day = d.day class ConstExpense (Expense): - months = ReqField(T.SmallInteger) - start = ReqField(T.Date, index = True) - end = ReqField(T.Date, index = True) + months = ReqColumn(T.SmallInteger) + start = ReqColumn(T.Date, index = True) + end = ReqColumn(T.Date, index = True) + prev_id = Column(T.Integer, ForeignKey('constexpense.id')) - prev = ManyToOne('ConstExpense', ondelete = "SET NULL") - next = OneToOne('ConstExpense', inverse = 'prev') - - monthly = ColumnProperty(lambda c: sql.cast(c.expense / c.months, ExpNum)) + prev = relationship('ConstExpense', remote_side = "ConstExpense.id", uselist = False, + backref=backref('next', uselist = False)) + + @property + def monthly(self): + return to_exp(self.expense / self.months) @classmethod def of_month (cls, month, year): @@ -100,16 +136,22 @@ class CatExpense (namedtuple('CatExpense', 'cat expense exps')): return self.exps.order_by(SingleExpense.day).all() class MonthExpense (namedtuple('MonthExpense', 'date catexps')): - __slots__ = () - @property - def constsum (self): - c = ConstExpense.of_month(self.date.month, self.date.year) - return c.value(sql.functions.sum(ConstExpense.monthly)) or 0 + def __init__ (self, *args, **kwargs): + self._consts = None + super(MonthExpense, self).__init__(*args, **kwargs) @property def consts (self): - return ConstExpense.of_month(self.date.month, self.date.year).all() + if self._consts is None: + self._consts = ConstExpense.of_month(self.date.month, self.date.year).all() + + return self._consts + + @property + def constsum (self): + s = sum(c.monthly for c in self.consts) + return s or 0 @property def sum (self): @@ -122,14 +164,6 @@ class MonthExpense (namedtuple('MonthExpense', 'date catexps')): def __str__ (self): return '' % (self.date, self.sum) -# -# Rest -# - -elixir.setup_all() - -session = elixir.session - # # Extra indizes have to be here # @@ -138,4 +172,4 @@ Index('idx_single_date', SingleExpense.year, SingleExpense.month) Index('idx_start_end', ConstExpense.start, ConstExpense.end) if __name__ == "__main__": - elixir.create_all() + Base.metadata.create_all(engine) -- cgit v1.2.3-70-g09d2