pantry-track/database.py
2025-02-08 12:00:50 -06:00

474 lines
16 KiB
Python

class DatabaseError(Exception):
def __init__(self, message, payload=[], sql=""):
super().__init__(message)
self.payload = payload
self.message = message
self.sql = sql
def __str__(self):
return f"DatabaseError(message='{self.message}', payload={self.payload}, sql='{self.sql}')"
def tupleDictionaryFactory(columns, row):
columns = [desc[0] for desc in columns]
return dict(zip(columns, row))
def lst2pgarr(alist):
return '{' + ','.join(alist) + '}'
# -------------------------
# Insert Database Functions
# -------------------------
def insertLoginsTuple(conn, payload, convert=False):
"""Inserts payload into logins table
Args:
conn (_T_connector@connect): Postgresql Connector
payload (tuple): (username[str], password[str], email[str])
convert (bool, optional): deteremines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted login
"""
login = ()
with open(f"sql/INSERT/insertLoginsTuple.sql", "r+") as file:
sql = file.read()
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
login = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
login = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return login
def insertGroupsTuple(conn, site, payload, convert=False):
"""insert into groups table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (stre):
payload (tuple): (name[str], description[str], included_items[lst2pgarr], group_type[str])
convert (bool, optional): Determines if to return tuple as a dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
group = ()
with open(f"sql/INSERT/insertGroupsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
group = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
group = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return group
def insertLogisticsInfoTuple(conn, site, payload, convert=False):
"""insert payload into logistics_info table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (barcode[str], primary_location[str], auto_issue_location[str], dynamic_locations[jsonb],
location_data[jsonb], quantity_on_hand[float])
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
logistics_info = ()
with open(f"sql/INSERT/insertLogisticsInfoTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
logistics_info = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
logistics_info = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return logistics_info
def insertItemInfoTuple(conn, site, payload, convert=False):
"""inserts payload into the item_info table of site
Args:
conn (_T_connector@connect): Postgresql Connector
site_name (str):
payload (tuple): (barcode[str], linked_items[lst2pgarr], shopping_lists[lst2pgarr], recipes[lst2pgarr], groups[lst2pgarr],
packaging[str], uom[str], cost[float], safety_stock[float], lead_time_days[float], ai_pick[bool])
convert (bool optional): Determines if to return tuple as dictionary. DEFAULTS to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
item_info = ()
with open(f"sql/INSERT/insertItemInfoTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
item_info = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
item_info = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return item_info
def insertFoodInfoTuple(conn, site, payload, convert=False):
"""insert payload into food_info table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (_type_): (ingrediants[lst2pgarr], food_groups[lst2pgarr], nutrients[jsonstr], expires[bool])
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
food_info = ()
with open(f"sql/INSERT/insertFoodInfoTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
food_info = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
food_info = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return food_info
def insertBrandsTuple(conn, site, payload, convert=False):
"""insert payload into brands table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (brand_name[str], )
convert (bool, optional): Determines if the tuple is returned as a dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
brand = ()
with open(f"sql/INSERT/insertBrandsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
brand = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
brand = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return brand
def insertItemTuple(conn, site, payload, convert=False):
"""insert payload into items table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (barcode[str], item_name[str], brand[int], description[str],
tags[lst2pgarr], links[jsonb], item_info_id[int], logistics_info_id[int],
food_info_id[int], row_type[str], item_type[str], search_string[str])
convert (bool, optional): Determines if to return tuple as a dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
item = ()
with open(f"sql/INSERT/insertItemTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
item = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
item = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return item
def insertItemLocationsTuple(conn, site, payload, convert=False):
"""insert payload into item_locations table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (part_id[int], location_id[int], quantity_on_hand[float], cost_layers[lst2pgarr])
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
location = ()
with open(f"sql/INSERT/insertItemLocationsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
location = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
location = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return location
def insertCostLayersTuple(conn, site, payload, convert=False):
"""insert payload into cost_layers table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (aquisition_date[timestamp], quantity[float], cost[float], currency_type[str], expires[timestamp/None], vendor[int])
convert (bool, optional): Determines if tuple is returned as a dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
cost_layer = ()
with open(f"sql/INSERT/insertCostLayersTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
cost_layer = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
cost_layer = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return cost_layer
def insertTransactionsTuple(conn, site, payload, convert=False):
"""insert payload into transactions table for site
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (timestamp[timestamp], logistics_info_id[int], barcode[str], name[str],
transaction_type[str], quantity[float], description[str], user_id[int], data[jsonb])
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
Raises:
DatabaseError:
Returns:
tuple or dict: inserted tuple
"""
transaction = ()
with open(f"sql/INSERT/insertTransactionsTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows and convert:
transaction = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
transaction = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return transaction
# -------------------------
# Update Database Functions
# -------------------------
def updateItemLocation(conn, site, payload):
item_location = ()
with open(f"sql/updateItemLocation.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows:
item_location = rows
except Exception as error:
return error
return item_location
def updateCostLayersTuple(conn, site, payload):
"""_summary_
Args:
conn (_type_): _description_
site (_type_): _description_
payload (_type_): _description_
Returns:
_type_: _description_
"""
cost_layer = ()
with open(f"sql/updateCostLayersTuple.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows:
cost_layer = rows
except Exception as error:
return error
return cost_layer
# -------------------------
# Delete Database Functions
# -------------------------
def deleteCostLayersTuple(conn, site_name, payload):
"""deletes tuple from cost_layers table for site_name
Args:
conn (_T_connector@connect): Postgresql Connector
site (str):
payload (tuple): (cost_layer_id, )
Raises:
DatabaseError: returns all database errors and encompasses payload and sql info.
Returns:
tuple: deleted row returned as a tuple
"""
cost_layer = ()
sql = f"WITH deleted_row AS (DELETE FROM {site_name}_cost_layers WHERE id=%s RETURNING *) SELECT * FROM deleted_row;"
try:
with conn.cursor() as cur:
cur.execute(sql, payload)
rows = cur.fetchone()
if rows:
cost_layer = rows
except Exception as error:
raise DatabaseError(error, payload, sql)
return cost_layer
# -------------------------
# Select Database Functions
# -------------------------
def selectItemLocationsTuple(conn, site_name, payload, convert=False):
"""select a single tuple from ItemLocations table for site_name
Args:
conn (_T_connector@connect):
site_name (str):
payload (tuple): [item_id, location_id]
convert (bool): defaults to False, used to determine return of tuple/dict
Returns:
tuple: the row that was returned from the table
"""
item_locations = ()
select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;"
try:
with conn.cursor() as cur:
cur.execute(select_item_location_sql, payload)
rows = cur.fetchone()
if rows and convert:
item_locations = tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
item_locations = rows
except Exception as error:
return error
return item_locations
def selectCostLayersTuple(conn, site_name, payload, convert=False):
"""select a single or series of cost layers from the database for site_name
Args:
conn (_T_connector@connect):
site_name (str):
payload (tuple): (item_locations_id, )
convert (bool): defaults to False, used for determining return as tuple/dict
Returns:
list: list of tuples/dict from the cost_layers table for site_name
"""
cost_layers = ()
select_cost_layers_sql = f"SELECT cl.* FROM {site_name}_item_locations il JOIN {site_name}_cost_layers cl ON cl.id = ANY(il.cost_layers) where il.id=%s;"
try:
with conn.cursor() as cur:
cur.execute(select_cost_layers_sql, payload)
rows = cur.fetchall()
if rows and convert:
cost_layers = rows
cost_layers = [tupleDictionaryFactory(cur.description, layer) for layer in rows]
elif rows and not convert:
cost_layers = rows
except Exception as error:
return error
return cost_layers
# -------------------------
# Custom Database Functions
# -------------------------
def getItemLocation(conn, site_name, payload):
"""This functions returns the bundled ItemLocation of item_id and location_id, with the cost_layers pulled from the database as tuples.
Args:
conn (_T_connector@connect):
site_name (str):
payload (tuple): [item_id, location_id]
Returns:
list: list of the itemLocation with cost_layers selected
"""
item_location = list(selectItemLocationsTuple(conn, site_name, payload))
print(item_location)
item_location[4] = selectCostLayersTuple(conn, site_name, (item_location[0], ))
return item_location