Jelajahi Sumber

load recipe on startup

Daniel Sheffield 2 tahun lalu
induk
melakukan
1d38bb07cf
3 mengubah file dengan 76 tambahan dan 17 penghapusan
  1. 68 17
      app/activities/RecipeEditor.py
  2. 3 0
      app/parse_recipe.py
  3. 5 0
      recipe.py

+ 68 - 17
app/activities/RecipeEditor.py

@@ -5,9 +5,8 @@
 #
 # THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
 import itertools
-from decimal import Decimal, InvalidOperation
 from itertools import chain
-from typing import Callable, Union
+from typing import List, Tuple, Union, Iterable, Callable
 from urwid import (
     connect_signal,
     AttrMap,
@@ -21,6 +20,7 @@ from urwid import (
     Pile,
     Text,
 )
+from urwid.numedit import FloatEdit
 
 from .. import COPYRIGHT
 from ..widgets import (
@@ -52,6 +52,21 @@ def in_same_row(name):
         _, row = name.split('#', 1)
     return lambda x: x[0][1] == int(row)
 
+def unzip(iter: List[Tuple[AutoCompleteEdit, FloatEdit, AutoCompleteEdit]]) -> Tuple[
+    List[AutoCompleteEdit], List[FloatEdit], List[AutoCompleteEdit]
+]:
+    return zip(*iter)
+
+def extract_values(x: Union[List[AutoCompleteFloatEdit], List[FloatEdit]]) -> Iterable[str]:
+    if isinstance(x, list) or isinstance(x, tuple):
+        if len(x) == 0:
+            return []
+        return ( v.get_edit_text() for v in x )
+    raise Exception(f"Unsupported type: {type(x)}")
+
+def to_named_value(name: str) -> Callable[[str], Tuple[str,str]]:
+    return lambda e: (f'{name}#{e[0]}', e[1])
+
 class RecipeEditor(FocusWidget):
 
     def keypress(self, size, key):
@@ -65,6 +80,8 @@ class RecipeEditor(FocusWidget):
             self.advance_focus()
         elif key == 'shift tab':
             self.advance_focus(reverse=True)
+        elif key == 'ctrl delete':
+            self.clear()
         else:
             return super().keypress(size, key)
 
@@ -95,9 +112,12 @@ class RecipeEditor(FocusWidget):
 
     @property
     def data(self):
+        zipped = zip(
+            ['product', 'quantity', 'unit'],
+            map(extract_values, unzip(self.ingredients)),
+        )
         ret = dict(itertools.chain(
-            [ (f'product#{idx}', v[0].get_edit_text()) for idx,v in enumerate(self.ingredients) ],
-            [ (f'unit#{idx}', v[1].get_edit_text()) for idx, v in enumerate(self.ingredients) ],
+            *[ map(to_named_value(n), enumerate(l)) for n,l in zipped ],
             [ ('organic', self.organic.state) ]
         ))
         return ret
@@ -106,8 +126,12 @@ class RecipeEditor(FocusWidget):
     def data(self, _data: dict):
         for k,v in _data.items():
             if len(k.split('#')) > 1:
-                name, idx = k.split('#',1)
-                self.ingredients[int(idx)][0 if name == 'product' else 1 ].set_edit_text(v)
+                name, idx = k.split('#', 1)
+                w = self.ingredients[int(idx)][ next(( pos for pos, n in zip(
+                    [0, 1, 2],
+                    ['product', 'quantity', 'unit']
+                ) if n == name ))]
+                w.set_edit_text(v)
             if k == 'organic':
                 self.organic.set_state(v)
 
@@ -115,7 +139,9 @@ class RecipeEditor(FocusWidget):
         for ef in self.ingredients:
             ef[0].set_edit_text('')
             ef[1].set_edit_text('')
+            ef[2].set_edit_text('')
         self.organic.set_state('mixed')
+        self.instructions.set_edit_text('')
         return self.update()
 
     def update(self):
@@ -124,18 +150,28 @@ class RecipeEditor(FocusWidget):
     def __init__(self,
         activity_manager: ActivityManager,
         query_manager: QueryManager,
+        recipe,
     ):
         self.buttons = {
           'clear': Button(('streak', 'Clear')),
           'exit': Button(('streak', 'Exit')),
           'add': Button(('streak', 'Add')),
         }
-        self.ingredients = [
-          (AutoCompleteEdit(('bg', 'product#0')), AutoCompleteEdit(('bg', 'unit#0'))),
-          (AutoCompleteEdit(('bg', 'product#1')), AutoCompleteEdit(('bg', 'unit#1'))),
+        self.ingredients: List[Tuple[AutoCompleteEdit, FloatEdit, AutoCompleteEdit]] = [
+            (
+                AutoCompleteEdit(('bg', f'product#{idx}'), edit_text=ingredient[0]),
+                FloatEdit(('bg', f''), default=ingredient[1]),
+                AutoCompleteEdit(('bg', f'unit#{idx}'), edit_text=ingredient[2]),
+            ) for idx, ingredient in enumerate(recipe['ingredients'])
+        ] if len(recipe['ingredients']) else [
+            (
+                AutoCompleteEdit(('bg', 'product#0')),
+                FloatEdit(('bg', f'')),
+                AutoCompleteEdit(('bg', 'unit#0'))
+            ),
         ]
         self.organic = NoTabCheckBox(('bg', "Organic"), state='mixed')
-        self.instructions = Edit(f'', multiline=True, allow_tab=True)
+        self.instructions = Edit('', edit_text=recipe['instructions'] or u'', multiline=True, allow_tab=True)
 
         bottom_pane = [
             self.organic,
@@ -157,7 +193,8 @@ class RecipeEditor(FocusWidget):
                 ))))
             ))
             connect_signal(widget[1], 'postchange', lambda _,v: self.update())
-            connect_signal(widget[1], 'apply', lambda w, name: self.autocomplete_callback(
+            connect_signal(widget[2], 'postchange', lambda _,v: self.update())
+            connect_signal(widget[2], 'apply', lambda w, name: self.autocomplete_callback(
                 w, self.autocomplete_options(name, dict(map(
                     to_unnumbered_field,
                     filter(in_same_row(name), map(to_numbered_field, self.data.items())
@@ -166,7 +203,7 @@ class RecipeEditor(FocusWidget):
 
         connect_signal(self.buttons['clear'], 'click', lambda x: self.clear().update())
         connect_signal(self.buttons['exit'], 'click', lambda x: show_or_exit('esc'))
-        self.clear()
+        connect_signal(self.instructions, 'postchange', lambda _,v: self.update())
 
         header = Text(u'Recipe Editor', 'center')
         _copyright = Text(COPYRIGHT, 'center')
@@ -188,15 +225,20 @@ class RecipeEditor(FocusWidget):
             ) for idx, ingredient in enumerate(self.ingredients)
         ]
         middle_pane = [
+            LineBox(
+                ingredient[1], title=f'Quantity {idx}', title_align='left'
+            ) for idx, ingredient in enumerate(self.ingredients)
+        ]
+        right_pane = [
             LineBox(AttrMap(
                 AutoCompletePopUp(
-                    ingredient[1],
+                    ingredient[2],
                     self.apply_choice,
                     lambda: activity_manager.show(self.update())
                 ), 'streak'), title=f'Unit {idx}', title_align='left'
             ) for idx, ingredient in enumerate(self.ingredients)
         ]
-        right_pane = [
+        gutter = [
             *[ Divider() for _ in itertools.product(
                 range(3), self.ingredients[:-1]
             )],
@@ -221,16 +263,22 @@ class RecipeEditor(FocusWidget):
                 ]))
             ], dividechars=1),
             'bottom_pane': Pile(bottom_pane),
-            'right_pane': (8, Pile(right_pane)),
+            'right_pane': (16, Pile(right_pane)),
             'middle_pane': (16, Pile(middle_pane)),
             'left_pane': Pile(left_pane),
+            'gutter': (8, Pile(gutter))
         }
 
         widget = Pile([
             banner,
             Divider(),
             components['top_pane'],
-            Columns((components['left_pane'], components['middle_pane'], (1,Divider()), components['right_pane']),
+            Columns((
+                components['left_pane'],
+                components['middle_pane'],
+                components['right_pane'],
+                (1,Divider()),
+                components['gutter']),
                 dividechars=0,
             ),
             components['bottom_pane'],
@@ -242,11 +290,14 @@ class RecipeEditor(FocusWidget):
                 self.buttons.items(),
                 [
                     ('ingredients', self.ingredients[-1][0]),
-                    ('units', self.ingredients[-1][1]),
+                    ('instructions', self.instructions),
+                    ('quantity', self.ingredients[-1][1]),
+                    ('units', self.ingredients[-1][2]),
                     ('organic', self.organic)
                 ],
             ) if x == n),
             [
+                'instructions',
                 'ingredients', 'units', 'add',
                 'organic',
                 'clear', 'exit',

+ 3 - 0
app/parse_recipe.py

@@ -27,6 +27,9 @@ def parse_recipe(fh, query_manager: QueryManager):
             recipe['ingredients'].append(
                 [match.group('product'), match.group('quantity'), match.group('unit')]
             )
+    for k in set(recipe.keys()) - set(['ingredients']):
+        if k in contents:
+            recipe[k] = contents[k]
     return recipe
     
 

+ 5 - 0
recipe.py

@@ -46,9 +46,14 @@ cur.execute("BEGIN")
 activity_manager = ActivityManager()
 query_manager = QueryManager(cur, display_mapper)
 
+from app import parse_recipe
+with open('recipe.yaml') as f:
+    recipe = parse_recipe.parse_recipe(f, query_manager)
+
 activity_manager.create(RecipeEditor,
     'recipe_editor',
     activity_manager, query_manager,
+    recipe,
 )
 
 app = Recipe(activity_manager)