Arag / app /models /book.py
AuthorBot
Add Platform Presence Scanner for URL import distribution scoring.
c5d0d2d
Raw
History Blame Contribute Delete
3.73 kB
"""Author RAG Chatbot SaaS — Book Model."""
from __future__ import annotations
from typing import TYPE_CHECKING
from sqlalchemy import Boolean, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base, TimestampMixin, generate_uuid
if TYPE_CHECKING:
from app.models.user import User
from app.models.document import Document
from app.models.link import Link
from app.models.platform_listing import BookPlatformListing
class Book(Base, TimestampMixin):
"""A book in an author's catalog."""
__tablename__ = "books"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=generate_uuid)
author_id: Mapped[str] = mapped_column(
String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
)
# Metadata
title: Mapped[str] = mapped_column(String(500), nullable=False)
tagline: Mapped[str | None] = mapped_column(String(500), nullable=True)
genre: Mapped[str | None] = mapped_column(String(300), nullable=True) # widened for long category strings
description: Mapped[str | None] = mapped_column(Text, nullable=True)
cover_path: Mapped[str | None] = mapped_column(String(500), nullable=True) # local file path
cover_url: Mapped[str | None] = mapped_column(String(1000), nullable=True) # remote CDN URL (scraped)
cover_thumbnail_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
cover_medium_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
ai_summary: Mapped[str | None] = mapped_column(Text, nullable=True)
publish_year: Mapped[str | None] = mapped_column(String(4), nullable=True)
# Processing
status: Mapped[str] = mapped_column(String(50), default="created", nullable=False, index=True)
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
chroma_collection_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
chunk_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
page_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
# Display
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
display_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
# Smart links
buy_url: Mapped[str | None] = mapped_column(String(1000), nullable=True)
preview_url: Mapped[str | None] = mapped_column(String(1000), nullable=True)
# Import tracking
source_url: Mapped[str | None] = mapped_column(String(1000), nullable=True)
isbn: Mapped[str | None] = mapped_column(String(20), nullable=True)
book_author: Mapped[str | None] = mapped_column(String(200), nullable=True)
about_author: Mapped[str | None] = mapped_column(Text, nullable=True)
language: Mapped[str | None] = mapped_column(String(50), nullable=True)
rating: Mapped[str | None] = mapped_column(String(10), nullable=True)
rating_count: Mapped[str | None] = mapped_column(String(20), nullable=True)
# Relationships
author: Mapped[User] = relationship("User", back_populates="books")
documents: Mapped[list[Document]] = relationship("Document", back_populates="book", cascade="all, delete-orphan")
links: Mapped[list[Link]] = relationship("Link", back_populates="book", cascade="all, delete-orphan")
platform_listings: Mapped[list["BookPlatformListing"]] = relationship(
"BookPlatformListing", back_populates="book", cascade="all, delete-orphan"
)
def __repr__(self) -> str:
return f"<Book id={self.id} title={self.title!r}>"