#!/usr/bin/python3 # # Copyright (c) Daniel Sheffield 2021 - 2023 # # All rights reserved # # THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY 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()