From 75c295ce4b6396e5f1a3c7042a15b5cb5eda7a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ur=C3=ADa?= Date: Fri, 13 Feb 2026 14:29:39 +0100 Subject: [PATCH] [UPD] Added BookTag --- app/schema/library/__init__.py | 5 +-- app/schema/library/book.py | 25 +++++++------ app/schema/library/book_tag.py | 21 +++++++++++ app/schema/library/library.py | 4 +++ app/schema/library/tag.py | 12 ++++--- tests/test_db.py | 66 +++++++++++++++++++++++++++++++--- 6 files changed, 112 insertions(+), 21 deletions(-) create mode 100644 app/schema/library/book_tag.py diff --git a/app/schema/library/__init__.py b/app/schema/library/__init__.py index 69eb8c5..c9f6b4b 100644 --- a/app/schema/library/__init__.py +++ b/app/schema/library/__init__.py @@ -1,6 +1,7 @@ from .base import Base from .env import Env -#from .tag import Tag +from .tag import Tag from .path import Path from .library import Library -from .book import Book \ No newline at end of file +from .book import Book +from .book_tag import BookTag \ No newline at end of file diff --git a/app/schema/library/book.py b/app/schema/library/book.py index 0cf5038..06192d8 100644 --- a/app/schema/library/book.py +++ b/app/schema/library/book.py @@ -1,6 +1,6 @@ from typing import List from typing import Optional -from sqlalchemy import String, ForeignKey +from sqlalchemy import String, ForeignKey, Integer from sqlalchemy.orm import Mapped from sqlalchemy.orm import mapped_column from sqlalchemy.orm import relationship @@ -10,17 +10,22 @@ from .base import Base class Book(Base): __tablename__ = "book" id: Mapped[int] = mapped_column(primary_key=True) - hash: Mapped[str] = mapped_column(String(255)) - file_name: Mapped[str] = mapped_column(String(255)) - file_path: Mapped[str] = mapped_column(String(65656)) - name: Mapped[str] = mapped_column(String(255)) - publisher: Mapped[str] = mapped_column(String(255)) - notes: Mapped[str] = mapped_column(String(65656)) - classification: Mapped[int] = mapped_column(primary_key=True) + hash: Mapped[Optional[str]] = mapped_column(String(255)) + file_name: Mapped[Optional[str]] = mapped_column(String(65656)) + file_path: Mapped[Optional[str]] = mapped_column(String(65656)) + name: Mapped[str] = mapped_column(String(65656)) + publisher: Mapped[Optional[str]] = mapped_column(String(65656)) + notes: Mapped[Optional[str]] = mapped_column(String(65656)) + classification: Mapped[Optional[int]] = mapped_column(Integer) library_id: Mapped[int] = mapped_column(ForeignKey("library.id")) library: Mapped[int] = relationship("Library", back_populates="books") + tags: Mapped[List["BookTag"]] = relationship( + back_populates="book", cascade="all, delete-orphan" + ) + def __repr__(self) -> str: - return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}," \ - " notes={self.notes!r}, classification={self.classification!r})" \ No newline at end of file + return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}, " \ + f"notes={self.notes!r}, classification={self.classification!r}), " \ + f"hash={self.hash!r}, file_name={self.file_name}, file_path={self.file_path})" \ No newline at end of file diff --git a/app/schema/library/book_tag.py b/app/schema/library/book_tag.py new file mode 100644 index 0000000..b17dbb9 --- /dev/null +++ b/app/schema/library/book_tag.py @@ -0,0 +1,21 @@ +from typing import List +from typing import Optional +from sqlalchemy import String, ForeignKey +from sqlalchemy.orm import Mapped +from sqlalchemy.orm import mapped_column +from sqlalchemy.orm import relationship + +from .base import Base + +class BookTag(Base): + __tablename__ = "book_tag_relation" + id: Mapped[int] = mapped_column(primary_key=True) + + book_id: Mapped[int] = mapped_column(ForeignKey("book.id")) + book: Mapped[int] = relationship("Book", back_populates="tags") + + tag_id: Mapped[int] = mapped_column(ForeignKey("tag.id")) + tag: Mapped[int] = relationship("Tag", back_populates="books") + + def __repr__(self) -> str: + return f"BookTag(id={self.id!r}, book_id={self.book_id!r}, tag_id={self.tag_id!r}" \ No newline at end of file diff --git a/app/schema/library/library.py b/app/schema/library/library.py index 57c7211..1c9b399 100644 --- a/app/schema/library/library.py +++ b/app/schema/library/library.py @@ -25,5 +25,9 @@ class Library(Base): back_populates="library", cascade="all, delete-orphan" ) + tags: Mapped[List["Tag"]] = relationship( + back_populates="library", cascade="all, delete-orphan" + ) + def __repr__(self) -> str: return f"Library(id={self.id!r}, name={self.name!r}, notes={self.notes!r}" \ No newline at end of file diff --git a/app/schema/library/tag.py b/app/schema/library/tag.py index 0f4c544..2d8c855 100644 --- a/app/schema/library/tag.py +++ b/app/schema/library/tag.py @@ -1,6 +1,6 @@ from typing import List from typing import Optional -from sqlalchemy import String +from sqlalchemy import String, ForeignKey from sqlalchemy.orm import Mapped from sqlalchemy.orm import mapped_column from sqlalchemy.orm import relationship @@ -12,10 +12,12 @@ class Tag(Base): id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(String(255)) - books: Mapped[List["Book"]] = relationship( - back_populates="tags", cascade="all, delete-orphan" + books: Mapped[List["BookTag"]] = relationship( + back_populates="tag", cascade="all, delete-orphan" ) + library_id: Mapped[int] = mapped_column(ForeignKey("library.id")) + library: Mapped[int] = relationship("Library", back_populates="tags") + def __repr__(self) -> str: - return f"Book(id={self.id!r}, name={self.name!r}, publisher={self.publisher!r}," \ - " notes={self.notes!r}, classification={self.classification!r})" \ No newline at end of file + return f"Tag(id={self.id!r}, name={self.name!r})" \ No newline at end of file diff --git a/tests/test_db.py b/tests/test_db.py index 307878f..2cfaa19 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -4,10 +4,11 @@ import unittest from sqlalchemy.orm import sessionmaker from app.api.actions import install -from app.api.cruds.library import create, read -from app.schema.library import Library, Path, Env +from app.api.cruds.library import create, read, update +from app.schema.library import Library, Path, Env, Book, Tag, BookTag import logging +logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) class TestDB(unittest.TestCase): @@ -16,6 +17,14 @@ class TestDB(unittest.TestCase): os.environ["DEV_URIA_BIBLIOGAME_CONFIG_DB"] = "sqlite:///" os.environ["DEV_URIA_BIBLIOGAME_DEBUG"] = "true" self.engine = install() + self.tags = [ + Tag( + name="Foo" + ), + Tag( + name="Bar" + ) + ] self.library = Library( name="Library Test", notes="My duckling library test", @@ -29,7 +38,18 @@ class TestDB(unittest.TestCase): key="ENVIRONMENT_VARIABLE", value="Clearly an environment variable" ) - ] + ], + books=[ + Book( + name="Test book", + tags=[ + BookTag( + tag=self.tags[0] + ) + ] + ), + ], + tags=self.tags ) self.Session = sessionmaker(bind=self.engine) self.session = self.Session() @@ -64,8 +84,46 @@ class TestDB(unittest.TestCase): self.assertEqual(library.envs[0].key, self.library.envs[0].key) self.assertEqual(library.envs[0].value, self.library.envs[0].value) self.assertEqual(library.envs[0].key, "ENVIRONMENT_VARIABLE") - self.assertEqual(library.envs[0].value, "Clearly an environment variable") + self.assertEqual(library.envs[0].value, "Clearly an environment variable") + def test_read_book(self): + library = read(self.session, 1) + book = library.books[0] + logger.debug(f"BOOK: {book}") + self.assertEqual(book.name, self.library.books[0].name) + self.assertEqual(book.name, "Test book") + def test_read_tags(self): + library = read(self.session, 1) + tags = library.tags + self.assertEqual(tags, self.library.tags) + self.assertEqual(tags, self.tags) + self.assertEqual(str(tags[0]), str(self.tags[0])) + self.assertEqual(tags[0].name, self.tags[0].name) + self.assertEqual(tags[0].name, "Foo") + self.assertEqual(str(tags[1]), str(self.tags[1])) + self.assertEqual(tags[1].name, self.tags[1].name) + self.assertEqual(tags[1].name, "Bar") + + def test_read_book_tags(self): + library = read(self.session, 1) + book = library.books[0] + tags = library.tags + logger.debug(f"BOOK TAGS: {book.tags}") + self.assertEqual(str(book.tags[0].tag), str(self.tags[0])) + self.assertEqual(str(book.tags[0].tag), str(tags[0])) + self.assertEqual(book.tags[0].tag.name, tags[0].name) + self.assertEqual(book.tags[0].tag.name, "Foo") + self.assertNotEqual(book.tags[0].tag.name, "Bar") + + def test_update_name(self): + library = read(self.session, 1) + library.name = "Another Library" + update(self.session, library) + self.assertEqual(library.name, self.library.name) + self.assertNotEqual(library.name, "Library Test") + self.assertEqual(library.name, "Another Library") + + if __name__ == "__main__": unittest.main() \ No newline at end of file