|
@@ -4,11 +4,9 @@
|
|
|
# All rights reserved
|
|
|
#
|
|
|
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
|
|
|
-from collections import OrderedDict
|
|
|
-from decimal import Decimal, InvalidOperation
|
|
|
-from typing import Callable, List, Union
|
|
|
import itertools
|
|
|
-import numpy as np
|
|
|
+from decimal import Decimal, InvalidOperation
|
|
|
+from typing import Callable, Union
|
|
|
from urwid import (
|
|
|
connect_signal,
|
|
|
AttrMap,
|
|
@@ -22,7 +20,6 @@ from urwid import (
|
|
|
RadioButton,
|
|
|
Text,
|
|
|
)
|
|
|
-from app.db_utils import QueryManager
|
|
|
|
|
|
from .. import COPYRIGHT
|
|
|
from ..widgets import (
|
|
@@ -32,7 +29,9 @@ from ..widgets import (
|
|
|
AutoCompletePopUp,
|
|
|
NoTabCheckBox
|
|
|
)
|
|
|
+from ..db_utils import QueryManager
|
|
|
from . import ActivityManager, show_or_exit
|
|
|
+from .Rating import Rating
|
|
|
|
|
|
class PriceCheck(FocusWidget):
|
|
|
|
|
@@ -98,45 +97,6 @@ class PriceCheck(FocusWidget):
|
|
|
self.update_historic_prices(self.data)
|
|
|
return self
|
|
|
|
|
|
- def update_rating(self, _avg, _min, _max, unit, price=None, quantity=None):
|
|
|
- if None in (_avg, _min, _max):
|
|
|
- return
|
|
|
- current = None if None in (price, quantity or None) else float(price/quantity)
|
|
|
- size = 14
|
|
|
- chars = ['|', *['-']*(size - 2), '|' ]
|
|
|
- rating = [' ']*len(chars)
|
|
|
- _min, _max = min(_min, current or _min), max(_max, current or _max)
|
|
|
- ls = np.linspace(_min, _max, len(chars))
|
|
|
- if current is not None:
|
|
|
- if current <= _avg:
|
|
|
- p = 'badge_good'
|
|
|
- else:
|
|
|
- p = 'badge_bad'
|
|
|
- else:
|
|
|
- p = 'badge_neutral'
|
|
|
- for idx, (e, a) in enumerate(zip(ls, ls[1:])):
|
|
|
- if e <= _avg <= a:
|
|
|
- for c, (_idx,_) in zip(''.join(list(f'{_avg:>5.2f}')[:idx+2]), filter(lambda x: idx-2 < x[0] and x[0]>0, enumerate(chars))):
|
|
|
- chars[_idx] = (p, c)
|
|
|
- chars[0] = '|'
|
|
|
- chars[-1] = '|'
|
|
|
-
|
|
|
- if current is not None and e <= current < a:
|
|
|
- rating[idx] = '^'
|
|
|
-
|
|
|
- if current is not None:
|
|
|
- self.text_fields['rating'].set_text(f"{current:>5.2f}/{unit} {current/_avg - 1:>4.0%}")
|
|
|
-
|
|
|
- if _min != _max:
|
|
|
- if current == _max:
|
|
|
- rating[-1] = '^'
|
|
|
- self.text_fields['spread'].set_text([f"{_min:>5.2f}", ('badge_highlight', chars), f"{_max:<5.2f}"])
|
|
|
- self.text_fields['marker'].set_text(f"{' '*5}{''.join(rating)}{' '*5}")
|
|
|
- else:
|
|
|
- self.text_fields['spread'].set_text('')
|
|
|
- self.text_fields['marker'].set_text('')
|
|
|
-
|
|
|
-
|
|
|
def update_historic_prices(self, data):
|
|
|
organic = None if data['organic'] == 'mixed' else data['organic']
|
|
|
sort = '$/unit' if self.buttons['sort_price'].state else 'ts'
|
|
@@ -151,12 +111,16 @@ class PriceCheck(FocusWidget):
|
|
|
except InvalidOperation:
|
|
|
quantity = None
|
|
|
|
|
|
- if None not in (sort, product, unit):
|
|
|
- self.text_fields['dbview'].set_text(
|
|
|
- self.query_manager.get_historic_prices(
|
|
|
- lambda *args: self.update_rating(*args, unit, price=price, quantity=quantity),
|
|
|
- sort, product, unit, organic=organic)
|
|
|
- )
|
|
|
+ if None in (sort, product, unit):
|
|
|
+ return
|
|
|
+
|
|
|
+ df = self.query_manager.get_historic_prices_data(unit, sort=sort, product=product, organic=organic)
|
|
|
+ _avg, _min, _max = [ x for x in df[['avg','min','max']].iloc[0].apply(float) ]
|
|
|
+ self.rating.update_rating(_avg, _min, _max, unit, price=price, quantity=quantity),
|
|
|
+
|
|
|
+ self.text_fields['dbview'].set_text(
|
|
|
+ self.rating.get_historic_prices(df)
|
|
|
+ )
|
|
|
|
|
|
def __init__(self,
|
|
|
activity_manager: ActivityManager,
|
|
@@ -184,6 +148,10 @@ class PriceCheck(FocusWidget):
|
|
|
self.text_fields = dict((
|
|
|
(k, Text('')) for k in ('dbview', 'spread', 'rating', 'marker')
|
|
|
))
|
|
|
+ self.rating = Rating(dict(filter(
|
|
|
+ lambda x: x[0] in ('spread','rating','marker'),
|
|
|
+ self.text_fields.items()
|
|
|
+ )))
|
|
|
top_pane = [ 'clear', 'exit', ['sort_price', 'sort_date'], ]
|
|
|
|
|
|
left_pane = [
|
|
@@ -202,12 +170,6 @@ class PriceCheck(FocusWidget):
|
|
|
]
|
|
|
bottom_pane = [ 'dbview', ]
|
|
|
|
|
|
- layout = [
|
|
|
- [ top_pane, ],
|
|
|
- [ left_pane, right_pane, ],
|
|
|
- [ badge, ],
|
|
|
- [ bottom_pane, ],
|
|
|
- ]
|
|
|
self.query_manager = query_manager
|
|
|
self.organic_checkbox = self.checkboxes['organic']
|
|
|
connect_signal(self.organic_checkbox, 'postchange', lambda _,v: self.update())
|
|
@@ -255,51 +217,52 @@ class PriceCheck(FocusWidget):
|
|
|
title_align='center',
|
|
|
),
|
|
|
})
|
|
|
- components = OrderedDict((
|
|
|
- ('right_pane', (16, Pile(map(
|
|
|
+ components = {
|
|
|
+ 'top_pane': Columns([
|
|
|
+ (9, Pile([
|
|
|
+ Divider(),
|
|
|
+ AttrMap(self.buttons['clear'], 'streak'),
|
|
|
+ Divider(),
|
|
|
+ ])),
|
|
|
+ LineBox(
|
|
|
+ Columns([ v for k,v in self.buttons.items() if 'sort' in k]),
|
|
|
+ title="Sort price by",
|
|
|
+ title_align='left',
|
|
|
+ ),
|
|
|
+ (9, Pile([
|
|
|
+ Divider(),
|
|
|
+ AttrMap(self.buttons['exit'], 'streak'),
|
|
|
+ Divider(),
|
|
|
+ ]))
|
|
|
+ ], dividechars=1),
|
|
|
+ 'right_pane': (16, Pile(map(
|
|
|
lambda x: _widgets[x] if x is not None else Divider,
|
|
|
right_pane
|
|
|
- )))),
|
|
|
- ('left_pane', Pile([_widgets['product'], _widgets['organic']])),
|
|
|
- ('badge', Pile(map(
|
|
|
+ ))),
|
|
|
+ 'left_pane': Pile(map(
|
|
|
+ lambda x: _widgets[x] if x is not None else Divider,
|
|
|
+ left_pane
|
|
|
+ )),
|
|
|
+ 'badge': Pile(map(
|
|
|
lambda x: _widgets[x] if x is not None else Divider,
|
|
|
badge
|
|
|
- ))),
|
|
|
- ('bottom_pane', _widgets['dbview']),
|
|
|
- ))
|
|
|
-
|
|
|
- left_pane_widget = (Pile([
|
|
|
+ )),
|
|
|
+ 'bottom_pane': _widgets['dbview'],
|
|
|
+ }
|
|
|
+ components.update({
|
|
|
+ 'left_pane': Pile([
|
|
|
components['left_pane'],
|
|
|
LineBox(
|
|
|
AttrMap(components['badge'], 'badge'),
|
|
|
title="Current Price", title_align='left',
|
|
|
)
|
|
|
- ]))
|
|
|
+ ])})
|
|
|
|
|
|
widget = Pile([
|
|
|
banner,
|
|
|
Divider(),
|
|
|
- Columns(
|
|
|
- [
|
|
|
- (9, Pile([
|
|
|
- Divider(),
|
|
|
- AttrMap(self.buttons['clear'], 'streak'),
|
|
|
- Divider(),
|
|
|
- ])),
|
|
|
- LineBox(
|
|
|
- Columns([ v for k,v in self.buttons.items() if 'sort' in k]),
|
|
|
- title="Sort price by",
|
|
|
- title_align='left',
|
|
|
- ),
|
|
|
- (9, Pile([
|
|
|
- Divider(),
|
|
|
- AttrMap(self.buttons['exit'], 'streak'),
|
|
|
- Divider(),
|
|
|
- ]))
|
|
|
- ],
|
|
|
- dividechars=1,
|
|
|
- ),
|
|
|
- Columns((left_pane_widget, components['right_pane']),
|
|
|
+ components['top_pane'],
|
|
|
+ Columns((components['left_pane'], components['right_pane']),
|
|
|
dividechars=0,
|
|
|
),
|
|
|
components['bottom_pane'],
|