|
@@ -7,14 +7,28 @@
|
|
from collections import OrderedDict
|
|
from collections import OrderedDict
|
|
from decimal import Decimal, InvalidOperation
|
|
from decimal import Decimal, InvalidOperation
|
|
from typing import Callable, List, Union
|
|
from typing import Callable, List, Union
|
|
-
|
|
|
|
|
|
+import itertools
|
|
import numpy as np
|
|
import numpy as np
|
|
-import urwid
|
|
|
|
|
|
+from urwid import (
|
|
|
|
+ connect_signal,
|
|
|
|
+ AttrMap,
|
|
|
|
+ Columns,
|
|
|
|
+ Divider,
|
|
|
|
+ Filler,
|
|
|
|
+ LineBox,
|
|
|
|
+ Padding,
|
|
|
|
+ Pile,
|
|
|
|
+ Text,
|
|
|
|
+)
|
|
from app.db_utils import QueryManager
|
|
from app.db_utils import QueryManager
|
|
|
|
|
|
from .. import COPYRIGHT
|
|
from .. import COPYRIGHT
|
|
-from ..widgets import (AutoCompleteEdit, AutoCompleteFloatEdit,
|
|
|
|
- AutoCompletePopUp, FocusWidget, NoTabCheckBox)
|
|
|
|
|
|
+from ..widgets import (
|
|
|
|
+ AutoCompleteEdit,
|
|
|
|
+ AutoCompleteFloatEdit,
|
|
|
|
+ FocusWidget,
|
|
|
|
+ AutoCompletePopUp
|
|
|
|
+)
|
|
from . import ActivityManager, show_or_exit
|
|
from . import ActivityManager, show_or_exit
|
|
|
|
|
|
class PriceCheck(FocusWidget):
|
|
class PriceCheck(FocusWidget):
|
|
@@ -44,42 +58,46 @@ class PriceCheck(FocusWidget):
|
|
|
|
|
|
def apply_changes(self, name, value):
|
|
def apply_changes(self, name, value):
|
|
if name == 'organic':
|
|
if name == 'organic':
|
|
- self.data.update({
|
|
|
|
|
|
+ self.data = {
|
|
name: {
|
|
name: {
|
|
'yes': True, 'no': False,
|
|
'yes': True, 'no': False,
|
|
True: True, False: False,
|
|
True: True, False: False,
|
|
'mixed': 'mixed'
|
|
'mixed': 'mixed'
|
|
}[value]
|
|
}[value]
|
|
- })
|
|
|
|
|
|
+ }
|
|
elif name in self.data:
|
|
elif name in self.data:
|
|
- self.data.update({
|
|
|
|
|
|
+ self.data = {
|
|
name: value,
|
|
name: value,
|
|
- })
|
|
|
|
- #self.update_historic_prices()
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @property
|
|
|
|
+ def data(self):
|
|
|
|
+ ret = dict(itertools.chain(
|
|
|
|
+ [(k, v.get_edit_text()) for k,v in self.edit_fields.items()],
|
|
|
|
+ [(k, v.state) for k,v in self.checkboxes.items()]
|
|
|
|
+ ))
|
|
|
|
+ return ret
|
|
|
|
+
|
|
|
|
+ @data.setter
|
|
|
|
+ def data(self, _data: dict):
|
|
|
|
+ for k,v in _data.items():
|
|
|
|
+ if k in self.edit_fields and v != self.edit_fields[k].get_edit_text():
|
|
|
|
+ self.edit_fields[k].set_edit_text(v)
|
|
|
|
+ if k in self.checkboxes and v != self.checkboxes[k].state:
|
|
|
|
+ self.checkboxes[k].set_state(v)
|
|
|
|
|
|
def clear(self):
|
|
def clear(self):
|
|
- for k in self.data:
|
|
|
|
- if k == 'organic':
|
|
|
|
- self.data[k] = 'mixed'
|
|
|
|
- continue
|
|
|
|
- self.data[k] = ''
|
|
|
|
- self.text_fields['rating'].set_text('')
|
|
|
|
- self.text_fields['spread'].set_text('')
|
|
|
|
- self.text_fields['marker'].set_text('')
|
|
|
|
- self.text_fields['dbview'].set_text('')
|
|
|
|
|
|
+ for (_, ef) in self.edit_fields.items():
|
|
|
|
+ ef.set_edit_text('')
|
|
|
|
+ for (_, cb) in self.checkboxes.items():
|
|
|
|
+ cb.set_state('mixed')
|
|
|
|
+ for (_, tf) in self.text_fields.items():
|
|
|
|
+ tf.set_text('')
|
|
self.update()
|
|
self.update()
|
|
return self
|
|
return self
|
|
|
|
|
|
def update(self):
|
|
def update(self):
|
|
- for k in self.edit_fields:
|
|
|
|
- if self.data[k] != self.edit_fields[k].get_edit_text():
|
|
|
|
- self.edit_fields[k].set_edit_text(self.data[k])
|
|
|
|
-
|
|
|
|
- if self.data['organic'] != self.organic_checkbox.state:
|
|
|
|
- self.organic_checkbox.set_state(self.data['organic'])
|
|
|
|
-
|
|
|
|
- self.update_historic_prices()
|
|
|
|
-
|
|
|
|
|
|
+ self.update_historic_prices(self.data)
|
|
return self
|
|
return self
|
|
|
|
|
|
def update_rating(self, _avg, _min, _max, unit, price=None, quantity=None):
|
|
def update_rating(self, _avg, _min, _max, unit, price=None, quantity=None):
|
|
@@ -121,17 +139,17 @@ class PriceCheck(FocusWidget):
|
|
self.text_fields['marker'].set_text('')
|
|
self.text_fields['marker'].set_text('')
|
|
|
|
|
|
|
|
|
|
- def update_historic_prices(self):
|
|
|
|
- organic = None if self.data['organic'] == 'mixed' else self.data['organic']
|
|
|
|
|
|
+ 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'
|
|
sort = '$/unit' if self.buttons['sort_price'].state else 'ts'
|
|
- product, unit = self.data['product'] or None, self.data['unit'] or None
|
|
|
|
|
|
+ product, unit = data['product'] or None, data['unit'] or None
|
|
try:
|
|
try:
|
|
- price = Decimal(self.data['price'])
|
|
|
|
|
|
+ price = Decimal(data['price'])
|
|
except InvalidOperation:
|
|
except InvalidOperation:
|
|
price = None
|
|
price = None
|
|
|
|
|
|
try:
|
|
try:
|
|
- quantity = Decimal(self.data['quantity'])
|
|
|
|
|
|
+ quantity = Decimal(data['quantity'])
|
|
except InvalidOperation:
|
|
except InvalidOperation:
|
|
quantity = None
|
|
quantity = None
|
|
|
|
|
|
@@ -145,77 +163,75 @@ class PriceCheck(FocusWidget):
|
|
def __init__(self,
|
|
def __init__(self,
|
|
activity_manager: ActivityManager,
|
|
activity_manager: ActivityManager,
|
|
query_manager: QueryManager,
|
|
query_manager: QueryManager,
|
|
- fields: List[str],
|
|
|
|
- top_pane, left_pane, right_pane, bottom_pane, badge,
|
|
|
|
|
|
+ buttons: OrderedDict, button_group: List,
|
|
|
|
+ checkboxes: OrderedDict,
|
|
|
|
+ edit_fields: OrderedDict,
|
|
|
|
+ text_fields: OrderedDict,
|
|
autocomplete_cb: Callable[[
|
|
autocomplete_cb: Callable[[
|
|
Union[AutoCompleteEdit, AutoCompleteFloatEdit], str, dict
|
|
Union[AutoCompleteEdit, AutoCompleteFloatEdit], str, dict
|
|
], None],
|
|
], None],
|
|
):
|
|
):
|
|
|
|
+ top_pane = [ 'clear', 'exit', ['sort_price', 'sort_date'], ]
|
|
|
|
+
|
|
|
|
+ left_pane = [
|
|
|
|
+ 'product',
|
|
|
|
+ 'organic',
|
|
|
|
+ ]
|
|
|
|
+ badge = [
|
|
|
|
+ 'rating',
|
|
|
|
+ 'spread',
|
|
|
|
+ 'marker',
|
|
|
|
+ ]
|
|
|
|
+ right_pane = [
|
|
|
|
+ 'unit',
|
|
|
|
+ 'quantity',
|
|
|
|
+ 'price',
|
|
|
|
+ ]
|
|
|
|
+ bottom_pane = [ 'dbview', ]
|
|
|
|
+
|
|
|
|
+ layout = [
|
|
|
|
+ [ top_pane, ],
|
|
|
|
+ [ left_pane, right_pane, ],
|
|
|
|
+ [ badge, ],
|
|
|
|
+ [ bottom_pane, ],
|
|
|
|
+ ]
|
|
self.query_manager = query_manager
|
|
self.query_manager = query_manager
|
|
- self.top_pane = top_pane
|
|
|
|
- self.left_pane = left_pane
|
|
|
|
- self.right_pane = right_pane
|
|
|
|
- self.bottom_pane = bottom_pane
|
|
|
|
- self.badge = badge
|
|
|
|
- self.edit_fields = OrderedDict()
|
|
|
|
- self.text_fields = OrderedDict()
|
|
|
|
- self.data = OrderedDict()
|
|
|
|
- self.organic_checkbox = NoTabCheckBox(
|
|
|
|
- u"Organic",
|
|
|
|
- state='mixed',
|
|
|
|
- )
|
|
|
|
- urwid.connect_signal(self.organic_checkbox, 'change', lambda _,v: self.apply_changes('organic', v))
|
|
|
|
-
|
|
|
|
- fields = [f for f in fields ]
|
|
|
|
- for k in fields:
|
|
|
|
- if k in self.right_pane and k != 'unit':
|
|
|
|
- ef = AutoCompleteFloatEdit(('bg', k))
|
|
|
|
- elif k != 'organic':
|
|
|
|
- ef = AutoCompleteEdit(('bg', k))
|
|
|
|
- elif k == 'organic':
|
|
|
|
- self.data[k] = 'mixed'
|
|
|
|
- continue
|
|
|
|
|
|
+ self.edit_fields = edit_fields
|
|
|
|
+ self.text_fields = text_fields
|
|
|
|
+ self.buttons = buttons
|
|
|
|
+ self.checkboxes = checkboxes
|
|
|
|
+ self.organic_checkbox = self.checkboxes['organic']
|
|
|
|
+ connect_signal(self.organic_checkbox, 'postchange', lambda _,v: self.update())
|
|
|
|
+ for (k, ef) in self.edit_fields.items():
|
|
|
|
+ connect_signal(ef, 'postchange', lambda _,v: self.update())
|
|
|
|
+ connect_signal(ef, 'apply', lambda w, name: autocomplete_cb(w, name, self.data))
|
|
|
|
|
|
- self.data[k] = ''
|
|
|
|
- urwid.connect_signal(ef, 'change', lambda w, v: self.apply_changes(w.name, v))
|
|
|
|
- urwid.connect_signal(ef, 'apply', lambda w, name: autocomplete_cb(w, name, self.data))
|
|
|
|
- self.edit_fields[k] = ef
|
|
|
|
|
|
+ for b in button_group:
|
|
|
|
+ connect_signal(b, 'postchange',lambda *_: self.update())
|
|
|
|
|
|
- self.buttons = OrderedDict()
|
|
|
|
- group = []
|
|
|
|
- for k in top_pane:
|
|
|
|
- if k != 'sort':
|
|
|
|
- self.buttons.update({ k: urwid.Button(('streak', f'{k.title()}')) })
|
|
|
|
- continue
|
|
|
|
- self.buttons.update({ f'{k}_price': urwid.RadioButton(group, ('streak', u'Best'), state="first True") })
|
|
|
|
- self.buttons.update({ f'{k}_date': urwid.RadioButton(group, ('streak', u'Last'), state="first True") })
|
|
|
|
- for button in group:
|
|
|
|
- urwid.connect_signal(button, 'postchange', lambda *args: self.update_historic_prices())
|
|
|
|
|
|
+ connect_signal(self.buttons['clear'], 'click', lambda x: self.clear().update())
|
|
|
|
+ connect_signal(self.buttons['exit'], 'click', lambda x: show_or_exit('esc'))
|
|
|
|
|
|
- urwid.connect_signal(self.buttons['clear'], 'click', lambda x: self.clear().update())
|
|
|
|
- urwid.connect_signal(self.buttons['exit'], 'click', lambda x: show_or_exit('esc'))
|
|
|
|
-
|
|
|
|
- dbview, spread, rating, marker = [ urwid.Text('') for i in range(0,4) ]
|
|
|
|
-
|
|
|
|
- self.text_fields.update({
|
|
|
|
- 'dbview': dbview,
|
|
|
|
- 'spread': spread,
|
|
|
|
- 'rating': rating,
|
|
|
|
- 'marker': marker,
|
|
|
|
- })
|
|
|
|
|
|
|
|
self.clear()
|
|
self.clear()
|
|
|
|
|
|
- header = urwid.Text(u'Price Check', 'center')
|
|
|
|
- _copyright = urwid.Text(COPYRIGHT, 'center')
|
|
|
|
|
|
+ header = Text(u'Price Check', 'center')
|
|
|
|
+ _copyright = Text(COPYRIGHT, 'center')
|
|
|
|
|
|
- banner = urwid.Pile([
|
|
|
|
- urwid.Padding(header, 'center', width=('relative', 100)),
|
|
|
|
- urwid.Padding(_copyright, 'center', width=('relative', 100)),
|
|
|
|
|
|
+ banner = Pile([
|
|
|
|
+ Padding(header, 'center', width=('relative', 100)),
|
|
|
|
+ Padding(_copyright, 'center', width=('relative', 100)),
|
|
])
|
|
])
|
|
- banner = urwid.AttrMap(banner, 'banner')
|
|
|
|
- fields = dict([
|
|
|
|
- (k, urwid.LineBox(urwid.AttrMap(
|
|
|
|
|
|
+ banner = AttrMap(banner, 'banner')
|
|
|
|
+
|
|
|
|
+ _widgets = dict(itertools.chain(*[
|
|
|
|
+ [(k, v) for k,v in x] for x in map(lambda x: x.items(), [
|
|
|
|
+ self.edit_fields, self.text_fields, self.checkboxes
|
|
|
|
+ ])
|
|
|
|
+ ]))
|
|
|
|
+
|
|
|
|
+ _widgets.update([
|
|
|
|
+ (k, LineBox(AttrMap(
|
|
AutoCompletePopUp(
|
|
AutoCompletePopUp(
|
|
self.edit_fields[k],
|
|
self.edit_fields[k],
|
|
self.apply_choice,
|
|
self.apply_choice,
|
|
@@ -223,68 +239,64 @@ class PriceCheck(FocusWidget):
|
|
), 'streak'), title=k.title(), title_align='left')
|
|
), 'streak'), title=k.title(), title_align='left')
|
|
) for k in self.edit_fields
|
|
) for k in self.edit_fields
|
|
])
|
|
])
|
|
- fields['organic'] = urwid.AttrMap(self.organic_checkbox, 'bg')
|
|
|
|
- fields.update({
|
|
|
|
- 'dbview': urwid.LineBox(
|
|
|
|
- urwid.AttrMap(dbview, 'streak'),
|
|
|
|
|
|
+ _widgets.update({
|
|
|
|
+ 'dbview': LineBox(
|
|
|
|
+ AttrMap(self.text_fields['dbview'], 'streak'),
|
|
title="Historic Prices",
|
|
title="Historic Prices",
|
|
title_align='center',
|
|
title_align='center',
|
|
),
|
|
),
|
|
- 'spread': spread,
|
|
|
|
- 'rating': rating,
|
|
|
|
- 'marker': marker,
|
|
|
|
})
|
|
})
|
|
-
|
|
|
|
- right_pane_widget = (16, urwid.Pile(map(
|
|
|
|
- lambda x: fields[x] if x is not None else urwid.Divider(),
|
|
|
|
- self.right_pane
|
|
|
|
- )))
|
|
|
|
- left_pane_widget = (urwid.Pile(map(
|
|
|
|
- lambda x: fields[x] if x is not None else urwid.Divider(),
|
|
|
|
- self.left_pane,
|
|
|
|
- )))
|
|
|
|
- left_pane_widget = (urwid.Pile([
|
|
|
|
- left_pane_widget,
|
|
|
|
- urwid.LineBox(urwid.AttrMap(
|
|
|
|
- urwid.Pile(map(
|
|
|
|
- lambda x: fields[x] if x is not None else urwid.Divider(),
|
|
|
|
- self.badge,
|
|
|
|
- )), 'badge',),
|
|
|
|
- title="Current Price",
|
|
|
|
- title_align='left',
|
|
|
|
|
|
+ components = OrderedDict((
|
|
|
|
+ ('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(
|
|
|
|
+ lambda x: _widgets[x] if x is not None else Divider,
|
|
|
|
+ badge
|
|
|
|
+ ))),
|
|
|
|
+ ('bottom_pane', _widgets['dbview']),
|
|
|
|
+ ))
|
|
|
|
+
|
|
|
|
+ left_pane_widget = (Pile([
|
|
|
|
+ components['left_pane'],
|
|
|
|
+ LineBox(
|
|
|
|
+ AttrMap(components['badge'], 'badge'),
|
|
|
|
+ title="Current Price", title_align='left',
|
|
)
|
|
)
|
|
]))
|
|
]))
|
|
|
|
|
|
- widget = urwid.Pile([
|
|
|
|
|
|
+ widget = Pile([
|
|
banner,
|
|
banner,
|
|
- urwid.Divider(),
|
|
|
|
- urwid.Columns(
|
|
|
|
|
|
+ Divider(),
|
|
|
|
+ Columns(
|
|
[
|
|
[
|
|
- (9, urwid.Pile([
|
|
|
|
- urwid.Divider(),
|
|
|
|
- urwid.AttrMap(self.buttons['clear'], 'streak'),
|
|
|
|
- urwid.Divider(),
|
|
|
|
|
|
+ (9, Pile([
|
|
|
|
+ Divider(),
|
|
|
|
+ AttrMap(self.buttons['clear'], 'streak'),
|
|
|
|
+ Divider(),
|
|
])),
|
|
])),
|
|
- urwid.LineBox(
|
|
|
|
- urwid.Columns([ v for k,v in self.buttons.items() if 'sort' in k]),
|
|
|
|
|
|
+ LineBox(
|
|
|
|
+ Columns([ v for k,v in self.buttons.items() if 'sort' in k]),
|
|
title="Sort price by",
|
|
title="Sort price by",
|
|
title_align='left',
|
|
title_align='left',
|
|
),
|
|
),
|
|
- (9, urwid.Pile([
|
|
|
|
- urwid.Divider(),
|
|
|
|
- urwid.AttrMap(self.buttons['exit'], 'streak'),
|
|
|
|
- urwid.Divider(),
|
|
|
|
|
|
+ (9, Pile([
|
|
|
|
+ Divider(),
|
|
|
|
+ AttrMap(self.buttons['exit'], 'streak'),
|
|
|
|
+ Divider(),
|
|
]))
|
|
]))
|
|
],
|
|
],
|
|
dividechars=1,
|
|
dividechars=1,
|
|
),
|
|
),
|
|
- urwid.Columns((left_pane_widget, right_pane_widget),
|
|
|
|
|
|
+ Columns((left_pane_widget, components['right_pane']),
|
|
dividechars=0,
|
|
dividechars=0,
|
|
),
|
|
),
|
|
- *[ fields[c] if c is not None else urwid.Divider() for c in self.bottom_pane ],
|
|
|
|
|
|
+ components['bottom_pane'],
|
|
])
|
|
])
|
|
- widget = urwid.Filler(widget, 'top')
|
|
|
|
- widget = urwid.AttrMap(widget, 'bg')
|
|
|
|
|
|
+ widget = Filler(widget, 'top')
|
|
|
|
+ widget = AttrMap(widget, 'bg')
|
|
widget.original_widget.original_widget.set_focus_path([3,0,0])
|
|
widget.original_widget.original_widget.set_focus_path([3,0,0])
|
|
super().__init__(widget, 4, [3,0,0,0], [
|
|
super().__init__(widget, 4, [3,0,0,0], [
|
|
[0, 0,], [0,1,],
|
|
[0, 0,], [0,1,],
|