summaryrefslogtreecommitdiff
path: root/app/model.py
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.eu>2020-07-23 00:28:47 +0200
committerRené 'Necoro' Neumann <necoro@necoro.eu>2020-07-23 00:28:47 +0200
commit81493afa53a1a1d5ff4b417d05febf9f9e2a172b (patch)
tree00de0a1bb7c386cff4203aa7b0789569e75347bb /app/model.py
parent6f6c8af2a55fabb69372e3fc4e8504167805d018 (diff)
downloadkosten-81493afa53a1a1d5ff4b417d05febf9f9e2a172b.tar.gz
kosten-81493afa53a1a1d5ff4b417d05febf9f9e2a172b.tar.bz2
kosten-81493afa53a1a1d5ff4b417d05febf9f9e2a172b.zip
Restructure
Diffstat (limited to 'app/model.py')
-rw-r--r--app/model.py205
1 files changed, 0 insertions, 205 deletions
diff --git a/app/model.py b/app/model.py
deleted file mode 100644
index 4663685..0000000
--- a/app/model.py
+++ /dev/null
@@ -1,205 +0,0 @@
-from flask_sqlalchemy import SQLAlchemy
-from sqlalchemy import sql
-from sqlalchemy.ext.declarative import declared_attr
-
-import datetime
-import decimal
-from functools import partial
-from collections import namedtuple
-
-from . import app
-
-db = SQLAlchemy(app)
-
-__all__ = ['db', \
- 'Category', 'SingleExpense', 'ConstExpense', \
- 'CatExpense', 'MonthExpense']
-
-Column = db.Column
-ReqColumn = partial(Column, nullable = False)
-ExpNum = db.Numeric(scale = 2, precision = 10)
-
-def to_exp(d):
- """Converts decimal into expense"""
- return d.quantize(decimal.Decimal('.01'), rounding = decimal.ROUND_UP)
-
-#
-# Database Entities
-#
-class Model (db.Model):
- """Abstract base class for all models.
- Adds an id PK and several convenience accessors.
- """
-
- __abstract__ = True
-
- id = Column(db.Integer, primary_key=True)
-
- @declared_attr
- def __tablename__ (cls):
- return cls.__name__.lower()
-
- @classmethod
- def get_by (cls, *args, **kwargs):
- return cls.query.filter_by(*args, **kwargs).first()
-
- @classmethod
- def get_by_or_404 (cls, *args, **kwargs):
- return cls.query.filter_by(*args, **kwargs).first_or_404()
-
- @classmethod
- def get (cls, *args, **kwargs):
- return cls.query.get(*args, **kwargs)
-
- @classmethod
- def get_or_404 (cls, *args, **kwargs):
- return cls.query.get_or_404(*args, **kwargs)
-
-
-class User (Model):
- # NB: This is abstract, the flesh is added in login.py
-
- __abstract__ = True
- name = ReqColumn(db.Unicode(50), unique = True)
- pwd = ReqColumn(db.Unicode(255))
- description = Column(db.Unicode(100))
-
-
-class UserModel (Model):
- """Abstract base class for tables that have a user column."""
- __abstract__ = True
-
- @declared_attr
- def user_id(cls):
- return ReqColumn(db.Integer, db.ForeignKey('user.id'))
-
- @declared_attr
- def user(cls):
- return db.relationship('User')
-
- @classmethod
- def of (cls, user):
- return cls.query.filter_by(user = user)
-
-
-class Category (UserModel):
- name = ReqColumn(db.Unicode(50))
- parent_id = Column(db.Integer, db.ForeignKey('category.id'))
-
- children = db.relationship('Category',
- backref=db.backref('parent', remote_side="Category.id"))
-
- def __init__ (self, name, user, parent_id = None):
- Model.__init__(self)
- self.name = name
- self.user = user
- self.parent_id = parent_id
-
- def __repr__ (self):
- if self.parent:
- return '<Category "%s" of "%s">' % (self.name, self.parent.name)
- else:
- return '<Category "%s">' % self.name
-
-
-class CategoryModel (db.Model):
- """Abstract base class for expenses: Adds the common fields
- and establishes the connection to `Category`.
- """
- __abstract__ = True
-
- @declared_attr
- def category_id(cls):
- return ReqColumn(db.Integer, db.ForeignKey(Category.id))
-
- @declared_attr
- def category(cls):
- return db.relationship(Category, innerjoin = True)
-
-
-class SingleExpense (UserModel, CategoryModel):
- description = Column(db.Unicode(50))
- expense = ReqColumn(ExpNum)
- year = ReqColumn(db.Integer)
- month = ReqColumn(db.SmallInteger)
- day = ReqColumn(db.SmallInteger)
-
- @classmethod
- def of_month (cls, user, month, year):
- return cls.of(user).filter_by(month = month, year = year)
-
- @property
- def date (self):
- return datetime.date(self.year, self.month, self.day)
-
- @date.setter
- def date (self, d):
- self.year = d.year
- self.month = d.month
- self.day = d.day
-
-
-class ConstExpense (UserModel, CategoryModel):
- description = Column(db.Unicode(50))
- expense = ReqColumn(ExpNum)
- months = ReqColumn(db.SmallInteger)
- start = ReqColumn(db.Date, index = True)
- end = ReqColumn(db.Date, index = True)
- prev_id = Column(db.Integer, db.ForeignKey('constexpense.id'))
-
- prev = db.relationship('ConstExpense', remote_side = 'ConstExpense.id', uselist = False,
- backref=db.backref('next', uselist = False))
-
- @property
- def monthly(self):
- return to_exp(self.expense / self.months)
-
- @classmethod
- def of_month (cls, user, month, year):
- d = datetime.date(year, month, 1)
- return cls.of(user).filter(sql.between(d, cls.start, cls.end))
-
-#
-# Work entities (not stored in DB)
-#
-class CatExpense (namedtuple('CatExpense', 'cat sum exps')):
- __slots__ = ()
-
- @property
- def all (self):
- return self.exps.order_by(SingleExpense.day).all()
-
-class MonthExpense (namedtuple('MonthExpense', 'user date catexps')):
-
- def __init__ (self, *args, **kwargs):
- self._consts = None
-
- @property
- def consts (self):
- if self._consts is None:
- self._consts = ConstExpense.of_month(self.user, 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):
- return self.constsum + sum(x.sum for x in self.catexps)
-
- @property
- def all (self):
- return SingleExpense.of_month(self.user, self.date.month, self.date.year).order_by(SingleExpense.day).all()
-
- def __str__ (self):
- return '<MonthExpense (user: %s) of "%s": %s>' % (self.user.name, self.date, self.sum)
-
-#
-# Extra indices have to be here
-#
-
-db.Index('idx_single_date', SingleExpense.user_id, SingleExpense.year, SingleExpense.month)
-db.Index('idx_start_end', ConstExpense.user_id, ConstExpense.start, ConstExpense.end)