diagram: interpolator ergänzt,

asset: berechnung 'nextupdate' korrigiert
This commit is contained in:
Frederik Jaeckel 2022-11-28 23:15:28 +01:00
parent d1421403b1
commit 99e18094aa
7 changed files with 303 additions and 67 deletions

190
asset.py
View file

@ -6,17 +6,22 @@
from trytond.model import ModelView, ModelSQL, fields
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.pyson import Eval, Bool, And, If
from trytond.pyson import Eval, Bool, And, If, Date
from trytond.report import Report
from decimal import Decimal
from datetime import time
from sql.functions import CurrentDate, CurrentTimestamp, Round
from sql.functions import CurrentDate, CurrentTimestamp, Round, Extract
from sql.conditionals import Case, Coalesce
from sql import Literal
digits_percent = 2
sel_updtdays = [
('work', 'Mon - Fri'),
('week', 'Mon - Sun'),
]
class Asset(ModelSQL, ModelView):
'Asset'
@ -45,9 +50,11 @@ class Asset(ModelSQL, ModelView):
model_name='investment.rate')
rate = fields.Function(fields.Numeric(string='Current Rate',
readonly=True, digits=(16, Eval('currency_digits', 4)),
depends=['currency_digits']), 'get_rate_data')
depends=['currency_digits']),
'get_rate_data', searcher='search_rate')
date = fields.Function(fields.Date(string='Date', readonly=True,
help='Date of current rate'), 'get_rate_data')
help='Date of current rate'),
'get_rate_data', searcher='search_date')
company_currency = fields.Function(fields.Many2One(readonly=True,
string='Company Currency', states={'invisible': True},
@ -74,6 +81,8 @@ class Asset(ModelSQL, ModelView):
updtsource = fields.Many2One(string='Update Source',
help='Select a source for the course update.',
ondelete='SET NULL', model_name='investment.source')
updtdays = fields.Selection(string='Select days', required=True,
selection=sel_updtdays)
updttime = fields.Time(string='Time',
states={
'readonly': ~Bool(Eval('updtsource')),
@ -114,7 +123,7 @@ class Asset(ModelSQL, ModelView):
def view_attributes(cls):
return super().view_attributes() + [
('/tree', 'visual',
If(Eval('date') < Date(delta_days=-5), 'muted',
If(Eval('date', Date()) < Date(delta_days=-5), 'muted',
If(Eval('change_day1', 0) < 0, 'warning',
If(Eval('change_day1', 0) > 0, 'success', '')
))
@ -150,43 +159,10 @@ class Asset(ModelSQL, ModelView):
return time(14, 0)
@classmethod
def get_rate_data(cls, assets, names):
""" get date and rate of asset
def default_updtdays(cls):
""" default: mon - fri
"""
pool = Pool()
Asset = pool.get('investment.asset')
Rate = pool.get('investment.rate')
tab_asset = Asset.__table__()
tab_rate = Rate.__table__()
cursor = Transaction().connection.cursor()
query = tab_asset.join(tab_rate,
condition=tab_asset.id==tab_rate.asset
).select(
tab_asset.id,
tab_rate.rate,
tab_rate.date,
distinct_on=[tab_asset.id],
order_by=[tab_asset.id, tab_rate.date.desc],
where=tab_asset.id.in_([x.id for x in assets]),
)
cursor.execute(*query)
records = cursor.fetchall()
result = {x:{y.id: None for y in assets} for x in names}
for record in records:
(id1, rate1, date1) = record
asset = Asset(id1)
exp = Decimal(Decimal(1) / 10 ** (asset.currency_digits or 4))
values = {'rate': record[1].quantize(exp), 'date': record[2]}
for name in names:
result[name][record[0]] = values[name]
return result
return 'work'
@fields.depends('updtsource', 'updttime')
def on_change_updtsource(self):
@ -246,6 +222,106 @@ class Asset(ModelSQL, ModelView):
if self.company.currency.id != self.currency.id:
return self.company.currency.id
@classmethod
def get_rate_data_sql(cls):
""" get sql for rate/date
"""
pool = Pool()
Asset = pool.get('investment.asset')
Rate = pool.get('investment.rate')
tab_asset = Asset.__table__()
tab_rate = Rate.__table__()
query = tab_asset.join(tab_rate,
condition=tab_asset.id==tab_rate.asset
).select(
tab_asset.id,
Round(tab_rate.rate, tab_asset.currency_digits).as_('rate'),
tab_rate.date,
distinct_on=[tab_asset.id],
order_by=[tab_asset.id, tab_rate.date.desc],
)
return (query, tab_asset)
@classmethod
def get_rate_data(cls, assets, names):
""" get date and rate of asset
"""
cursor = Transaction().connection.cursor()
(query, tab_asset) = cls.get_rate_data_sql()
query.where=tab_asset.id.in_([x.id for x in assets])
cursor.execute(*query)
records = cursor.fetchall()
result = {x:{y.id: None for y in assets} for x in names}
for record in records:
(id1, rate1, date1) = record
asset = Asset(id1)
exp = Decimal(Decimal(1) / 10 ** (asset.currency_digits or 4))
values = {'rate': record[1].quantize(exp), 'date': record[2]}
for name in names:
result[name][record[0]] = values[name]
return result
@classmethod
def search_date(cls, names, clause):
""" search in date
"""
(tab_query, tab_asset) = cls.get_rate_data_sql()
Operator = fields.SQL_OPERATORS[clause[1]]
query = tab_query.select(
tab_query.id,
where=Operator(tab_query.date, clause[2]),
)
return [('id', 'in', query)]
@classmethod
def search_rate(cls, names, clause):
""" search in rate
"""
(tab_query, tab_asset) = cls.get_rate_data_sql()
Operator = fields.SQL_OPERATORS[clause[1]]
query = tab_query.select(
tab_query.id,
where=Operator(tab_query.rate, clause[2]),
)
return [('id', 'in', query)]
@staticmethod
def order_date(tables):
""" order date
"""
(tab_query, tab_asset) = Asset.get_rate_data_sql()
table, _ = tables[None]
query = tab_query.select(
tab_query.date,
where=tab_query.id==table.id,
)
return [query]
@staticmethod
def order_rate(tables):
""" order rate
"""
(tab_query, tab_asset) = Asset.get_rate_data_sql()
table, _ = tables[None]
query = tab_query.select(
tab_query.rate,
where=tab_query.id==table.id,
)
return [query]
@classmethod
def get_percentage_sql(cls, name_lst, select_date=True):
""" get table for percentages and dates,
@ -331,7 +407,7 @@ class Asset(ModelSQL, ModelView):
table, _ = tables[None]
query = tab_asset.select(
getattr(tab_asset, 'day1'),
tab_asset.day1,
where=tab_asset.id==table.id,
)
return [query]
@ -345,7 +421,7 @@ class Asset(ModelSQL, ModelView):
table, _ = tables[None]
query = tab_asset.select(
getattr(tab_asset, 'month1'),
tab_asset.month1,
where=tab_asset.id==table.id,
)
return [query]
@ -359,7 +435,7 @@ class Asset(ModelSQL, ModelView):
table, _ = tables[None]
query = tab_asset.select(
getattr(tab_asset, 'month3'),
tab_asset.month3,
where=tab_asset.id==table.id,
)
return [query]
@ -373,7 +449,7 @@ class Asset(ModelSQL, ModelView):
table, _ = tables[None]
query = tab_asset.select(
getattr(tab_asset, 'month6'),
tab_asset.month6,
where=tab_asset.id==table.id,
)
return [query]
@ -387,7 +463,7 @@ class Asset(ModelSQL, ModelView):
table, _ = tables[None]
query = tab_asset.select(
getattr(tab_asset, 'month12'),
tab_asset.month12,
where=tab_asset.id==table.id,
)
return [query]
@ -443,16 +519,30 @@ class Asset(ModelSQL, ModelView):
context = Transaction().context
query_date = context.get('qdate', CurrentDate() - Literal(1))
query = tab_asset.join(tab_rate,
# get last date of rate
tab_date = tab_asset.join(tab_rate,
condition=tab_asset.id == tab_rate.asset,
type_ = 'LEFT OUTER',
).select(
tab_asset.id,
((Coalesce(tab_rate.date, query_date) + Literal(1)) + \
tab_asset.updttime).as_('updttime'),
(Coalesce(tab_rate.date, query_date) + Literal(1)).as_('date'),
tab_asset.updtdays,
tab_asset.updttime,
distinct_on = [tab_asset.id],
order_by = [tab_asset.id, tab_rate.date.desc],
where=(tab_asset.updtsource != None),
where=tab_asset.updtsource != None,
)
query = tab_date.select(
tab_date.id,
(Case(
((tab_date.updtdays == 'work') & \
(Extract('dow', tab_date.date) == 0), tab_date.date + Literal(1)),
((tab_date.updtdays == 'work') & \
(Extract('dow', tab_date.date) == 6), tab_date.date + Literal(2)),
else_ = tab_date.date,
) + tab_date.updttime).as_('updttime'),
)
return query

View file

@ -89,6 +89,43 @@ class GraphDef(metaclass=PoolMeta):
class ChartPoint(metaclass=PoolMeta):
__name__ = 'diagram.point'
@classmethod
def get_interpolated_val(cls, keyname, query_date):
""" query two neighbour-values to
interpolate missing value
"""
Rate = Pool().get('investment.rate')
if keyname is None:
return None
# check if query is for us
if keyname.startswith('asset'):
asset_id = int(keyname[len('asset'):])
before = Rate.search([
('date', '<', query_date),
('asset.id', '=', asset_id),
], limit=1, order=[('date', 'DESC')])
after = Rate.search([
('date', '>', query_date),
('asset.id', '=', asset_id),
], limit=1, order=[('date', 'ASC')])
if (len(before) == 1) and (len(after) == 1):
result = cls.interpolate_linear(
(after[0].date, after[0].rate),
(before[0].date, before[0].rate),
query_date
)
return result
elif len(before) == 1:
return before[0].rate
elif len(after) == 1:
return after[0].rate
return super(ChartPoint, cls).get_interpolated_val(keyname, query_date)
@classmethod
def get_table_parts(cls):
""" return a list of tables to union,

View file

@ -238,6 +238,18 @@ msgctxt "help:investment.asset,change_month12:"
msgid "percentage change in value during 1 year"
msgstr "Prozentuale Wertänderung während 1 Jahr"
msgctxt "field:investment.asset,updtdays:"
msgid "Select days"
msgstr "Tage wählen"
msgctxt "selection:investment.asset,updtdays:"
msgid "Mon - Fri"
msgstr "Mo - Fr"
msgctxt "selection:investment.asset,updtdays:"
msgid "Mon - Sun"
msgstr "Mo - So"
#####################
# investment.source #

View file

@ -210,6 +210,18 @@ msgctxt "help:investment.asset,change_month12:"
msgid "percentage change in value during 1 year"
msgstr "percentage change in value during 1 year"
msgctxt "field:investment.asset,updtdays:"
msgid "Select days"
msgstr "Select days"
msgctxt "selection:investment.asset,updtdays:"
msgid "Mon - Fri"
msgstr "Mon - Fri"
msgctxt "selection:investment.asset,updtdays:"
msgid "Mon - Sun"
msgstr "Mon - Sun"
msgctxt "model:investment.source,name:"
msgid "Online Source"
msgstr "Online Source"

View file

@ -112,6 +112,82 @@ class AssetTestCase(ModuleTestCase):
self.assertEqual(asset.rec_name, 'Product 1 - 2.4500 usd/Unit [05/15/2022]')
self.assertEqual(Asset.search_count([('name', '=', 'Product 1')]), 1)
@with_transaction()
def test_asset_order_and_search_rate_and_date(self):
""" create asset, check order of rate + date
"""
Asset = Pool().get('investment.asset')
company = self.prep_asset_company()
product1 = self.prep_asset_product(
name='Product 1',
description='some asset')
product2 = self.prep_asset_product(
name='Product 2',
description='some asset')
asset1 = self.prep_asset_item(
company=company,
product = product1)
asset2 = self.prep_asset_item(
company=company,
product = product2)
Asset.write(*[
[asset1],
{
'rates': [('create', [{
'date': date(2022, 5, 18),
'rate': Decimal('3.5'),
}, {
'date': date(2022, 5, 15),
'rate': Decimal('2.45'),
}])],
},
[asset2],
{
'rates': [('create', [{
'date': date(2022, 5, 17),
'rate': Decimal('2.6'),
}, {
'date': date(2022, 5, 14),
'rate': Decimal('2.4'),
}])],
},
])
self.assertEqual(asset1.rec_name, 'Product 1 - 3.5000 usd/Unit [05/18/2022]')
self.assertEqual(asset2.rec_name, 'Product 2 - 2.6000 usd/Unit [05/17/2022]')
assets = Asset.search([], order=[('date', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].date, date(2022, 5, 17))
self.assertEqual(assets[1].date, date(2022, 5, 18))
assets = Asset.search([], order=[('date', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].date, date(2022, 5, 18))
self.assertEqual(assets[1].date, date(2022, 5, 17))
assets = Asset.search([], order=[('rate', 'ASC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].rate, Decimal('2.6'))
self.assertEqual(assets[1].rate, Decimal('3.5'))
assets = Asset.search([], order=[('rate', 'DESC')])
self.assertEqual(len(assets), 2)
self.assertEqual(assets[0].rate, Decimal('3.5'))
self.assertEqual(assets[1].rate, Decimal('2.6'))
self.assertEqual(Asset.search_count([
('date', '=', date(2022, 5, 17)),
]), 1)
self.assertEqual(Asset.search_count([
('date', '>=', date(2022, 5, 17)),
]), 2)
self.assertEqual(Asset.search_count([
('date', '<', date(2022, 5, 17)),
]), 0)
@with_transaction()
def test_asset_percentages_dateselect1(self):
""" create asset, add rates, check selection of
@ -535,7 +611,7 @@ class AssetTestCase(ModuleTestCase):
}])
with Transaction().set_context({
'qdate': date(2022, 10, 14),
'qdate': date(2022, 10, 14), # friday
}):
# re-read to make context work
asset2, = Asset.browse([asset.id])
@ -543,21 +619,24 @@ class AssetTestCase(ModuleTestCase):
self.assertEqual(asset2.updtsource.rec_name, 'Source 1')
self.assertEqual(asset2.updttime, time(10, 45))
self.assertEqual(len(asset2.rates), 0)
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 15, 10, 45))
# qdate = 2022-10-14 simulates existence of record at this day
# next call would be the 15. - but its saturday,
# next-call-date is moved to 17.
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 17, 10, 45))
self.assertEqual(
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 15, 10, 45))]),
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 17, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 15, 10, 45))]),
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 17, 10, 45))]),
1)
# add rate at yesterday
# add rate at next monday
Asset.write(*[
[asset],
{
'rates': [('create', [{
'date': date(2022, 10, 14),
'date': date(2022, 10, 17), # monday
'rate': Decimal('1.5'),
}])],
}])
@ -567,14 +646,14 @@ class AssetTestCase(ModuleTestCase):
self.assertEqual(asset.updtsource.rec_name, 'Source 1')
self.assertEqual(asset.updttime, time(10, 45))
self.assertEqual(len(asset.rates), 1)
self.assertEqual(asset.rates[0].date, date(2022, 10, 14))
self.assertEqual(asset.nextupdate, datetime(2022, 10, 15, 10, 45))
self.assertEqual(asset.rates[0].date, date(2022, 10, 17))
self.assertEqual(asset.nextupdate, datetime(2022, 10, 18, 10, 45))
self.assertEqual(
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 15, 10, 45))]),
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 18, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 15, 10, 45))]),
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 18, 10, 45))]),
1)
# add rate at today
@ -582,7 +661,7 @@ class AssetTestCase(ModuleTestCase):
[asset],
{
'rates': [('create', [{
'date': date(2022, 10, 15),
'date': date(2022, 10, 18),
'rate': Decimal('1.5'),
}])],
}])
@ -592,14 +671,14 @@ class AssetTestCase(ModuleTestCase):
self.assertEqual(asset2.updtsource.rec_name, 'Source 1')
self.assertEqual(asset2.updttime, time(10, 45))
self.assertEqual(len(asset2.rates), 2)
self.assertEqual(asset2.rates[0].date, date(2022, 10, 15))
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 16, 10, 45))
self.assertEqual(asset2.rates[0].date, date(2022, 10, 18))
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 19, 10, 45))
self.assertEqual(
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 15, 10, 45))]),
Asset.search_count([('nextupdate', '<', datetime(2022, 10, 19, 10, 45))]),
0)
self.assertEqual(
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 15, 10, 45))]),
Asset.search_count([('nextupdate', '>=', datetime(2022, 10, 19, 10, 45))]),
1)
@with_transaction()

View file

@ -68,7 +68,7 @@ class SourceTestCase(ModuleTestCase):
}])
with Transaction().set_context({
'qdate': date(2022, 10, 1),
'qdate': date(2022, 10, 1), # saturday
'qdatetime': datetime(2022, 10, 2, 10, 0, 0),
}):
asset2, = Asset.browse([asset])
@ -77,9 +77,11 @@ class SourceTestCase(ModuleTestCase):
self.assertEqual(asset2.secsymb, '1472977')
self.assertEqual(asset2.updttime, time(14, 0))
self.assertEqual(asset2.updtsource.rec_name, 'Source 1')
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 2, 14, 0))
self.assertEqual(asset2.updtdays, 'work')
self.assertEqual(asset2.nextupdate, datetime(2022, 10, 3, 14, 0))
self.assertEqual(len(asset.rates), 0)
# fake server-response
resp1 = Response()
resp1._content = """<html><body>Response from finance-server

View file

@ -61,6 +61,10 @@ full copyright notices and license terms. -->
<field name="updtsource"/>
<label name="updttime"/>
<field name="updttime"/>
<label id="labupdt" colspan="2" string=" "/>
<label name="updtdays"/>
<field name="updtdays"/>
</page>
</notebook>