From 81612fa6bea109d5c81c84b39daef005cadcb8a8 Mon Sep 17 00:00:00 2001 From: Aaron Truong Date: Mon, 17 Mar 2025 17:41:47 -0700 Subject: [PATCH 1/5] exceptions.py file --- backend/utils/exceptions.py | 137 ++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 backend/utils/exceptions.py diff --git a/backend/utils/exceptions.py b/backend/utils/exceptions.py new file mode 100644 index 0000000..2da9ac1 --- /dev/null +++ b/backend/utils/exceptions.py @@ -0,0 +1,137 @@ +class DatabaseError(Exception): + """Base exception for database errors""" + pass + +class ConnectionError(DatabaseError): + """Failed to connect to database""" + pass + +class RecordNotFoundError(DatabaseError): + """Requested record does not exist""" + pass + +class DuplicateRecordError(DatabaseError): + """Record with this identifier already exists""" + pass + +class DatabaseInitError(DatabaseError): + """Failed to initialize database""" + pass + +import sqlite3 +from typing import Type +from models.snack import Snack, SnackCreateSchema, SnackUpdateSchema + + +def get_db_connection(db_file_path:str="data/db.sqlite3"): + """Creates and returns a SQLite database connection""" + try: + connection = sqlite3.connect(db_file_path) + connection.row_factory = sqlite3.Row + return connection + except sqlite3.Error as e: + raise ConnectionError(f"Failed to connect to database: {str(e)}") + + + +def get_snack(sku: str) -> Snack: + """ + Returns a single snack by SKU + + Args: + sku: The unique SKU of the snack + + Returns: + Snack object + + Raises: + RecordNotFoundError: If no snack with the given SKU exists + ConnectionError: If database connection fails + DatabaseError: For other database errors + """ + try: + with get_db_connection() as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM snacks WHERE sku = ?", (sku,)) + record = cursor.fetchone() + + if record is None: + raise RecordNotFoundError(f"No snack found with SKU: {sku}") + + return Snack(**record) + except sqlite3.Error as e: + raise DatabaseError(f"Database error when fetching snack {sku}: {str(e)}") + except RecordNotFoundError as e: + raise RecordNotFoundError(f"Snack not found {sku}: {str(e)}") + except ConnectionError as e: + raise ConnectionError(f"Database error when connecting to database {sku}: {str(e)}") + + + +def create_snack(sku: str,name: str, ) -> Snack: + """ + Returns a single snack by SKU + + Args: + sku: The unique SKU of the snack + + Returns: + result + + Raises: + RecordNotFoundError: If no snack with the given SKU exists + ConnectionError: If database connection fails + DatabaseError: For other database errors + """ + try: + with get_db_connection() as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM snacks WHERE sku = ?", (sku,)) + record = cursor.fetchone() + + if Type(sku) is not str: + raise RecordNotFoundError(f"{sku} is not a string") + if Type(name) is not str: + raise RecordNotFoundError(f"{name} is not a string") + return Snack(**record) + except ConnectionError as e: + raise ConnectionError(f"Database error when connecting to database {sku}: {str(e)}") + except DatabaseError as e: + raise DatabaseError(f"Database error {sku}: {str(e)}") + +# def update_snack(sku: str) -> Snack: +# """ +# Returns a single snack by SKU + +# Args: +# sku: The unique SKU of the snack + +# Returns: +# Snack object + +# Raises: +# RecordNotFoundError: If no snack with the given SKU exists +# ConnectionError: If database connection fails +# DatabaseError: For other database errors +# """ +# try: +# with get_db_connection() as conn: +# cursor = conn.cursor() +# cursor.execute("SELECT * FROM snacks WHERE sku = ?", (sku,)) +# record = cursor.fetchone() + +# if record is None: +# raise RecordNotFoundError(f"No snack found with SKU: {sku}") + +# return Snack(**record) +# except sqlite3.Error as e: +# raise DatabaseError(f"Database error when fetching snack {sku}: {str(e)}") +# except RecordNotFoundError as e: +# raise RecordNotFoundError(f"Snack not found {sku}: {str(e)}") +# except ConnectionError as e: +# raise ConnectionError(f"Database error when connecting to database {sku}: {str(e)}") +# except DatabaseError as e: +# raise DatabaseError(f"Database error {sku}: {str(e)}") + + + From f010970da816c81304d8277dc6ecda80a8ab236c Mon Sep 17 00:00:00 2001 From: Aaron Truong Date: Mon, 17 Mar 2025 17:44:21 -0700 Subject: [PATCH 2/5] Testing for Jayden --- backend/utils/exceptions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/utils/exceptions.py b/backend/utils/exceptions.py index 2da9ac1..6c82379 100644 --- a/backend/utils/exceptions.py +++ b/backend/utils/exceptions.py @@ -99,6 +99,9 @@ def create_snack(sku: str,name: str, ) -> Snack: except DatabaseError as e: raise DatabaseError(f"Database error {sku}: {str(e)}") + +# TESTING THIS FOR JAYDEN CUZ HE NOT SMART + # def update_snack(sku: str) -> Snack: # """ # Returns a single snack by SKU From df296c1f41f78caec2e0e0222e33153e9705d2ff Mon Sep 17 00:00:00 2001 From: Aaron Truong Date: Mon, 17 Mar 2025 17:46:16 -0700 Subject: [PATCH 3/5] Testing again --- backend/utils/exceptions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/utils/exceptions.py b/backend/utils/exceptions.py index 6c82379..3884261 100644 --- a/backend/utils/exceptions.py +++ b/backend/utils/exceptions.py @@ -101,6 +101,7 @@ def create_snack(sku: str,name: str, ) -> Snack: # TESTING THIS FOR JAYDEN CUZ HE NOT SMART +# Test 2.0 # def update_snack(sku: str) -> Snack: # """ From a02420370dc469ec9fca91885162bb4d19373ca3 Mon Sep 17 00:00:00 2001 From: Aaron Truong Date: Mon, 17 Mar 2025 18:42:24 -0700 Subject: [PATCH 4/5] exception.py file and added some error handling to most --- backend/utils/db.py | 180 ++++++++++++++++++++++-------------- backend/utils/exceptions.py | 124 +------------------------ 2 files changed, 111 insertions(+), 193 deletions(-) diff --git a/backend/utils/db.py b/backend/utils/db.py index 23e7b46..9308d8a 100644 --- a/backend/utils/db.py +++ b/backend/utils/db.py @@ -11,7 +11,9 @@ """ import os import sqlite3 +from typing import Type from models.snack import Snack, SnackCreateSchema, SnackUpdateSchema +from exceptions import DatabaseError, ConnectionError, RecordNotFoundError, DuplicateRecordError, DatabaseInitError def get_db_connection(db_file_path:str="data/db.sqlite3"): """Creates and returns a SQLite database connection""" @@ -22,93 +24,131 @@ def get_db_connection(db_file_path:str="data/db.sqlite3"): def init_db(db_file_path: str = "data/db.sqlite3"): """Initialize the database with schema""" - os.makedirs(os.path.dirname(db_file_path), exist_ok=True) - with open('data/schema.sql') as f: - schema = f.read() - with get_db_connection() as conn: - conn.executescript(schema) + try: + os.makedirs(os.path.dirname(db_file_path), exist_ok=True) + with open('data/schema.sql') as f: + schema = f.read() + with get_db_connection() as conn: + conn.executescript(schema) + except sqlite3.Error as e: + raise DatabaseInitError(f"Error when initializing database: {str(e)}") def get_inventory() -> list[Snack]: """Returns all snacks in the database""" - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute("SELECT * FROM snacks") - records = cursor.fetchall() - return [Snack(**record) for record in records] + try: + with get_db_connection() as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM snacks") + records = cursor.fetchall() + return [Snack(**record) for record in records] + except sqlite3.Error as e: + raise DatabaseError(f"Database error when fetching snacks: {str(e)}") def get_snack(sku: str) -> Snack: """Returns a single snack by SKU""" - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute("SELECT * FROM snacks WHERE sku = ?", (sku,)) - record = cursor.fetchone() - return Snack(**record) - + try: + with get_db_connection() as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM snacks WHERE sku = ?", (sku,)) + record = cursor.fetchone() + if record is None: + raise RecordNotFoundError(f"No snack found with SKU: {sku}") + return Snack(**record) + except sqlite3.Error as e: + if(isinstance(e,ConnectionError)): + raise ConnectionError(f"Error when connecting to database: {str(e)}") + raise DatabaseError(f"Database error when fetching snack {sku}: {str(e)}") + def delete_snack(sku: str) -> Snack: """Removes a snack from the database""" - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute(""" - DELETE FROM snacks - WHERE sku = ? - RETURNING * - """, (sku,)) - record = cursor.fetchone() - return Snack(**record) - - + try: + with get_db_connection() as conn: + cursor = conn.cursor() + cursor.execute(""" + DELETE FROM snacks + WHERE sku = ? + RETURNING * + """, (sku,)) + record = cursor.fetchone() + return Snack(**record) + except sqlite3.Error as e: + raise DatabaseError(f"Database error when deleting snack {sku}: {str(e)}") + + def create_snack(snack: SnackCreateSchema) -> Snack: """Creates a new snack in the database""" - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute(""" - INSERT INTO snacks - (sku, name, quantity, price, description, category, photo_url) - VALUES - (?, ?, ?, ?, ?, ?, ?) - RETURNING * - """, ( - snack.sku, - snack.name, - snack.quantity if snack.quantity is not None else 1, - snack.price, - snack.description, - snack.category, - snack.photo_url - )) - record = cursor.fetchone() - return Snack(**record) + try: + with get_db_connection() as conn: + cursor = conn.cursor() + cursor.execute(""" + INSERT INTO snacks + (sku, name, quantity, price, description, category, photo_url) + VALUES + (?, ?, ?, ?, ?, ?, ?) + RETURNING * + """, ( + snack.sku, + snack.name, + snack.quantity if snack.quantity is not None else 1, + snack.price, + snack.description, + snack.category, + snack.photo_url + )) + record = cursor.fetchone() + if not isinstance(snack.sku, str): + raise RecordNotFoundError(f"{snack.sku} is not a string") + if not isinstance(snack.name, str): + raise RecordNotFoundError(f"{snack.name} is not a string") + if not isinstance(snack.quantity, int): + raise RecordNotFoundError(f"{snack.quantity} is not an int") + if not isinstance(snack.price, float): + raise RecordNotFoundError(f"{snack.price} is not a float") + if not isinstance(snack.description, str): + raise RecordNotFoundError(f"{snack.description} is not a string") + if not isinstance(snack.category, str): + raise RecordNotFoundError(f"{snack.category} is not a string") + if not isinstance(snack.photo_url, str): + raise RecordNotFoundError(f"{snack.photo_url} is not a string") + return Snack(**record) + if record is none: + raise RecordNotFoundError(f"Error when creating snack: {str(e)}") + except sqlite3.Error as e: + raise DatabaseError(f"Database error when creating snack {snack.sku}: {str(e)}") def update_snack(sku: str, updates: SnackUpdateSchema) -> Snack: """Updates an existing snack in the database""" - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute(""" - UPDATE snacks - SET - name = ?, - quantity = ?, - price = ?, - description = ?, - category = ?, - photo_url = ? - WHERE sku = ? - RETURNING * - """, ( - updates.name, - updates.quantity, - updates.price, - updates.description, - updates.category, - updates.photo_url, - sku - )) - record = cursor.fetchone() - return Snack(**record) + try: + with get_db_connection() as conn: + cursor = conn.cursor() + cursor.execute(""" + UPDATE snacks + SET + name = ?, + quantity = ?, + price = ?, + description = ?, + category = ?, + photo_url = ? + WHERE sku = ? + RETURNING * + """, ( + updates.name, + updates.quantity, + updates.price, + updates.description, + updates.category, + updates.photo_url, + sku + )) + record = cursor.fetchone() + return Snack(**record) + except sqlite3.Error as e: + raise DatabaseError(f"Database error when deleting snack {sku}: {str(e)}") # Initialize the database and create tables diff --git a/backend/utils/exceptions.py b/backend/utils/exceptions.py index 3884261..8bd5646 100644 --- a/backend/utils/exceptions.py +++ b/backend/utils/exceptions.py @@ -16,126 +16,4 @@ class DuplicateRecordError(DatabaseError): class DatabaseInitError(DatabaseError): """Failed to initialize database""" - pass - -import sqlite3 -from typing import Type -from models.snack import Snack, SnackCreateSchema, SnackUpdateSchema - - -def get_db_connection(db_file_path:str="data/db.sqlite3"): - """Creates and returns a SQLite database connection""" - try: - connection = sqlite3.connect(db_file_path) - connection.row_factory = sqlite3.Row - return connection - except sqlite3.Error as e: - raise ConnectionError(f"Failed to connect to database: {str(e)}") - - - -def get_snack(sku: str) -> Snack: - """ - Returns a single snack by SKU - - Args: - sku: The unique SKU of the snack - - Returns: - Snack object - - Raises: - RecordNotFoundError: If no snack with the given SKU exists - ConnectionError: If database connection fails - DatabaseError: For other database errors - """ - try: - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute("SELECT * FROM snacks WHERE sku = ?", (sku,)) - record = cursor.fetchone() - - if record is None: - raise RecordNotFoundError(f"No snack found with SKU: {sku}") - - return Snack(**record) - except sqlite3.Error as e: - raise DatabaseError(f"Database error when fetching snack {sku}: {str(e)}") - except RecordNotFoundError as e: - raise RecordNotFoundError(f"Snack not found {sku}: {str(e)}") - except ConnectionError as e: - raise ConnectionError(f"Database error when connecting to database {sku}: {str(e)}") - - - -def create_snack(sku: str,name: str, ) -> Snack: - """ - Returns a single snack by SKU - - Args: - sku: The unique SKU of the snack - - Returns: - result - - Raises: - RecordNotFoundError: If no snack with the given SKU exists - ConnectionError: If database connection fails - DatabaseError: For other database errors - """ - try: - with get_db_connection() as conn: - cursor = conn.cursor() - cursor.execute("SELECT * FROM snacks WHERE sku = ?", (sku,)) - record = cursor.fetchone() - - if Type(sku) is not str: - raise RecordNotFoundError(f"{sku} is not a string") - if Type(name) is not str: - raise RecordNotFoundError(f"{name} is not a string") - return Snack(**record) - except ConnectionError as e: - raise ConnectionError(f"Database error when connecting to database {sku}: {str(e)}") - except DatabaseError as e: - raise DatabaseError(f"Database error {sku}: {str(e)}") - - -# TESTING THIS FOR JAYDEN CUZ HE NOT SMART -# Test 2.0 - -# def update_snack(sku: str) -> Snack: -# """ -# Returns a single snack by SKU - -# Args: -# sku: The unique SKU of the snack - -# Returns: -# Snack object - -# Raises: -# RecordNotFoundError: If no snack with the given SKU exists -# ConnectionError: If database connection fails -# DatabaseError: For other database errors -# """ -# try: -# with get_db_connection() as conn: -# cursor = conn.cursor() -# cursor.execute("SELECT * FROM snacks WHERE sku = ?", (sku,)) -# record = cursor.fetchone() - -# if record is None: -# raise RecordNotFoundError(f"No snack found with SKU: {sku}") - -# return Snack(**record) -# except sqlite3.Error as e: -# raise DatabaseError(f"Database error when fetching snack {sku}: {str(e)}") -# except RecordNotFoundError as e: -# raise RecordNotFoundError(f"Snack not found {sku}: {str(e)}") -# except ConnectionError as e: -# raise ConnectionError(f"Database error when connecting to database {sku}: {str(e)}") -# except DatabaseError as e: -# raise DatabaseError(f"Database error {sku}: {str(e)}") - - - + pass \ No newline at end of file From 5975a6fc572862e94791f621361428158cedea8b Mon Sep 17 00:00:00 2001 From: Aaron Truong Date: Thu, 20 Mar 2025 19:44:21 -0700 Subject: [PATCH 5/5] updated db handling for most functions --- backend/utils/db.py | 54 ++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/backend/utils/db.py b/backend/utils/db.py index 9308d8a..fda6ac5 100644 --- a/backend/utils/db.py +++ b/backend/utils/db.py @@ -11,15 +11,17 @@ """ import os import sqlite3 -from typing import Type from models.snack import Snack, SnackCreateSchema, SnackUpdateSchema from exceptions import DatabaseError, ConnectionError, RecordNotFoundError, DuplicateRecordError, DatabaseInitError def get_db_connection(db_file_path:str="data/db.sqlite3"): """Creates and returns a SQLite database connection""" - connection = sqlite3.connect(db_file_path) - connection.row_factory = sqlite3.Row # Allows accessing columns by name - return connection + try: + connection = sqlite3.connect(db_file_path) + connection.row_factory = sqlite3.Row # Allows accessing columns by name + return connection + except sqlite3.Error as e: + raise DatabaseError(f"Error creating database connection: {str(e)}") def init_db(db_file_path: str = "data/db.sqlite3"): @@ -32,7 +34,7 @@ def init_db(db_file_path: str = "data/db.sqlite3"): conn.executescript(schema) except sqlite3.Error as e: raise DatabaseInitError(f"Error when initializing database: {str(e)}") - + def get_inventory() -> list[Snack]: """Returns all snacks in the database""" @@ -41,9 +43,13 @@ def get_inventory() -> list[Snack]: cursor = conn.cursor() cursor.execute("SELECT * FROM snacks") records = cursor.fetchall() + if not records: + raise RecordNotFoundError("No snacks in database") return [Snack(**record) for record in records] + except ConnectionError as e: + raise ConnectionError(f"Error when connecting to database: {str(e)}") except sqlite3.Error as e: - raise DatabaseError(f"Database error when fetching snacks: {str(e)}") + raise DatabaseError(f"Database error when fetching all snacks: {str(e)}") def get_snack(sku: str) -> Snack: @@ -56,9 +62,9 @@ def get_snack(sku: str) -> Snack: if record is None: raise RecordNotFoundError(f"No snack found with SKU: {sku}") return Snack(**record) + except ConnectionError as e: + raise ConnectionError(f"Error when connecting to database: {str(e)}") except sqlite3.Error as e: - if(isinstance(e,ConnectionError)): - raise ConnectionError(f"Error when connecting to database: {str(e)}") raise DatabaseError(f"Database error when fetching snack {sku}: {str(e)}") @@ -73,7 +79,11 @@ def delete_snack(sku: str) -> Snack: RETURNING * """, (sku,)) record = cursor.fetchone() + if record is None: + raise RecordNotFoundError(f"Didn't delete snack {sku} successfully") return Snack(**record) + except ConnectionError as e: + raise ConnectionError(f"Error when connecting to database: {str(e)}") except sqlite3.Error as e: raise DatabaseError(f"Database error when deleting snack {sku}: {str(e)}") @@ -99,23 +109,13 @@ def create_snack(snack: SnackCreateSchema) -> Snack: snack.photo_url )) record = cursor.fetchone() - if not isinstance(snack.sku, str): - raise RecordNotFoundError(f"{snack.sku} is not a string") - if not isinstance(snack.name, str): - raise RecordNotFoundError(f"{snack.name} is not a string") - if not isinstance(snack.quantity, int): - raise RecordNotFoundError(f"{snack.quantity} is not an int") - if not isinstance(snack.price, float): - raise RecordNotFoundError(f"{snack.price} is not a float") - if not isinstance(snack.description, str): - raise RecordNotFoundError(f"{snack.description} is not a string") - if not isinstance(snack.category, str): - raise RecordNotFoundError(f"{snack.category} is not a string") - if not isinstance(snack.photo_url, str): - raise RecordNotFoundError(f"{snack.photo_url} is not a string") + if record is None: + raise DatabaseError(f"Error when creating snack") return Snack(**record) - if record is none: - raise RecordNotFoundError(f"Error when creating snack: {str(e)}") + except DuplicateRecordError as e: + raise DuplicateRecordError(f"This snack already exists: {str(e)}") + except ConnectionError as e: + raise ConnectionError(f"Error when connecting to database: {str(e)}") except sqlite3.Error as e: raise DatabaseError(f"Database error when creating snack {snack.sku}: {str(e)}") @@ -146,9 +146,13 @@ def update_snack(sku: str, updates: SnackUpdateSchema) -> Snack: sku )) record = cursor.fetchone() + if record is None: + raise RecordNotFoundError(f"Snack with SKU: {sku} doesn't exist") return Snack(**record) + except ConnectionError as e: + raise ConnectionError(f"Error when connecting to database: {str(e)}") except sqlite3.Error as e: - raise DatabaseError(f"Database error when deleting snack {sku}: {str(e)}") + raise DatabaseError(f"Database error when updating snack {sku}: {str(e)}") # Initialize the database and create tables