import uuid from datetime import datetime from typing import Dict, List, Optional from natsort import natsorted 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): __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): return f'Publisher({self.id} {self.name})' def __str__(self): return self.__repr__() class Comic(Base, BaseMixin): __tablename__ = 'comic' title = Column(String, unique=True) publisher_id = Column(String, ForeignKey('publisher.id'), nullable=False) 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") volumes = relationship("Volume") comic_works = relationship("ComicWork") def __repr__(self): return f'Comic({self.id} {self.version} {self.title} {self.publisher.name})' def __str__(self): return f'{self.title}({self.id})' def get_artists(self) -> Dict[str, List[str]]: works: Dict[str, List[str]] = {} for work in self.comic_works: work_type = work.work_type.name artist = work.artist if work_type in works: works[work_type].append(artist) else: works[work_type] = [artist] return works def sorted_issues(self): sorted_issues = natsorted(self.issues, key=lambda x: getattr(x, 'issue_number')) return sorted_issues class Volume(Base, BaseMixin): __tablename__ = "volume" name = Column(String, nullable=False) comic_id = Column(String, ForeignKey("comic.id"), nullable=False) comic = relationship("Comic", back_populates="volumes") issues = relationship("Issue") class TradePaperback(Base, BaseMixin): __tablename__ = "trade_paperback" name = Column(String, nullable=False) issue_start = Column(Integer) issue_end = Column(Integer) comic_id = Column(String, ForeignKey("comic.id"), nullable=False) comic = relationship("Comic", back_populates="trade_paperbacks") class StoryArc(Base, BaseMixin): __tablename__ = "story_arc" name = Column(String, nullable=False) comic_id = Column(String, ForeignKey("comic.id"), nullable=False) comic = relationship("Comic", back_populates="story_arcs") class Issue(Base, BaseMixin): __tablename__ = "issue" issue_number = Column(String) in_stock = Column(Boolean) is_read = Column(Boolean) comic_id = Column(String, ForeignKey("comic.id"), nullable=False) comic = relationship("Comic", back_populates="issues") volume_id = Column(String, ForeignKey("volume.id"), nullable=True) volume = relationship("Volume", back_populates="issues") 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]]: works: Dict[str, List[str]] = {} for work in self.comic_works: work_type = work.work_type.name comic = work.comic if work_type in works: works[work_type].append(comic) else: works[work_type] = [comic] return works class WorkType(Base, BaseMixin): __tablename__ = "worktype" name = Column(String, nullable=False, unique=True) comic_works = relationship("ComicWork") def get_artists(self) -> Dict[str, List[str]]: works: Dict[str, List[str]] = {} for work in self.comic_works: comic = work.comic.title artist = work.artist if comic in works: works[comic].append(artist) else: works[comic] = [artist] return works def __repr__(self): return f'Worktype({self.id} {self.version} {self.name} {len(self.comic_works)})' def __str__(self): return f'{self.name}({self.id})' class ComicWork(Base, BaseMixin): __tablename__ = "comic_work" comic_id = Column(String, ForeignKey("comic.id"), nullable=False) comic = relationship("Comic", back_populates="comic_works") artist_id = Column(String, ForeignKey("artist.id"), nullable=False) artist = relationship("Artist", back_populates="comic_works") work_type_id = Column(String, ForeignKey("worktype.id"), nullable=False) work_type = relationship("WorkType", back_populates="comic_works")