From: Edward Delaporte Date: Tue, 5 May 2026 03:56:20 +0000 (-0500) Subject: Print shopping list by zone X-Git-Url: https://apps.delaporte.us/gitweb/matrix-shop-bot.git/commitdiff_plain/5014c979631941a7086c2c266320d5f3d390991a?ds=inline Print shopping list by zone --- diff --git a/matrix_shop_bot/bot.py b/matrix_shop_bot/bot.py index ae65bdc..581e19b 100644 --- a/matrix_shop_bot/bot.py +++ b/matrix_shop_bot/bot.py @@ -13,34 +13,35 @@ DATE_FORMAT = '%Y-%m-%d' def today(): return dt.now().strftime(DATE_FORMAT) -PREFIX = f'!{MODULE}' +PREFIX = f'.{MODULE}' logger = logging.getLogger(__name__) logging.basicConfig(filename=f'{MODULE}.log', encoding='utf-8', level=logging.DEBUG) logging.getLogger('nio').setLevel(logging.WARNING) async def do_shop(match): ''' - Prefix: !shop + Prefix: .shop Synax: - !shop COMMAND ARGS + .shop COMMAND ARGS Commands: help - show this help extra.help - show extended help add - add an item to shop for add.store - add a store - stores - list all stores - at STORE_ID - (!shop at STORE_ID) list all items assigned to store and use this store for future commands - assign ITEM_ID - assign item to last used store id + at STORE_ID - use this store for future commands buy - mark item as purchased - clear STORE_ID - clear all purchased items for STORE_ID from database + clear STORE - clear all purchased items for STORE list - re-show list from last used store + past STORE - re-show past purchased items from STORE + stores - list all stores ''' extended_help = ''' + assign ITEM_ID - assign item to last used store id assign.item.to.store ITEM_ID STORE_ID - plan to shop for at store for item - clear.all STORE_ID - clear **all** items for STORE_ID from database - remove.item ITEM_ID - remove a shopping item from the datatbase + clear.all STORE - clear **all** items for STORE + remove ITEM_ID - remove a shopping item from the datatbase remove.store STORE_ID - remove a store from the datatbase (requires clearing the list first) setup - run one time database setup unsorted - list all items not assigned to any store @@ -125,7 +126,7 @@ async def do_shop(match): if match.command('past'): store_name = ' '.join(match.args()[1:]) items = Data().list_purchases(store_name) - results = ['Past purchases at {store_name}'] + results = ['Past purchases...'] for item in items: results.append(str(item)) return results @@ -138,7 +139,7 @@ async def do_shop(match): results.append(str(item)) return results - if match.command('remove.item'): + if match.command('remove'): item_id = ' '.join(match.args()[1:]) Data().remove_item(item_id) return ['Success'] diff --git a/matrix_shop_bot/db.py b/matrix_shop_bot/db.py index 278d4a1..9d40236 100644 --- a/matrix_shop_bot/db.py +++ b/matrix_shop_bot/db.py @@ -24,18 +24,60 @@ class ItemStoreLink(SQLModel, table=True): class Item(SQLModel, table=True): + '''An item to shop for.''' id: int | None = Field(default=None, primary_key=True) name: str = Field(default=None, unique=True) price: Decimal | None = Field(decimal_places=2) stores: list["Store"] = Relationship(back_populates="shopping_list", link_model=ItemStoreLink) purchase_date: dt | None = Field(default=None) def __str__(self): - return f'{self.name} (#{self.id})' + if(self.purchase_date): + return f'- [x] {self.name} (#{self.id})' + else: + return f'- [ ] {self.name} (#{self.id})' + +class ItemZone(SQLModel, table=True): + '''Where an item is usually found. + + Matched only by loose text matching, since items come and go, over time. + ''' + id: int | None = Field(default=None, primary_key=True) + name: str = Field(default=None, unique=True) # milk + zone: str = Field(default=None, unique=False) # dairy section class PreferStore(SQLModel, table=True): + '''Tracks which store we are shopping at, at the moment.''' store_id: int | None = Field(default=None, foreign_key="store.id", primary_key=True) +def _get_item_zone(item_name, session) -> str: + zone = session.exec(select(ItemZone).where(ItemZone.name == item_name)).first() + if zone: + return zone.zone + return 'No Zone Assigned' + +def _get_pretty_shopping_list(store_id, session) -> List[str]: + store = session.exec(_get_with_id(Store, store_id)).first() + results = [] + results.append(f' ## {store.name}') + items = store.shopping_list + if not items: + results.append(f'-- Nothing to shop for at {store.name} --') + + zones = {} + + for item in items: + zone = _get_item_zone(item.name, session) + if not zone in zones: + zones[zone] = [] + zones[zone].append(str(item)) + for zone in zones: + results.append(f' ### {zone}') + for item in zones[zone]: + results.append(item) + return results + class Store(SQLModel, table=True): + '''A place to shop at.''' id: int | None = Field(default=None, primary_key=True) name: str = Field(default=None, unique=True) shopping_list: list["Item"] = Relationship(back_populates="stores", link_model=ItemStoreLink) @@ -43,23 +85,9 @@ class Store(SQLModel, table=True): def __str__(self): return f'{self.name} (#{self.id})' - def get_pretty_shopping_list(self) -> List[str]: - results = [] - results.append(f' ## {self.name}') - items = self.shopping_list - if not items: - results.append(f'-- Nothing to shop for at {self.name} --') - else: - for item in items: - if(item.purchase_date): - results.append(f' - [x] {item}') - else: - results.append(f' - [ ] {item}') - return results - -# May get used later... class PurchaseRecord(SQLModel, table=True): + '''Tracks when and where items were purchased.''' id: int | None = Field(default=None, primary_key=True) name: str = Field(default=None) price: Decimal | None = Field(decimal_places=2) @@ -135,6 +163,19 @@ class Data(): self.assign_store(item_id, store_id) return store_id + def assign_zone(self, item_name, zone_name): + item_id = -1 + with Session(self.engine) as session: + try: + new = ItemZone(name=item_name) + new.zone = zone_name + session.add(new) + session.commit() + item_id = new.id + except: + pass # Already assigned + return item_id + def assign_store(self, item_id, store_id): self._set_pref_store_id(store_id) @@ -150,7 +191,7 @@ class Data(): def list_purchases(self, store_name): with Session(self.engine) as session: - cmd = select(PurchaseRecord).where(PurchaseRecord.store == store_name) + cmd = select(PurchaseRecord).where(func.lower(PurchaseRecord.store) == func.lower(store_name)) records = session.exec(cmd).all() return records @@ -195,7 +236,7 @@ class Data(): session.delete(item) session.commit() - results = store.get_pretty_shopping_list() + results = _get_pretty_shopping_list(store.id, session) return results def get_stores(self): @@ -219,7 +260,7 @@ class Data(): with Session(self.engine) as session: store = session.exec(_get_with_id(Store, store_id)).first() if store: - return store.get_pretty_shopping_list() + return _get_pretty_shopping_list(store.id, session) return [f'Last shopping list store not found.'] def get_shopping_list(self, store_name) -> List[str]: @@ -228,7 +269,7 @@ class Data(): store = session.exec(_get_with_name(Store, store_name)).first() if not store: return [f'Store {store_name} not found.'] - results = store.get_pretty_shopping_list() + results = _get_pretty_shopping_list(store.id, session) self._set_pref_store_id(store.id) return results diff --git a/matrix_shop_bot/tasks.py b/matrix_shop_bot/tasks.py index 515649b..07ffb40 100644 --- a/matrix_shop_bot/tasks.py +++ b/matrix_shop_bot/tasks.py @@ -33,6 +33,10 @@ def assign_to_store(c, item_id, store_id): result = Data().assign_store(store_id, item_id) print(result) +@task +def assign_to_zone(c, item, zone): + result = Data().assign_zone(item, zone) + print(result) @task def buy(c, items):