Files

179 lines
8.6 KiB
Python

import uuid
from datetime import UTC, datetime
from sqlalchemy import CheckConstraint, Float, ForeignKey, Index, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db import Base
def new_uuid() -> str:
return str(uuid.uuid4())
def utc_now() -> datetime:
return datetime.now(UTC)
class Task(Base):
__tablename__ = "tasks"
__table_args__ = (
CheckConstraint("status IN ('running', 'success', 'failed')", name="ck_tasks_status"),
CheckConstraint(
"analysis_status IN ('normal', 'insufficient')",
name="ck_tasks_analysis_status",
),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
platform: Mapped[str] = mapped_column(String(32), nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False, default="running")
analysis_status: Mapped[str] = mapped_column(String(16), nullable=False, default="normal")
analysis_success_rate: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
hotspot_limit: Mapped[int] = mapped_column(Integer, nullable=False, default=5)
item_limit_per_hotspot: Mapped[int] = mapped_column(Integer, nullable=False, default=5)
comment_limit_per_item: Mapped[int] = mapped_column(Integer, nullable=False, default=50)
total_items_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
processed_items_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
successful_items_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
failed_items_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
current_stage: Mapped[str | None] = mapped_column(String(64))
last_progress_at: Mapped[datetime | None] = mapped_column()
error_stage: Mapped[str | None] = mapped_column(String(64))
error_type: Mapped[str | None] = mapped_column(String(64))
error_message: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(nullable=False, default=utc_now)
started_at: Mapped[datetime | None] = mapped_column()
finished_at: Mapped[datetime | None] = mapped_column()
hotspots: Mapped[list["Hotspot"]] = relationship(back_populates="task")
content_items: Mapped[list["ContentItem"]] = relationship(back_populates="task")
comments: Mapped[list["Comment"]] = relationship(back_populates="task")
reports: Mapped[list["Report"]] = relationship(back_populates="task")
class Hotspot(Base):
__tablename__ = "hotspots"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
task_id: Mapped[str] = mapped_column(ForeignKey("tasks.id"), nullable=False)
platform: Mapped[str] = mapped_column(String(32), nullable=False)
source_hot_id: Mapped[str | None] = mapped_column(String(128))
rank: Mapped[int | None] = mapped_column(Integer)
title: Mapped[str] = mapped_column(Text, nullable=False)
heat_value: Mapped[str | None] = mapped_column(String(128))
raw_data: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
created_at: Mapped[datetime] = mapped_column(nullable=False, default=utc_now)
task: Mapped[Task] = relationship(back_populates="hotspots")
content_items: Mapped[list["ContentItem"]] = relationship(back_populates="hotspot")
comments: Mapped[list["Comment"]] = relationship(back_populates="hotspot")
reports: Mapped[list["Report"]] = relationship(back_populates="hotspot")
class ContentItem(Base):
__tablename__ = "content_items"
__table_args__ = (
Index("ix_content_items_task_hotspot", "task_id", "hotspot_id"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
task_id: Mapped[str] = mapped_column(ForeignKey("tasks.id"), nullable=False)
hotspot_id: Mapped[str] = mapped_column(ForeignKey("hotspots.id"), nullable=False)
platform: Mapped[str] = mapped_column(String(32), nullable=False)
source_item_id: Mapped[str] = mapped_column(String(128), nullable=False)
item_type: Mapped[str] = mapped_column(String(32), nullable=False)
title: Mapped[str | None] = mapped_column(Text)
summary: Mapped[str | None] = mapped_column(Text)
url: Mapped[str | None] = mapped_column(Text)
status: Mapped[str] = mapped_column(String(32), nullable=False, default="pending")
error_stage: Mapped[str | None] = mapped_column(String(64))
error_type: Mapped[str | None] = mapped_column(String(64))
error_message: Mapped[str | None] = mapped_column(Text)
raw_data: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
created_at: Mapped[datetime] = mapped_column(nullable=False, default=utc_now)
task: Mapped[Task] = relationship(back_populates="content_items")
hotspot: Mapped[Hotspot] = relationship(back_populates="content_items")
comments: Mapped[list["Comment"]] = relationship(back_populates="content_item")
reports: Mapped[list["Report"]] = relationship(back_populates="content_item")
class Comment(Base):
__tablename__ = "comments"
__table_args__ = (
Index("ix_comments_task_content_item", "task_id", "content_item_id"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
task_id: Mapped[str] = mapped_column(ForeignKey("tasks.id"), nullable=False)
hotspot_id: Mapped[str] = mapped_column(ForeignKey("hotspots.id"), nullable=False)
content_item_id: Mapped[str] = mapped_column(ForeignKey("content_items.id"), nullable=False)
platform: Mapped[str] = mapped_column(String(32), nullable=False)
source_comment_id: Mapped[str | None] = mapped_column(String(128))
content: Mapped[str] = mapped_column(Text, nullable=False)
author: Mapped[str | None] = mapped_column(Text)
like_count: Mapped[int | None] = mapped_column(Integer)
comment_time: Mapped[datetime | None] = mapped_column()
sentiment: Mapped[str] = mapped_column(String(32), nullable=False, default="unknown")
labels: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
reason: Mapped[str | None] = mapped_column(Text)
ai_analysis_status: Mapped[str] = mapped_column(String(32), nullable=False, default="skipped")
raw_data: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
ai_raw_response: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(nullable=False, default=utc_now)
task: Mapped[Task] = relationship(back_populates="comments")
hotspot: Mapped[Hotspot] = relationship(back_populates="comments")
content_item: Mapped[ContentItem] = relationship(back_populates="comments")
class Report(Base):
__tablename__ = "reports"
__table_args__ = (
Index("ix_reports_task_report_type", "task_id", "report_type"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
task_id: Mapped[str] = mapped_column(ForeignKey("tasks.id"), nullable=False)
hotspot_id: Mapped[str | None] = mapped_column(ForeignKey("hotspots.id"))
content_item_id: Mapped[str | None] = mapped_column(ForeignKey("content_items.id"))
report_type: Mapped[str] = mapped_column(String(32), nullable=False)
title: Mapped[str] = mapped_column(Text, nullable=False)
data: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
markdown: Mapped[str] = mapped_column(Text, nullable=False, default="")
metrics_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
typical_comments_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
summary: Mapped[str] = mapped_column(Text, nullable=False, default="")
markdown_content: Mapped[str] = mapped_column(Text, nullable=False, default="")
created_at: Mapped[datetime] = mapped_column(nullable=False, default=utc_now)
task: Mapped[Task] = relationship(back_populates="reports")
hotspot: Mapped[Hotspot | None] = relationship(back_populates="reports")
content_item: Mapped[ContentItem | None] = relationship(back_populates="reports")
def create_report_record(
*,
task_id: str,
report_type: str,
title: str,
hotspot_id: str | None = None,
content_item_id: str | None = None,
data: str = "{}",
markdown: str = "",
) -> Report:
if report_type == "hotspot" and not hotspot_id:
raise ValueError("hotspot report requires hotspot_id")
if report_type == "content_item" and not content_item_id:
raise ValueError("content item report requires content_item_id")
return Report(
task_id=task_id,
report_type=report_type,
title=title,
hotspot_id=hotspot_id,
content_item_id=content_item_id,
data=data,
markdown=markdown,
)