Rewrote OHLC into daily balances.

This commit is contained in:
Alexis Lahouze 2017-05-25 22:04:12 +02:00
parent 5d498adf32
commit 69d5f57b90
2 changed files with 79 additions and 63 deletions

View File

@ -166,8 +166,9 @@ class Operation(db.Model):
return query
@classmethod
def get_ohlc_per_day_for_range(cls, account, begin=None, end=None):
"""Get Opening, High, Low, Closing per day for a specific range"""
def query_daily_balances(cls, account, begin=None, end=None):
"""Get expenses, revenues, income and balance per day for a specific
range and a specific account."""
if isinstance(account, (int, str)):
account_id = account
else:
@ -175,53 +176,65 @@ class Operation(db.Model):
end = end if end else arrow.now().ceil('month').date()
sold = db.func.sum(cls.value).over(
order_by=[cls.operation_date, db.desc(cls.value), cls.label]
)
previous = sold - cls.value
subquery = db.session.query(
cls.operation_date,
sold.label("sold"),
previous.label("previous")
).filter(
cls.account_id == account_id,
cls.canceled == db.false()
).subquery()
query = db.session.query(
subquery.c.operation_date,
db.func.first_value(subquery.c.previous).over(
partition_by=subquery.c.operation_date
).label("open"),
db.func.max(
db.func.greatest(
subquery.c.previous, subquery.c.sold
cls.operation_date,
db.func.coalesce(
db.func.sum(
cls.value
).filter(
db.func.sign(cls.value) == -1
).over(
partition_by=[cls.account_id, cls.operation_date],
),
0
).label("expenses"),
db.func.coalesce(
db.func.sum(
cls.value
).filter(
db.func.sign(cls.value) == 1
).over(
partition_by=[cls.account_id, cls.operation_date],
),
0
).label("revenues"),
db.func.coalesce(
db.func.sum(
cls.value
).over(
partition_by=[cls.account_id, cls.operation_date],
)
).over(
partition_by=subquery.c.operation_date
).label('high'),
db.func.min(
db.func.least(
subquery.c.previous, subquery.c.sold
).label("income"),
db.func.coalesce(
db.func.sum(
cls.value
).over(
partition_by=cls.account_id,
order_by=cls.operation_date
)
).over(
partition_by=subquery.c.operation_date
).label('low'),
db.func.last_value(subquery.c.sold).over(
partition_by=subquery.c.operation_date
).label('close')
).distinct()
).label("balance")
).distinct(
).order_by(
cls.operation_date
).filter(
cls.account_id == account_id
)
if begin:
query = query.filter(subquery.c.operation_date >= str(begin))
base_query = query.subquery()
if end:
query = query.filter(subquery.c.operation_date <= str(end))
query = db.session.query(
base_query
).filter(
base_query.c.operation_date >= str(begin)
).order_by(
base_query.c.operation_date
)
query = query.order_by(
subquery.c.operation_date
)
if end:
query = query.filter(query.c.operation_date <= str(end))
elif end:
query = query.filter(cls.operation_date <= str(end))
return query

View File

@ -7,7 +7,7 @@ import dateutil.parser
from flask_jwt_extended import jwt_required
from flask_restplus import Namespace, Resource, fields
from ..models import db, row_as_dict
from ..models import db, row_as_dict, result_as_dicts
from ..models.accounts import Account
from ..models.operations import Operation
@ -71,33 +71,33 @@ category_model = ns.model('Category', {
description='Total income for the category')
})
# OHLC model.
ohlc_model = ns.model('OHLC', {
'operation_date': fields.DateTime(
# Daily balance model.
daily_balance_model = ns.model('Daily balance', {
'operation_date': fields.Date(
dt_format='iso8601',
readonly=True,
required=True,
description='Date of the OHLC object'
description='Date'
),
'open': fields.Float(
'expenses': fields.Float(
readonly=True,
required=True,
description='Open value'
description='Expenses'
),
'high': fields.Float(
'revenues': fields.Float(
readonly=True,
required=True,
description='High value'
description='Revenues'
),
'low': fields.Float(
'income': fields.Float(
readonly=True,
required=True,
description='Low value'
description='Income'
),
'close': fields.Float(
'balance': fields.Float(
readonly=True,
required=True,
description='Close value'
description='Balance'
)
})
@ -330,26 +330,29 @@ class CategoryResource(Resource):
return Operation.get_categories_for_range(account_id, **data).all()
@ns.route('/<int:account_id>/ohlc')
@ns.route('/<int:account_id>/daily_balances')
@ns.doc(
security='apikey',
params={
'account_id': 'Id of the account to manage'
},
responses={
200: ('OK', [ohlc_model]),
200: ('OK', [daily_balance_model]),
401: 'Unauthorized',
404: 'Account not found'
})
class OHLCResource(Resource):
"""Resource to expose OHLC."""
class DailyBalancesResource(Resource):
"""Resource to expose account daily balances."""
@ns.expect(range_parser)
@ns.marshal_list_with(ohlc_model)
@ns.marshal_list_with(daily_balance_model)
@jwt_required
def get(self, account_id):
"""Get OHLC data for a specific date range and account."""
"""Get account daily balance data for a specific date range and
account."""
data = range_parser.parse_args()
return Operation.get_ohlc_per_day_for_range(account_id, **data).all()
return list(result_as_dicts(
Operation.query_daily_balances(account_id, **data)
)), 200