]>
us-delaporte.pagekite.me Git - matrix-shop-bot.git/blob - matrix_shop_bot/db.py
186350acfd606f2fd1c5d03da8f25e931341b040
3 from datetime
import datetime
as dt
4 from decimal
import Decimal
5 from typing
import List
7 from sqlmodel
import Field
, Session
, SQLModel
, create_engine
, select
, text
, Relationship
9 from sqlite3
import IntegrityError
13 DATE_FORMAT
= '%Y-%m-%d'
16 return dt
.now().strftime(DATE_FORMAT
)
19 logger
= logging
.getLogger(__name__
)
20 logging
.basicConfig(filename
=f
'{MODULE}.log', encoding
='utf-8', level
=logging
.DEBUG
)
22 class ItemStoreLink(SQLModel
, table
=True):
23 item_id
: int |
None = Field(default
=None, foreign_key
="item.id", primary_key
=True)
24 store_id
: int |
None = Field(default
=None, foreign_key
="store.id", primary_key
=True)
27 class Item(SQLModel
, table
=True):
28 id: int |
None = Field(default
=None, primary_key
=True)
29 name
: str = Field(default
=None, unique
=True)
30 price
: Decimal |
None = Field(decimal_places
=2)
31 stores
: list["Store"] = Relationship(back_populates
="shopping_list", link_model
=ItemStoreLink
)
32 purchase_date
: dt |
None = Field(default
=None)
34 return f
'{self.name} (#{self.id})'
36 class PreferStore(SQLModel
, table
=True):
37 store_id
: int |
None = Field(default
=None, foreign_key
="store.id", primary_key
=True)
39 class Store(SQLModel
, table
=True):
40 id: int |
None = Field(default
=None, primary_key
=True)
41 name
: str = Field(default
=None, unique
=True)
42 shopping_list
: list["Item"] = Relationship(back_populates
="stores", link_model
=ItemStoreLink
)
43 # purchase_history: list["PurchaseRecord"] = Relationship(back_populates="store")
45 return f
'{self.name} (#{self.id})'
47 # May get used later...
48 #class PurchaseRecord(SQLModel, table=True):
49 # id: int | None = Field(default=None, primary_key=True)
50 # name: str = Field(default=None, unique=True)
51 # price: Decimal | None = Field(decimal_places=2)
52 # store: Store = Relationship(back_populates="purchase_history")
53 # purchase_date: dt | None = Field(default=None)
55 def _get_with_id(cls
, item_id
):
56 return select(cls
).where(cls
.id == item_id
)
60 def __init__(self
, db_file
=None):
62 db_file
= os
.path
.join(os
.environ
.get('MATRIX_DATA_DIR', ''), f
'{MODULE}.db')
65 self
.engine
= self
.get_engine()
66 self
.last_store_id
= None
68 def _set_pref_store_id(self
, store_id
):
69 with Session(self
.engine
) as session
:
70 prefs
= session
.exec(select(PreferStore
)).all()
74 pref
.store_id
= store_id
78 def _get_pref_store_id(self
):
79 with Session(self
.engine
) as session
:
80 pref
= session
.exec(select(PreferStore
)).one()
83 def _add_with_name(self
, cls
, name
):
86 with Session(self
.engine
) as session
:
93 # recover by returning the item_id
94 item
= session
.exec(select(Item
).where(Item
.name
== name
)).first()
99 def add_store(self
, name
):
100 store_id
= self
._add
_with
_name
(Store
, name
)
101 self
._set
_pref
_store
_id
(store_id
)
104 def add_item(self
, name
):
105 names
= name
.split(',')
107 item_id
= self
._add
_with
_name
(Item
, name
.strip())
108 store_id
= self
._get
_pref
_store
_id
()
109 self
.assign_store(item_id
, store_id
)
112 def assign_last_store(self
, item_id
):
113 store_id
= self
._get
_pref
_store
_id
()
114 self
.assign_store(item_id
, store_id
)
117 def assign_store(self
, item_id
, store_id
):
118 self
._set
_pref
_store
_id
(store_id
)
120 with Session(self
.engine
) as session
:
121 store
= session
.exec(_get_with_id(Store
, store_id
)).first()
122 item
= session
.exec(_get_with_id(Item
, item_id
)).first()
124 item
.stores
.append(store
)
127 pass # Already assigned
131 def purchase_items(self
, item_ids
):
133 with Session(self
.engine
) as session
:
134 for item_id
in item_ids
.split(' '):
135 item
= session
.exec(_get_with_id(Item
, item_id
)).first()
137 item
.purchase_date
= dt
.today()
138 results
.append(str(item
))
142 def purchase_item(self
, item_id
):
143 with Session(self
.engine
) as session
:
144 item
= session
.exec(_get_with_id(Item
, item_id
)).first()
145 item
.purchase_date
= dt
.today()
149 def clear_list(self
, store_id
, clear_all
=False):
150 with Session(self
.engine
) as session
:
151 store
= session
.exec(_get_with_id(Store
, store_id
)).first()
152 for item
in store
.shopping_list
:
153 if clear_all
or item
.purchase_date
:
157 def get_stores(self
):
158 with Session(self
.engine
) as session
:
160 return session
.exec(cmd
).all()
162 def get_unsorted_list(self
):
165 with Session(self
.engine
) as session
:
166 cmd
= select(Item
).where(Item
.stores
== None)
167 items
= session
.exec(cmd
).all()
170 return [f
'No unsorted items!']
174 def get_shopping_list_for_store(self
, store_id
) -> List
[str]:
175 self
._set
_pref
_store
_id
(store_id
)
176 return self
.get_shopping_list(store_id
)
178 def get_last_shopping_list(self
) -> List
[str]:
179 store_id
= self
._get
_pref
_store
_id
()
180 return self
.get_shopping_list(store_id
)
182 def get_shopping_list(self
, store_id
) -> List
[str]:
184 with Session(self
.engine
) as session
:
185 cmd
= select(Store
).where(Store
.id == store_id
)
186 store
= session
.exec(cmd
).first()
188 return [f
'Store {store_id} not found.']
190 results
.append(f
' ## {store.name}')
192 items
= store
.shopping_list
194 results
.append(f
'-- Nothing to shop for at {store.name} --')
197 if(item
.purchase_date
):
198 results
.append(f
' - [x] {item}')
200 results
.append(f
' - [ ] {item}')
203 def remove_item(self
, item_id
):
204 with Session(self
.engine
) as session
:
205 item
= session
.exec(_get_with_id(Item
, item_id
)).first()
209 def remove_store(self
, store_id
):
210 with Session(self
.engine
) as session
:
211 store
= session
.exec(_get_with_id(Store
, store_id
)).first()
212 session
.delete(store
)
215 def get_engine(self
):
216 return create_engine(f
"sqlite:///{self.file}")
219 SQLModel
.metadata
.create_all(self
.engine
)