Rewrote OHLC into daily balances.
This commit is contained in:
parent
5d498adf32
commit
69d5f57b90
@ -166,8 +166,9 @@ class Operation(db.Model):
|
|||||||
return query
|
return query
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ohlc_per_day_for_range(cls, account, begin=None, end=None):
|
def query_daily_balances(cls, account, begin=None, end=None):
|
||||||
"""Get Opening, High, Low, Closing per day for a specific range"""
|
"""Get expenses, revenues, income and balance per day for a specific
|
||||||
|
range and a specific account."""
|
||||||
if isinstance(account, (int, str)):
|
if isinstance(account, (int, str)):
|
||||||
account_id = account
|
account_id = account
|
||||||
else:
|
else:
|
||||||
@ -175,53 +176,65 @@ class Operation(db.Model):
|
|||||||
|
|
||||||
end = end if end else arrow.now().ceil('month').date()
|
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(
|
query = db.session.query(
|
||||||
subquery.c.operation_date,
|
cls.operation_date,
|
||||||
db.func.first_value(subquery.c.previous).over(
|
db.func.coalesce(
|
||||||
partition_by=subquery.c.operation_date
|
db.func.sum(
|
||||||
).label("open"),
|
cls.value
|
||||||
db.func.max(
|
).filter(
|
||||||
db.func.greatest(
|
db.func.sign(cls.value) == -1
|
||||||
subquery.c.previous, subquery.c.sold
|
|
||||||
)
|
|
||||||
).over(
|
).over(
|
||||||
partition_by=subquery.c.operation_date
|
partition_by=[cls.account_id, cls.operation_date],
|
||||||
).label('high'),
|
),
|
||||||
db.func.min(
|
0
|
||||||
db.func.least(
|
).label("expenses"),
|
||||||
subquery.c.previous, subquery.c.sold
|
db.func.coalesce(
|
||||||
)
|
db.func.sum(
|
||||||
|
cls.value
|
||||||
|
).filter(
|
||||||
|
db.func.sign(cls.value) == 1
|
||||||
).over(
|
).over(
|
||||||
partition_by=subquery.c.operation_date
|
partition_by=[cls.account_id, cls.operation_date],
|
||||||
).label('low'),
|
),
|
||||||
db.func.last_value(subquery.c.sold).over(
|
0
|
||||||
partition_by=subquery.c.operation_date
|
).label("revenues"),
|
||||||
).label('close')
|
db.func.coalesce(
|
||||||
).distinct()
|
db.func.sum(
|
||||||
|
cls.value
|
||||||
|
).over(
|
||||||
|
partition_by=[cls.account_id, cls.operation_date],
|
||||||
|
)
|
||||||
|
).label("income"),
|
||||||
|
db.func.coalesce(
|
||||||
|
db.func.sum(
|
||||||
|
cls.value
|
||||||
|
).over(
|
||||||
|
partition_by=cls.account_id,
|
||||||
|
order_by=cls.operation_date
|
||||||
|
)
|
||||||
|
).label("balance")
|
||||||
|
).distinct(
|
||||||
|
).order_by(
|
||||||
|
cls.operation_date
|
||||||
|
).filter(
|
||||||
|
cls.account_id == account_id
|
||||||
|
)
|
||||||
|
|
||||||
if begin:
|
if begin:
|
||||||
query = query.filter(subquery.c.operation_date >= str(begin))
|
base_query = query.subquery()
|
||||||
|
|
||||||
if end:
|
query = db.session.query(
|
||||||
query = query.filter(subquery.c.operation_date <= str(end))
|
base_query
|
||||||
|
).filter(
|
||||||
query = query.order_by(
|
base_query.c.operation_date >= str(begin)
|
||||||
subquery.c.operation_date
|
).order_by(
|
||||||
|
base_query.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
|
return query
|
||||||
|
@ -7,7 +7,7 @@ import dateutil.parser
|
|||||||
from flask_jwt_extended import jwt_required
|
from flask_jwt_extended import jwt_required
|
||||||
from flask_restplus import Namespace, Resource, fields
|
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.accounts import Account
|
||||||
from ..models.operations import Operation
|
from ..models.operations import Operation
|
||||||
|
|
||||||
@ -71,33 +71,33 @@ category_model = ns.model('Category', {
|
|||||||
description='Total income for the category')
|
description='Total income for the category')
|
||||||
})
|
})
|
||||||
|
|
||||||
# OHLC model.
|
# Daily balance model.
|
||||||
ohlc_model = ns.model('OHLC', {
|
daily_balance_model = ns.model('Daily balance', {
|
||||||
'operation_date': fields.DateTime(
|
'operation_date': fields.Date(
|
||||||
dt_format='iso8601',
|
dt_format='iso8601',
|
||||||
readonly=True,
|
readonly=True,
|
||||||
required=True,
|
required=True,
|
||||||
description='Date of the OHLC object'
|
description='Date'
|
||||||
),
|
),
|
||||||
'open': fields.Float(
|
'expenses': fields.Float(
|
||||||
readonly=True,
|
readonly=True,
|
||||||
required=True,
|
required=True,
|
||||||
description='Open value'
|
description='Expenses'
|
||||||
),
|
),
|
||||||
'high': fields.Float(
|
'revenues': fields.Float(
|
||||||
readonly=True,
|
readonly=True,
|
||||||
required=True,
|
required=True,
|
||||||
description='High value'
|
description='Revenues'
|
||||||
),
|
),
|
||||||
'low': fields.Float(
|
'income': fields.Float(
|
||||||
readonly=True,
|
readonly=True,
|
||||||
required=True,
|
required=True,
|
||||||
description='Low value'
|
description='Income'
|
||||||
),
|
),
|
||||||
'close': fields.Float(
|
'balance': fields.Float(
|
||||||
readonly=True,
|
readonly=True,
|
||||||
required=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()
|
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(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
params={
|
params={
|
||||||
'account_id': 'Id of the account to manage'
|
'account_id': 'Id of the account to manage'
|
||||||
},
|
},
|
||||||
responses={
|
responses={
|
||||||
200: ('OK', [ohlc_model]),
|
200: ('OK', [daily_balance_model]),
|
||||||
401: 'Unauthorized',
|
401: 'Unauthorized',
|
||||||
404: 'Account not found'
|
404: 'Account not found'
|
||||||
})
|
})
|
||||||
class OHLCResource(Resource):
|
class DailyBalancesResource(Resource):
|
||||||
"""Resource to expose OHLC."""
|
"""Resource to expose account daily balances."""
|
||||||
|
|
||||||
@ns.expect(range_parser)
|
@ns.expect(range_parser)
|
||||||
@ns.marshal_list_with(ohlc_model)
|
@ns.marshal_list_with(daily_balance_model)
|
||||||
@jwt_required
|
@jwt_required
|
||||||
def get(self, account_id):
|
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()
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user