123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- import sys
- from psycopg import Cursor
- from urwid import raw_display, WidgetPlaceholder, SolidFill, MainLoop
- from app.activities.ActivityManager import ActivityManager, show_or_exit
- from app.activities.TransactionEditor import TransactionEditor
- from app.data.QueryManager import QueryManager, display_mapper
- from app.palette import iter_palettes, solarized
- try:
- from db_credentials import HOST, PASSWORD
- host = f'host={HOST}'
- password = f'password={PASSWORD}'
- except:
- host = ''
- password = ''
- try:
- import os
- import psycopg
- user = os.getenv('USER')
- conn: Cursor = psycopg.connect(f"{host} dbname=grocery user={user} {password}")
- cur = conn.cursor()
- except:
- print('Failed to set up db connection. Entering Mock mode')
- from mock import *
- args = sys.argv
- log = args[1]
- class GroceryTransactionEditor(WidgetPlaceholder):
- def __init__(self, activity_manager, cur, log):
- super().__init__(SolidFill(u'/'))
- self.activity_manager = activity_manager
- self.cur = cur
- txn: TransactionEditor = self.activity_manager.get('transaction')
- with open(log, 'r') as f:
- date = None
- store = None
- for line in f.readlines():
- if date is None and store is None:
- if '$store$' in line:
- date, store, _= line.split('$store$')
- date = date.split("'")[1]
- else:
- assert None not in (date, store,), \
- "Both date and store should be set or neither should be set"
- if '$store$' in line:
- assert date in line and f'$store${store}$store$' in line, \
- "Date ({date}) and store ({store}) not found in {line}."\
- " Mixing transactions from different dates and stores is not supported"
- self.cur.execute(line)
- if None not in (date, store):
- txn.apply_choice('ts', date)
- txn.apply_choice('store', store)
- else:
- txn.focus_on(txn.edit_fields['ts'])
- self.activity_manager.show(self)
- self.activity_manager.show(txn.update())
- self.log = self.open(log)
- def _open(self, log):
- with open(log, 'a') as f:
- yield f
- def open(self, log):
- self._to_close = self._open(log)
- return next(self._to_close)
- def close(self):
- if self._to_close is not None:
- self._to_close.close()
- self._to_close = None
- def save(self, data):
- ts = data['ts']
- store = data['store']
- description = data['description']
- quantity = data['quantity']
- unit = data['unit']
- price = data['price']
- product = data['product']
- organic = 'true' if data['organic'] is True else 'false'
- tags = ', '.join([
- f'$quot${v}$quot$' for t,v in data.items() if 'tags' in t and v
- ])
- tags = f", ARRAY[{tags}]" if tags else ''
- statement = \
- f"CALL insert_transaction('{ts}', $store${store}$store$, " \
- f"$descr${description}$descr$, {quantity}, $unit${unit}$unit$, " \
- f"{price}, $produ${product}$produ$, {organic}{tags});\n"
- self.log.write(statement)
- self.log.flush()
- self.cur.execute(statement)
- cur.execute("BEGIN")
- activity_manager = ActivityManager()
- query_manager = QueryManager(cur, display_mapper)
- activity_manager.create(TransactionEditor, 'transaction',
- activity_manager, query_manager,
- )
- app = None
- palettes = iter_palettes(solarized.theme)
- try:
- app = GroceryTransactionEditor(activity_manager, cur, log)
- screen = raw_display.Screen()
- loop = MainLoop(app, next(palettes), screen=screen,
- unhandled_input=lambda k: show_or_exit(k, screen=screen, palettes=palettes),
- pop_ups=True
- )
- loop.run()
- finally:
- if app is not None:
- app.close()
- cur.close()
- conn.close()
|