From 3537642df99298dc9fda45494ff6f063b591eda9 Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Tue, 13 May 2025 00:42:41 +0200 Subject: [PATCH] add CRUD for WorkType --- kontor-api/.gitignore | 2 + kontor-api/src/core/log_conf.py | 53 ++++++++++++ kontor-api/src/db/models/base.py | 2 +- kontor-api/src/db/models/bookshelf.py | 12 +-- kontor-api/src/db/models/comic.py | 19 +++- kontor-api/src/db/repository/comic.py | 19 +++- kontor-api/src/main.py | 12 ++- kontor-api/src/schema/comics/artist.py | 1 + kontor-api/src/schema/comics/comic.py | 3 +- .../src/templates/comic/artist_detail.html | 8 ++ .../src/templates/comic/comic_detail.html | 4 + .../src/templates/comic/publisher_detail.html | 18 ++++ .../src/templates/comic/worktype_detail.html | 22 +++++ .../{add_worktype.html => worktype_edit.html} | 0 kontor-api/src/templates/comic/worktypes.html | 6 +- .../templates/components/artist_cards.html | 1 + .../src/templates/components/navbar.html | 2 +- kontor-api/src/templates/media/videos.html | 8 +- kontor-api/src/webapps/comic/route_comics.py | 49 +++++++++-- kontor-scripts/{schema => db}/__init__.py | 0 kontor-scripts/db/models/__init__.py | 86 +++++++++++++++++++ kontor-scripts/{schema => db/models}/admin.py | 18 ++-- kontor-scripts/{schema => db/models}/base.py | 0 .../{schema => db/models}/bookshelf.py | 2 +- kontor-scripts/{schema => db/models}/comic.py | 21 ++++- .../{schema => db/models}/database.py | 81 +++-------------- kontor-scripts/{schema => db/models}/media.py | 2 +- .../{schema => db/models}/metadata.py | 2 +- kontor-scripts/{schema => db/models}/tysc.py | 2 +- kontor-scripts/db/repository/__init__.py | 0 kontor-scripts/db/repository/metadata.py | 28 ++++++ kontor-scripts/db/schemas/metadata.py | 15 ++++ kontor-scripts/download.py | 4 +- kontor-scripts/export.py | 42 +++++++-- kontor-scripts/json_to_postgres.py | 1 - kontor-scripts/update_title.py | 8 +- .../thpeetz/kontor/comics/ComicConstants.java | 1 + .../kontor/comics/SetupModuleComics.java | 20 +---- .../de/thpeetz/kontor/comics/data/Artist.java | 2 + .../de/thpeetz/kontor/comics/data/Comic.java | 6 +- .../thpeetz/kontor/comics/data/Publisher.java | 29 ++++++- .../kontor/comics/views/ArtistForm.java | 10 +-- .../kontor/comics/views/ComicForm.java | 10 +-- .../kontor/comics/views/ComicView.java | 3 + .../kontor/comics/views/PublisherForm.java | 18 ++-- .../kontor/comics/views/PublisherView.java | 48 +++++++---- .../kontor/comics/views/WorktypeView.java | 13 +-- .../kontor/common/views/KontorLayoutUtil.java | 10 +-- .../common/views/SeparateMainLayout.java | 6 -- 49 files changed, 514 insertions(+), 215 deletions(-) create mode 100644 kontor-api/src/core/log_conf.py rename kontor-api/src/templates/comic/{add_worktype.html => worktype_edit.html} (100%) rename kontor-scripts/{schema => db}/__init__.py (100%) create mode 100644 kontor-scripts/db/models/__init__.py rename kontor-scripts/{schema => db/models}/admin.py (83%) rename kontor-scripts/{schema => db/models}/base.py (100%) rename kontor-scripts/{schema => db/models}/bookshelf.py (97%) rename kontor-scripts/{schema => db/models}/comic.py (78%) rename kontor-scripts/{schema => db/models}/database.py (77%) rename kontor-scripts/{schema => db/models}/media.py (98%) rename kontor-scripts/{schema => db/models}/metadata.py (96%) rename kontor-scripts/{schema => db/models}/tysc.py (98%) create mode 100644 kontor-scripts/db/repository/__init__.py create mode 100644 kontor-scripts/db/repository/metadata.py create mode 100644 kontor-scripts/db/schemas/metadata.py diff --git a/kontor-api/.gitignore b/kontor-api/.gitignore index 3373a70..f1f28bd 100644 --- a/kontor-api/.gitignore +++ b/kontor-api/.gitignore @@ -1,2 +1,4 @@ .env .coverage +app.log + diff --git a/kontor-api/src/core/log_conf.py b/kontor-api/src/core/log_conf.py new file mode 100644 index 0000000..99c17ad --- /dev/null +++ b/kontor-api/src/core/log_conf.py @@ -0,0 +1,53 @@ +import logging +import logging.config +from typing import Any + +LOGGING_CONFIG: dict[str, Any] = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "default": { + "()": "uvicorn.logging.DefaultFormatter", + "fmt": "%(asctime)s - %(name)s - %(levelprefix)s %(message)s", + "use_colors": None, + }, + "access": { + "()": "uvicorn.logging.AccessFormatter", + "fmt": '%(asctime)s - %(name)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501 + }, + "access_file": { + "()": "uvicorn.logging.AccessFormatter", + "fmt": '%(asctime)s - %(name)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501 + "use_colors": False, + }, + }, + "handlers": { + "file_handler": { + "formatter": "access_file", + "class": "logging.handlers.RotatingFileHandler", + "filename" : "./app.log", + "mode" : "a+", + "maxBytes" : 10*1024*1024, + "backupCount": 0, + }, + + "default": { + "formatter": "default", + "class": "logging.StreamHandler", + "stream": "ext://sys.stderr", + }, + "access": { + "formatter": "access", + "class": "logging.StreamHandler", + "stream": "ext://sys.stdout", + }, + }, + "loggers": { + "uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False}, + "uvicorn.error": {"level": "INFO"}, + "uvicorn.access": {"handlers": ["access", "file_handler"], "level": "INFO", "propagate": False}, + }, +} + +logging.config.dictConfig(LOGGING_CONFIG) +logger = logging.getLogger(__name__) diff --git a/kontor-api/src/db/models/base.py b/kontor-api/src/db/models/base.py index d090abf..84d00ef 100644 --- a/kontor-api/src/db/models/base.py +++ b/kontor-api/src/db/models/base.py @@ -10,7 +10,7 @@ class Base(DeclarativeBase): class BaseMixin: - #id = Column(String(255), primary_key=True, default=uuid.uuid4) + #id = Column(String, primary_key=True, default=uuid.uuid4) id: Mapped[str] = mapped_column(primary_key=True, default=uuid.uuid4) # created_date = Column(DateTime) created_date: Mapped[datetime] = mapped_column(default=func.now()) diff --git a/kontor-api/src/db/models/bookshelf.py b/kontor-api/src/db/models/bookshelf.py index d01fc6b..c2b4e4b 100644 --- a/kontor-api/src/db/models/bookshelf.py +++ b/kontor-api/src/db/models/bookshelf.py @@ -6,28 +6,28 @@ from src.db.models.base import Base, BaseMixin class Article(Base, BaseMixin): __tablename__ = 'article' - title = Column(String(length=255), unique=True) + title = Column(String, unique=True) article_authors = relationship("ArticleAuthor") class Author(Base, BaseMixin): __tablename__ = 'author' - first_name = Column(String(255)) - last_name = Column(String(255)) + first_name = Column(String) + last_name = Column(String) article_authors = relationship("ArticleAuthor") book_authors = relationship("BookAuthor") class BookshelfPublisher(Base, BaseMixin): __tablename__ = 'bookshelf_publisher' - name = Column(String(length=255), unique=True) + name = Column(String, unique=True) books = relationship("Book") class Book(Base, BaseMixin): __tablename__ = 'book' - isbn = Column(String(255), unique=True) - title = Column(String(255)) + isbn = Column(String, unique=True) + title = Column(String) year = Column(Integer, nullable=False) publisher_id = Column(String, ForeignKey('bookshelf_publisher.id'), nullable=False) publisher = relationship('BookshelfPublisher', back_populates="books") diff --git a/kontor-api/src/db/models/comic.py b/kontor-api/src/db/models/comic.py index e9432fe..460421b 100644 --- a/kontor-api/src/db/models/comic.py +++ b/kontor-api/src/db/models/comic.py @@ -1,14 +1,23 @@ -from typing import Dict, List +import uuid +from datetime import datetime +from typing import Dict, List, Optional from natsort import natsorted -from sqlalchemy import Column, ForeignKey, Integer, String, Boolean -from sqlalchemy.orm import relationship +from sqlalchemy import Column, ForeignKey, Integer, String, Boolean, func +from sqlalchemy.orm import relationship, Mapped, mapped_column from src.db.models.base import Base, BaseMixin -class Publisher(Base, BaseMixin): +class Publisher(Base): __tablename__ = "publisher" + id: Mapped[str] = mapped_column(primary_key=True, default=uuid.uuid4) + created_date: Mapped[datetime] = mapped_column(default=func.now()) + last_modified_date: Mapped[datetime] = mapped_column(default=func.now()) + version: Mapped[int] = mapped_column(default=0) name = Column(String, unique=True) + parent_publisher_id: Mapped[Optional[str]] = mapped_column(ForeignKey('publisher.id')) + parent_publisher: Mapped[Optional['Publisher']] = relationship("Publisher", back_populates="imprints", remote_side=[id]) + imprints: Mapped[List['Publisher']] = relationship('Publisher', back_populates="parent_publisher") comics = relationship("Comic") def __repr__(self): @@ -25,6 +34,7 @@ class Comic(Base, BaseMixin): publisher = relationship("Publisher", back_populates="comics") current_order = Column(Boolean) completed = Column(Boolean) + weblink = Column(String, nullable=True) issues = relationship("Issue", order_by="Issue.issue_number") story_arcs = relationship("StoryArc") trade_paperbacks = relationship("TradePaperback") @@ -91,6 +101,7 @@ class Issue(Base, BaseMixin): class Artist(Base, BaseMixin): __tablename__ = "artist" name = Column(String, nullable=False) + weblink = Column(String, nullable=True) comic_works = relationship("ComicWork") def get_comics(self) -> Dict[str, List[str]]: diff --git a/kontor-api/src/db/repository/comic.py b/kontor-api/src/db/repository/comic.py index 53f3a12..a20fccc 100644 --- a/kontor-api/src/db/repository/comic.py +++ b/kontor-api/src/db/repository/comic.py @@ -1,13 +1,14 @@ import uuid from datetime import datetime -from typing import List, Type +from typing import List, Type, AnyStr from sqlalchemy.orm import Session +from src.core.log_conf import logger from src.db.models.comic import Artist, Comic, Issue, WorkType from src.schema.comics.artist import ArtistDetailResponse from src.schema.comics.issue import IssueDetailsResponse -from src.webapps.comic.forms import AddWorktypeForm +from src.schema.comics.worktype import AddWorkType def get_artist_details(artist: Artist) -> ArtistDetailResponse: @@ -42,7 +43,7 @@ def get_issue_details(issue: Issue) -> IssueDetailsResponse: ) return response -def create_new_worktype(work: AddWorktypeForm, db: Session) -> WorkType: +def create_new_worktype(work: AddWorkType, db: Session) -> WorkType: worktype = WorkType() worktype.id = str(uuid.uuid4()) worktype.created_date = datetime.now() @@ -51,5 +52,15 @@ def create_new_worktype(work: AddWorktypeForm, db: Session) -> WorkType: db.add(worktype) db.commit() db.refresh(worktype) - print(worktype) + logger.info(f"create_new_worktype: {worktype}") + return worktype + + +def update_worktype(work: AddWorkType, worktype_id: AnyStr, db: Session) -> WorkType: + logger.info("update worktype") + worktype = db.get(WorkType, worktype_id) + worktype.name = work.worktype + db.add(worktype) + db.commit() + db.refresh(worktype) return worktype diff --git a/kontor-api/src/main.py b/kontor-api/src/main.py index df4abe9..7650618 100644 --- a/kontor-api/src/main.py +++ b/kontor-api/src/main.py @@ -1,19 +1,18 @@ import logging +import logging.config from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from src.apis.base import api_router +from src.core.log_conf import LOGGING_CONFIG, logger from src.db.session import engine from src.db.utils import check_db_connected, check_db_disconnected from src.webapps.base import api_router as web_app_router from src.core.config import settings from src.db.models.base import Base -logging.basicConfig(level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - handlers=[logging.StreamHandler()]) # Logs to console @asynccontextmanager async def lifespan(app: FastAPI): @@ -31,13 +30,12 @@ def configure_static(app: FastAPI): def create_tables(): Base.metadata.create_all(bind=engine) -def start_application(): - logging.info(f"using database: {settings.DATABASE_URL}") +def start_application(log): + log.info(f"using database: {settings.DATABASE_URL}") app = FastAPI(title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION, lifespan=lifespan) include_router(app) configure_static(app) create_tables() return app - -kontor = start_application() +kontor = start_application(logger) diff --git a/kontor-api/src/schema/comics/artist.py b/kontor-api/src/schema/comics/artist.py index c97282c..48d6210 100644 --- a/kontor-api/src/schema/comics/artist.py +++ b/kontor-api/src/schema/comics/artist.py @@ -15,4 +15,5 @@ class ArtistResponse(BaseModel): class ArtistDetailResponse(BaseModel): id: str name: str + weblink: str works: Dict[str, List[str]] diff --git a/kontor-api/src/schema/comics/comic.py b/kontor-api/src/schema/comics/comic.py index 4d7051b..168402a 100644 --- a/kontor-api/src/schema/comics/comic.py +++ b/kontor-api/src/schema/comics/comic.py @@ -16,6 +16,7 @@ class ComicDetailsResponse(BaseModel): title: str completed : bool current_order : bool + weblink: str publisher: str volumes: List[str] works: Dict[str, List[str]] @@ -47,9 +48,9 @@ def get_comic_details(comic: Comic) -> ComicDetailsResponse | None: title=comic.title, completed=comic.completed, current_order=comic.current_order, + weblink=comic.weblink, publisher=comic.publisher.name, volumes=volumes, works=works ) return response - diff --git a/kontor-api/src/templates/comic/artist_detail.html b/kontor-api/src/templates/comic/artist_detail.html index 58ee61b..aed3bd1 100644 --- a/kontor-api/src/templates/comic/artist_detail.html +++ b/kontor-api/src/templates/comic/artist_detail.html @@ -20,6 +20,10 @@ Artist Name {{artist.name}} + + Link + {{artist.weblink}} + Works @@ -46,5 +50,9 @@ +
+
Back to list
+
{% endblock %} + diff --git a/kontor-api/src/templates/comic/comic_detail.html b/kontor-api/src/templates/comic/comic_detail.html index 76a5b79..c30c31c 100644 --- a/kontor-api/src/templates/comic/comic_detail.html +++ b/kontor-api/src/templates/comic/comic_detail.html @@ -27,6 +27,10 @@ {% endwith %} + + Link + {{comic.weblink}} + Works diff --git a/kontor-api/src/templates/comic/publisher_detail.html b/kontor-api/src/templates/comic/publisher_detail.html index 6429d58..d9fc233 100644 --- a/kontor-api/src/templates/comic/publisher_detail.html +++ b/kontor-api/src/templates/comic/publisher_detail.html @@ -20,6 +20,24 @@ Publisher Name {{publisher.name}} + {% if publisher.parent_publisher_id %} + + Parent Company + {{publisher.parent_publisher.name}} + + {% endif %} + {% if publisher.imprints|length > 0 %} + + Imprints + + + + + {% endif %} Comics diff --git a/kontor-api/src/templates/comic/worktype_detail.html b/kontor-api/src/templates/comic/worktype_detail.html index ac734a3..1c17823 100644 --- a/kontor-api/src/templates/comic/worktype_detail.html +++ b/kontor-api/src/templates/comic/worktype_detail.html @@ -30,6 +30,10 @@ {% endfor %} + + ID + {{worktype.id}} + Data Created {{worktype.created_date}} @@ -45,5 +49,23 @@ +
+
+ Back to list + Edit + Delete +
{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/kontor-api/src/templates/comic/add_worktype.html b/kontor-api/src/templates/comic/worktype_edit.html similarity index 100% rename from kontor-api/src/templates/comic/add_worktype.html rename to kontor-api/src/templates/comic/worktype_edit.html diff --git a/kontor-api/src/templates/comic/worktypes.html b/kontor-api/src/templates/comic/worktypes.html index 19a43fb..ee5ea6a 100644 --- a/kontor-api/src/templates/comic/worktypes.html +++ b/kontor-api/src/templates/comic/worktypes.html @@ -16,11 +16,13 @@ {% for worktype in worktypes %} - {{worktype.name}} + {{worktype.name}} + Edit + Delete {% endfor %} - Add WorkType + Add WorkType {% endblock %} diff --git a/kontor-api/src/templates/components/artist_cards.html b/kontor-api/src/templates/components/artist_cards.html index 53192af..f0f5584 100644 --- a/kontor-api/src/templates/components/artist_cards.html +++ b/kontor-api/src/templates/components/artist_cards.html @@ -1,6 +1,7 @@
{{obj.name}}
+

Link : {{obj.weblink}}

Read more
diff --git a/kontor-api/src/templates/components/navbar.html b/kontor-api/src/templates/components/navbar.html index f4cb733..ad9cc99 100644 --- a/kontor-api/src/templates/components/navbar.html +++ b/kontor-api/src/templates/components/navbar.html @@ -16,7 +16,7 @@
  • Artists
  • Publishers
  • -
  • WorkTypes
  • +
  • WorkTypes