179 lines
8.6 KiB
Python
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,
|
|
)
|