mirror of
https://github.com/kristoferssolo/LU-bookstore.git
synced 2025-10-21 18:00:34 +00:00
feat(log): add logger
This commit is contained in:
parent
2ed46444bb
commit
dbb16c9e9a
@ -6,7 +6,12 @@ authors = [{ name = "Kristofers Solo", email = "dev@kristofers.xyz" }]
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
license = { text = "MIT" }
|
license = { text = "MIT" }
|
||||||
dependencies = ["attrs==23.1.0", "customtkinter==5.2.1", "packaging==23.2"]
|
dependencies = [
|
||||||
|
"attrs==23.1.0",
|
||||||
|
"customtkinter==5.2.1",
|
||||||
|
"loguru==0.7.2",
|
||||||
|
"packaging==23.2",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
attrs>=23.1.0
|
attrs>=23.1.0
|
||||||
customtkinter>=5.2.1
|
customtkinter>=5.2.1
|
||||||
|
loguru>=0.7.2
|
||||||
packaging>=23.2
|
packaging>=23.2
|
||||||
|
|||||||
@ -1,18 +1,22 @@
|
|||||||
from attrs import define, field, setters, validators
|
from attrs import define, field, setters, validators
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .isbn import ISBN
|
from .isbn import ISBN
|
||||||
|
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def _check_price_value(instance, attribute, value):
|
def _check_price_value(instance, attribute, value):
|
||||||
if value < 0:
|
if value < 0:
|
||||||
raise ValueError("Price must be larger or equal to 0!")
|
raise ValueError("Price must be larger or equal to 0!")
|
||||||
|
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def _check_stock_value(instance, attribute, value):
|
def _check_stock_value(instance, attribute, value):
|
||||||
if value < 0:
|
if value < 0:
|
||||||
raise ValueError("Stock must be larger or equal to 0!")
|
raise ValueError("Stock must be larger or equal to 0!")
|
||||||
|
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def _to_isbn(number: str):
|
def _to_isbn(number: str):
|
||||||
return ISBN(number)
|
return ISBN(number)
|
||||||
|
|
||||||
@ -26,12 +30,15 @@ class Book:
|
|||||||
stock: int = field(converter=int, validator=[validators.instance_of(int), _check_stock_value])
|
stock: int = field(converter=int, validator=[validators.instance_of(int), _check_stock_value])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@logger.catch
|
||||||
def fields(cls) -> tuple[str, str, str, str, str]:
|
def fields(cls) -> tuple[str, str, str, str, str]:
|
||||||
return "ISBN", "Title", "Author", "Price", "Stock"
|
return "ISBN", "Title", "Author", "Price", "Stock"
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def values(self) -> tuple[ISBN, str, str, float, int]:
|
def values(self) -> tuple[ISBN, str, str, float, int]:
|
||||||
return self.isbn, self.title, self.author, self.price, self.stock
|
return self.isbn, self.title, self.author, self.price, self.stock
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def get(self, field: str, default: str = "") -> ISBN | str | float | int:
|
def get(self, field: str, default: str = "") -> ISBN | str | float | int:
|
||||||
match field:
|
match field:
|
||||||
case "ISBN":
|
case "ISBN":
|
||||||
@ -47,5 +54,6 @@ class Book:
|
|||||||
case _:
|
case _:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
yield from self.values()
|
yield from self.values()
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from .book import Book
|
from .book import Book
|
||||||
from .isbn import ISBN
|
from .isbn import ISBN
|
||||||
|
|
||||||
|
|
||||||
class Inventory:
|
class Inventory:
|
||||||
|
@logger.catch
|
||||||
def __init__(self, db_path: Path) -> None:
|
def __init__(self, db_path: Path) -> None:
|
||||||
self.conn = sqlite3.connect(db_path)
|
self.conn = sqlite3.connect(db_path)
|
||||||
self.cursor = self.conn.cursor()
|
self.cursor = self.conn.cursor()
|
||||||
@ -21,24 +24,28 @@ class Inventory:
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
"""Save `Inventory` to SQLite database."""
|
"""Save `Inventory` to SQLite database."""
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""Close database connection."""
|
"""Close database connection."""
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def add(self, *books: Book) -> None:
|
def add(self, *books: Book) -> None:
|
||||||
"""Add `Book` to the `Inventory`. `Book`s ISBN must be unique."""
|
"""Add `Book` to the `Inventory`. `Book`s ISBN must be unique."""
|
||||||
for book in books:
|
for book in books:
|
||||||
try:
|
try:
|
||||||
self.cursor.execute("INSERT INTO Book VALUES (?, ?, ?, ?, ?)", (book.isbn, book.title, book.author, book.price, book.stock))
|
self.cursor.execute("INSERT INTO Book VALUES (?, ?, ?, ?, ?)", (book.isbn, book.title, book.author, book.price, book.stock))
|
||||||
self.save()
|
self.save()
|
||||||
print(f"Book with ISBN: {book.isbn} was successfully saved")
|
logger.info(f"Create: {book}")
|
||||||
except sqlite3.InternalError as e:
|
except sqlite3.InternalError as e:
|
||||||
print(f"A book with ISBN {book.isbn} already exists in the database.\n{e}")
|
logger.error(f"A book with ISBN {book.isbn} already exists in the database.\t{e}")
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def edit(self, book: Book) -> None:
|
def edit(self, book: Book) -> None:
|
||||||
"""Edit `Book`."""
|
"""Edit `Book`."""
|
||||||
try:
|
try:
|
||||||
@ -46,20 +53,22 @@ class Inventory:
|
|||||||
"UPDATE Book SET title = ?, author = ?, price = ?, stock = ? WHERE isbn = ?", (book.title, book.author, book.price, book.stock, book.isbn)
|
"UPDATE Book SET title = ?, author = ?, price = ?, stock = ? WHERE isbn = ?", (book.title, book.author, book.price, book.stock, book.isbn)
|
||||||
)
|
)
|
||||||
self.save()
|
self.save()
|
||||||
print(f"Book with ISBN: {book.isbn} was successfully updated!")
|
logger.info(f"Update: {book}")
|
||||||
except sqlite3.InternalError as e:
|
except sqlite3.InternalError as e:
|
||||||
print(f"Something went wrong.\n{e}")
|
logger.error(f"Something went wrong.\t{e}")
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def delete(self, isbn: ISBN) -> Book | None:
|
def delete(self, isbn: ISBN) -> Book | None:
|
||||||
"""Deletes `Book` from `Inventory` by `ISBN` and returns deleted `Book`"""
|
"""Deletes `Book` from `Inventory` by `ISBN` and returns deleted `Book`"""
|
||||||
deleted_book = self.find_by_isbn(isbn)
|
deleted_book = self.find_by_isbn(isbn)
|
||||||
|
|
||||||
self.cursor.execute("DELETE FROM Book WHERE isbn = ?", (isbn,))
|
self.cursor.execute("DELETE FROM Book WHERE isbn = ?", (isbn,))
|
||||||
self.save()
|
self.save()
|
||||||
print(f"Book with ISBN: {isbn} was successfully deleted!")
|
logger.info(f"Delete: {deleted_book}")
|
||||||
|
|
||||||
return deleted_book
|
return deleted_book
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def find_by_isbn(self, isbn: ISBN) -> Book | None:
|
def find_by_isbn(self, isbn: ISBN) -> Book | None:
|
||||||
"""Looks up `Book` within `Inventory` by book `ISBN` and returns it. Returns `None` if it doesn't exist."""
|
"""Looks up `Book` within `Inventory` by book `ISBN` and returns it. Returns `None` if it doesn't exist."""
|
||||||
self.cursor.execute("SELECT * FROM Book WHERE isbn = ?", (isbn,))
|
self.cursor.execute("SELECT * FROM Book WHERE isbn = ?", (isbn,))
|
||||||
@ -68,6 +77,7 @@ class Inventory:
|
|||||||
return None
|
return None
|
||||||
return Book(*book)
|
return Book(*book)
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def find_by_title(self, title: str) -> list[Book] | None:
|
def find_by_title(self, title: str) -> list[Book] | None:
|
||||||
"""Looks up `Book`s within `Inventory` by book title and returns them as a `List`. Returns `None` if none were found"""
|
"""Looks up `Book`s within `Inventory` by book title and returns them as a `List`. Returns `None` if none were found"""
|
||||||
self.cursor.execute("SELECT * FROM Book WHERE title LIKE ?", (f"%{title}%",))
|
self.cursor.execute("SELECT * FROM Book WHERE title LIKE ?", (f"%{title}%",))
|
||||||
@ -76,6 +86,7 @@ class Inventory:
|
|||||||
return None
|
return None
|
||||||
return [Book(*book) for book in books]
|
return [Book(*book) for book in books]
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def find_by_author(self, author: str) -> list[Book] | None:
|
def find_by_author(self, author: str) -> list[Book] | None:
|
||||||
"""Looks up `Book`s within `Inventory` by book author and returns them as a `List`. Returns `None` if none were found"""
|
"""Looks up `Book`s within `Inventory` by book author and returns them as a `List`. Returns `None` if none were found"""
|
||||||
self.cursor.execute("SELECT * FROM Book WHERE author LIKE ?", (f"%{author}%",))
|
self.cursor.execute("SELECT * FROM Book WHERE author LIKE ?", (f"%{author}%",))
|
||||||
@ -84,6 +95,7 @@ class Inventory:
|
|||||||
return None
|
return None
|
||||||
return [Book(*book) for book in books]
|
return [Book(*book) for book in books]
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def list_all(self) -> list[Book | None]:
|
def list_all(self) -> list[Book | None]:
|
||||||
"""Returns `List` of all `Book`s."""
|
"""Returns `List` of all `Book`s."""
|
||||||
self.cursor.execute("SELECT * FROM Book")
|
self.cursor.execute("SELECT * FROM Book")
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from attrs import define, field, validators
|
from attrs import define, field, validators
|
||||||
|
|
||||||
|
|
||||||
# TODO: create checksum method
|
|
||||||
@define(frozen=True)
|
@define(frozen=True)
|
||||||
class ISBN(str):
|
class ISBN(str):
|
||||||
number: str = field(converter=str, validator=validators.matches_re(r"^\d{10}$|^\d{13}$"))
|
number: str = field(converter=str, validator=validators.matches_re(r"^\d{10}$|^\d{13}$"))
|
||||||
|
|||||||
12
src/main.py
12
src/main.py
@ -4,9 +4,21 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from bookstore.inventory import Inventory
|
from bookstore.inventory import Inventory
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
from ui.app import App
|
from ui.app import App
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
logger.add(
|
||||||
|
Path("logs", "bookstore.log"),
|
||||||
|
format="{time} | {level} | {message}",
|
||||||
|
level="INFO",
|
||||||
|
rotation="1 MB",
|
||||||
|
compression="zip",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@logger.catch
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
db_path = Path("db.sqlite3")
|
db_path = Path("db.sqlite3")
|
||||||
inventory = Inventory(db_path)
|
inventory = Inventory(db_path)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user