diff --git a/.gitignore b/.gitignore index 27f1fd8..faedc77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,45 @@ -.ruff_cachetry init +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +# C extensions +*.so + +# Distribution / packaging +*.egg +*.egg-info/ +dist/ +build/ +.eggs/ + +# Virtual environment +venv/ +env/ +.venv/ +.env/ + +# PyCharm .idea/ + +# VS Code +.vscode/ + +# Pytest +.pytest_cache/ + +# Jupyter Notebook +.ipynb_checkpoints/ + +# Logs and coverage +*.log +.coverage +htmlcov/ +.tox/ +nosetests.xml +coverage.xml +*.cover + +# System files +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/README.md b/README.md index f8e70ba..59b8752 100644 --- a/README.md +++ b/README.md @@ -1 +1,12 @@ -Learn project with Python examples and for homework \ No newline at end of file +# lib-management-sys +## Result of script execution: ++ creating a library ++ creating an instance of a book and a magazine ++ adding them to the library ++ displaying a list of books in the library ++ displaying a list of library books by author name ++ saving the list of books to a file ++ deleting a book from the library ++ displaying a list of books after deletion ++ adding books from a file to the library ++ displaying a list of library books after addition. \ No newline at end of file diff --git a/main.py b/main.py index 94e3a87..75a8e36 100644 --- a/main.py +++ b/main.py @@ -1,16 +1,176 @@ -# This is a sample Python script. +from pydantic import BaseModel +from abc import ABC, abstractmethod -# Press ⌃R to execute it or replace it with your code. -# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings. +class BookModel(BaseModel): + title: str + author: str + year: int +class MagazineModel(BaseModel): + name: str + number: int + month: str + year: int -def print_hi(name): - # Use a breakpoint in the code line below to debug your script. - print(f'Hi, {name}') # Press ⌘F8 to toggle the breakpoint. +class Publication(ABC): + @abstractmethod + def show_author(self): + pass +class Book(Publication): + def __init__(self, model: BookModel): + self.__model = model -# Press the green button in the gutter to run the script. -if __name__ == '__main__': - print_hi('PyCharm') + def __str__(self): + return f"{self.__model.title} by {self.__model.author} in {self.__model.year}" -# See PyCharm help at https://www.jetbrains.com/help/pycharm/ + def show_author(self) -> str: + return self.__model.author + +class Magazine(Publication): + def __init__(self, model: MagazineModel): + self.__model = model + + def __str__(self): + return f"{self.__model.name} #{self.__model.number} / {self.__model.month} in {self.__model.year}" + + def show_author(self) -> str: + return self.__model.name + +class Library: + def __init__(self, book_list: list[Book]): + self.books = book_list + self.current_book = 0 + + def __iter__(self): + self.current_book = 0 + + return self + + def __next__(self): + if self.current_book >= len(self.books): + raise StopIteration + + b = self.books[self.current_book] + self.current_book += 1 + + return b + + def book_generator(self, author): + for b in self.books: + if b.show_author() == author: + yield b + + def add_book(self, b: Book): + self.books.append(b) + + def remove_book(self, b: Book): + self.books.remove(b) + + def save_to_file(self, filename: str): + with open(filename, "w", encoding="utf-8") as f: + for book in self.books: + f.write(str(book) + "\n") + + @staticmethod + def load_from_file(filename: str): + books = [] + with open(filename, "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if " by " in line and " in " in line: + try: + title_part, rest = line.split(" by ", 1) + author_part, year_part = rest.rsplit(" in ", 1) + + title = title_part.strip() + author = author_part.strip() + year = int(year_part.strip()) + model = BookModel(title=title, author=author, year=year) + books.append(Book(model)) + except ValueError: + print(f"Failed to parse line: {line}") + elif " #" in line and " / " and " in " in line: + try: + name_part, rest = line.split(" #", 1) + number_part, rest = rest.split(" / ", 1) + month_part, year_part = rest.rsplit(" in ", 1) + + name = name_part.strip() + number = int(number_part.strip()) + month = month_part.strip() + year = int(year_part.strip()) + + model = MagazineModel(name=name, number=number, month=month, year=year) + books.append(Magazine(model)) + except ValueError: + print(f"Failed to parse line: {line}") + else: + print(f"Invalid format: {line}") + return books + +def add_book_generator(func): + def wrapper(book, library, *args, **kwargs): + print(f"Adding book...") + result = func(book, library, *args, **kwargs) + print(f"Book {book} added to library") + print("[BOOKS IN LIBRARY]") + for b in library.books: + print(b) + + return result + return wrapper + +@add_book_generator +def add_book_library(book: Book, library: Library): + library.add_book(book) + +def remove_book_generator(func): + def wrapper(book, library, *args, **kwargs): + if book not in library.books: + print(f"Book {book} not found in library!") + + return None + + print(f"Removing book...") + result = func(book, library, *args, **kwargs) + print(f"Book removed from library") + print("[BOOKS IN LIBRARY]") + for b in library.books: + print(b) + + return result + return wrapper + +@remove_book_generator +def remove_book_library(book, library): + library.remove_book(book) + +book1 = Book(BookModel(title="Ubik", author="Philip K. Dick", year=1966)) +book2 = Book(BookModel(title="The Man in the High Castle", author="Philip K. Dick", year=1962)) +book3 = Book(BookModel(title="The colour of magic", author="Terry Pratchett", year=1983)) +book4 = Book(BookModel(title="Mort", author="Terry Pratchett", year=1987)) + +magazine1 = Magazine(MagazineModel(name="Nintendo Power", number=13, month="January", year=1989)) +magazine2 = Magazine(MagazineModel(name="Pepper", number=21, month="June", year=1995)) + +myBooks = Library([book1, book2, book3, magazine1, magazine2]) + +print(next(myBooks)) +print(next(myBooks)) +print(next(myBooks)) + +print("\n[ADD BOOKS TO LIBRARY]") +add_book_library(book4, myBooks) + +print(next(myBooks)) + +remove_book_library(book1, myBooks) + +myBooks.save_to_file("log.txt") + +print("\n[LOAD FROM FILE]") +r = myBooks.load_from_file("log.txt") + +for book in r: + print(book.show_author()) \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 6b3dbaa..2b58baf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "annotated-types" @@ -201,14 +201,14 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.14.0" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, + {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, + {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] [[package]]