Move queries in models.

This commit is contained in:
Alexis Lahouze 2015-06-06 23:35:31 +02:00
parent e1b5d8be43
commit c411ce5328
7 changed files with 124 additions and 75 deletions

View File

@ -14,8 +14,12 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>. along with Accountant. If not, see <http://www.gnu.org/licenses/>.
""" """
from sqlalchemy import func, case, cast
from accountant import db from accountant import db
from .entries import Entry
class Account(db.Model): class Account(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -25,3 +29,18 @@ class Account(db.Model):
def __init__(self, name, authorized_overdraft): def __init__(self, name, authorized_overdraft):
self.name = name self.name = name
self.authorized_overdraft = authorized_overdraft self.authorized_overdraft = authorized_overdraft
@classmethod
def get_accounts(cls, session):
query = session.query(
cls.id.label("id"),
cls.name.label("name"),
cls.authorized_overdraft.label("authorized_overdraft"),
func.sum(Entry.value).label("future"),
func.sum(case([(Entry.pointed, Entry.value,)],
else_=cast(0, db.Numeric(15, 2)))).label("pointed"),
func.sum(case([(Entry.operation_date < func.now(), Entry.value,)],
else_=cast(0, db.Numeric(15, 2)))).label("current")
).outerjoin(Entry).group_by(cls.id).order_by(cls.id)
return query

View File

@ -14,10 +14,9 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>. along with Accountant. If not, see <http://www.gnu.org/licenses/>.
""" """
from accountant import db
from .accounts import Account from sqlalchemy import distinct, func, cast, extract
from .scheduled_operations import ScheduledOperation from accountant import db
class Entry(db.Model): class Entry(db.Model):
@ -30,9 +29,9 @@ class Entry(db.Model):
scheduled_operation_id = db.Column(db.Integer, scheduled_operation_id = db.Column(db.Integer,
db.ForeignKey('scheduled_operation.id')) db.ForeignKey('scheduled_operation.id'))
account = db.relationship(Account, backref=db.backref('entry', account = db.relationship("Account", backref=db.backref('entry',
lazy="dynamic")) lazy="dynamic"))
scheduled_operation = db.relationship(ScheduledOperation, scheduled_operation = db.relationship("ScheduledOperation",
backref=db.backref('entry', backref=db.backref('entry',
lazy="dynamic")) lazy="dynamic"))
@ -47,3 +46,19 @@ class Entry(db.Model):
self.account_id = account_id self.account_id = account_id
self.category = category self.category = category
self.scheduled_operation_id = scheduled_operation_id self.scheduled_operation_id = scheduled_operation_id
@classmethod
def get_months_for_account(cls, session, account):
if isinstance(account, int) or isinstance(account, str):
account_id = account
else:
account_id = account.id
query = session.query(
distinct(func.lpad(cast(extract("year", cls.operation_date),
db.String), 4, '0')).label("year"),
func.lpad(cast(extract("month", cls.operation_date),
db.String), 2, '0').label("month")
).filter(cls.account_id == account_id).order_by("year", "month")
return query

View File

@ -14,6 +14,8 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>. along with Accountant. If not, see <http://www.gnu.org/licenses/>.
""" """
from sqlalchemy import func, case, desc
from accountant import db from accountant import db
from .accounts import Account from .accounts import Account
@ -44,3 +46,52 @@ class Operation(db.Model):
self.value = value self.value = value
self.account_id = account_id self.account_id = account_id
self.category = category self.category = category
@classmethod
def get_for_account_and_month(cls, session, account, year, month):
if isinstance(account, int) or isinstance(account, str):
account_id = account
else:
account_id = account.id
base_query = session.query(
cls,
case(
whens={cls.canceled: None},
else_=func.sum(cls.value).over(
partition_by="canceled",
order_by=["operation_date", desc("value"), desc("label")])
).label("sold")
).filter(cls.account_id == account_id).order_by(
desc(cls.operation_date),
cls.value,
cls.label,
).subquery()
query = session.query(base_query).select_from(base_query)
query = query.filter(func.date_trunc(
'month', base_query.c.operation_date) == "%s-%s-01" % (year, month))
return query
@classmethod
def get_account_status(cls, session, account, year, month):
if isinstance(account, int) or isinstance(account, str):
account_id = account
else:
account_id = account.id
query = session.query(
func.sum(case([(func.sign(cls.value) == -1, cls.value)],
else_=0)).label("expenses"),
func.sum(case([(func.sign(cls.value) == 1, cls.value)],
else_=0)).label("revenues"),
func.sum(cls.value).label("balance")
).filter(
cls.account_id == account_id
).filter(
func.date_trunc('month',
cls.operation_date) == "%s-%s-01" % (year, month)
).group_by(cls.account_id)
return query

View File

@ -14,6 +14,8 @@
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with Accountant. If not, see <http://www.gnu.org/licenses/>. along with Accountant. If not, see <http://www.gnu.org/licenses/>.
""" """
from sqlalchemy import desc
from accountant import db from accountant import db
from .accounts import Account from .accounts import Account
@ -44,3 +46,24 @@ class ScheduledOperation(db.Model):
self.value = value self.value = value
self.account_id = account_id self.account_id = account_id
self.category = category self.category = category
@classmethod
def get_scheduled_operations_for_account(cls, session, account):
if isinstance(account, int) or isinstance(account, str):
account_id = account
else:
account_id = account.id
query = session.query(
cls
).select_from(
session.query(cls)
.filter(cls.account_id == account_id)
.order_by(
desc(cls.day),
cls.value,
cls.label,
).subquery()
)
return query

View File

@ -16,11 +16,9 @@
""" """
from flask import json, request from flask import json, request
from sqlalchemy import func, case, cast, extract, distinct
from forms.accounts import AccountForm from forms.accounts import AccountForm
from accountant import db, session_scope from accountant import session_scope
from . import auth from . import auth
from .. import api from .. import api
@ -37,16 +35,7 @@ def get_accounts():
Returns accounts with their solds. Returns accounts with their solds.
""" """
with session_scope() as session: with session_scope() as session:
query = session.query( query = Account.get_accounts(session)
Account.id.label("id"),
Account.name.label("name"),
Account.authorized_overdraft.label("authorized_overdraft"),
func.sum(Entry.value).label("future"),
func.sum(case([(Entry.pointed, Entry.value,)],
else_=cast(0, db.Numeric(15, 2)))).label("pointed"),
func.sum(case([(Entry.operation_date < func.now(), Entry.value,)],
else_=cast(0, db.Numeric(15, 2)))).label("current")
).outerjoin(Entry).group_by(Account.id).order_by(Account.id)
return json.dumps([{ return json.dumps([{
"id": i.id, "id": i.id,
@ -62,19 +51,7 @@ def get_accounts():
@auth.login_required @auth.login_required
def get_account_status(account_id, year, month): def get_account_status(account_id, year, month):
with session_scope() as session: with session_scope() as session:
query = session.query( query = Operation.get_account_status(session, account_id, year, month)
func.sum(case([(func.sign(Operation.value) == -1, Operation.value)],
else_=0)).label("expenses"),
func.sum(case([(func.sign(Operation.value) == 1, Operation.value)],
else_=0)).label("revenues"),
func.sum(Operation.value).label("balance")
).filter(
Operation.account_id == account_id
).filter(
func.date_trunc('month',
Operation.operation_date) == "%s-%s-01" % (year,
month)
).group_by(Operation.account_id)
if query.count() == 1: if query.count() == 1:
result = query.one() result = query.one()
@ -97,12 +74,7 @@ def get_account_status(account_id, year, month):
@auth.login_required @auth.login_required
def get_months(account_id): def get_months(account_id):
with session_scope() as session: with session_scope() as session:
query = session.query( query = Entry.get_months_for_account(session, account_id)
distinct(func.lpad(cast(extract("year", Entry.operation_date),
db.String), 4, '0')).label("year"),
func.lpad(cast(extract("month", Entry.operation_date),
db.String), 2, '0').label("month")
).filter(Entry.account_id == account_id).order_by("year", "month")
return json.dumps([{ return json.dumps([{
"year": i.year, "year": i.year,
@ -129,10 +101,7 @@ def update_account(account_id):
if account_form.validate(): if account_form.validate():
with session_scope() as session: with session_scope() as session:
query = session.query(Account) account = session.query(Account).get(account_id)
query = query.filter(Account.id == account_id)
account = query.first()
account.name = request.json['name'] account.name = request.json['name']
account.authorized_overdraft = request.json['authorized_overdraft'] account.authorized_overdraft = request.json['authorized_overdraft']

View File

@ -16,8 +16,6 @@
""" """
from flask import json, request from flask import json, request
from sqlalchemy import func, case, desc
from accountant import session_scope from accountant import session_scope
from .. import api from .. import api
@ -32,23 +30,8 @@ def get_entries(account_id, year, month):
Return entries for an account, year, and month. Return entries for an account, year, and month.
""" """
with session_scope() as session: with session_scope() as session:
base_query = session.query( query = Operation.get_for_account_and_month(session, account_id, year,
Operation, month)
case(
whens={Operation.canceled: None},
else_=func.sum(Operation.value).over(
partition_by="canceled",
order_by=["operation_date", desc("value"), desc("label")])
).label("sold")
).filter(Operation.account_id == account_id).order_by(
desc(Operation.operation_date),
Operation.value,
Operation.label,
).subquery()
query = session.query(base_query).select_from(base_query)
query = query.filter(func.date_trunc(
'month', base_query.c.operation_date) == "%s-%s-01" % (year, month))
return json.dumps([{ return json.dumps([{
"id": i.id, "id": i.id,
@ -85,7 +68,7 @@ def add_entry():
@api.route("/entries/<entry_id>", methods=["PUT"]) @api.route("/entries/<entry_id>", methods=["PUT"])
def update_entry(entry_id): def update_entry(entry_id):
with session_scope() as session: with session_scope() as session:
entry = session.query(Entry).filter(Entry.id == entry_id).first() entry = session.query(Entry).get(entry_id)
entry.id = entry_id entry.id = entry_id
entry.operation_date = request.json['operation_date'] entry.operation_date = request.json['operation_date']

View File

@ -16,8 +16,6 @@
""" """
from flask import json, request from flask import json, request
from sqlalchemy import desc
from accountant import session_scope from accountant import session_scope
from .. import api from .. import api
@ -30,17 +28,8 @@ def get_scheduled_operations(account_id):
Return entries for an account, year, and month. Return entries for an account, year, and month.
""" """
with session_scope() as session: with session_scope() as session:
query = session.query( query = ScheduledOperation.get_scheduled_operations_for_account(
ScheduledOperation session, account_id)
).select_from(
session.query(ScheduledOperation)
.filter(ScheduledOperation.account_id == account_id)
.order_by(
desc(ScheduledOperation.day),
ScheduledOperation.value,
ScheduledOperation.label,
).subquery()
)
return json.dumps([{ return json.dumps([{
"id": i.id, "id": i.id,