Files
tps-dashboard/backend/app/models/episode.py
T
simonkoson d91c20a126 看板升级: GT v0.5.0(20期叙事结构全覆盖) + schema加字段方案 + git清理
- ground-truth v0.5.0: 补完10期narrative_structure标注(MiMo跑批+制片人审定)
- summarize.py: 修复无标注期次格式化崩溃
- 003_add_ai_labels.sql: episodes表加7列(6打标+1置信度)迁移文件
- episode.py: Model同步加7个Optional字段
- CLAUDE.md: 项目主控文件入库
- doco/deliverables: 20期终版文稿md入库
- doco/programs: 中间产物从git移除(本地保留), .gitignore统一管理
- note/寄存条: 两个子项目寄存条入库
- .gitignore: 补充doco中间产物/Excel锁文件/临时文件规则

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-26 09:14:29 +08:00

79 lines
3.3 KiB
Python

"""
节目期次模型 — SQLModel
业务规则(.clinerules 5.3 软引用 + 快照模式):
- editor_id 可空(离职编导)
- editor_name_snapshot 必填(历史导入也走快照)
- 查询历史作品:先 editor_id,回退 editor_name_snapshot 文本匹配
"""
from datetime import date, datetime
from typing import Any
from sqlalchemy import Column
from sqlalchemy import DateTime as SADateTime
from sqlalchemy import Text
from sqlalchemy import Boolean as SABoolean
from sqlalchemy import Integer as SAInteger
from sqlalchemy import Numeric as SANumeric
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.sql import func as sa_func
from sqlmodel import Field, SQLModel
class Episode(SQLModel, table=True):
__tablename__ = "episodes"
id: int | None = Field(default=None, primary_key=True)
episode_number: int = Field(unique=True, index=True)
program_name: str = Field(max_length=200)
air_date: date
# 编导(软引用 + 快照双字段)
# 在职编导:editor_id 和 editor_name_snapshot 都填
# 离职编导:editor_id 留空,editor_name_snapshot 填姓名
editor_id: int | None = Field(default=None, foreign_key="users.id")
editor_name_snapshot: str = Field(max_length=100)
# 收视数据
# 注意:.clinerules 5.1 颜色只对 audience_share 上色
# audience_rating 只展示数字,不参与颜色判定
audience_share: float | None = Field(default=None)
audience_rating: float | None = Field(default=None)
# 重播相关
is_rerun: bool = Field(default=False)
original_episode_id: int | None = Field(default=None, foreign_key="episodes.id")
is_time_sensitive: bool = Field(default=False)
# 外拍信息
is_outdoor_shoot: bool = Field(default=False)
outdoor_contact_person: str | None = Field(default=None, max_length=100)
outdoor_contact_info: str | None = Field(default=None, max_length=200)
# 线索
lead_source: str | None = Field(default=None, max_length=300)
# 责编备注
notes: str | None = Field(default=None)
# ── AI 打标字段(看板升级 L2→L3)──
# 全部可空:旧数据不填,AI 打标 + 制片人审核后写入
# 枚举值在应用层校验,不加数据库 CHECK 约束
program_format: str | None = Field(default=None, max_length=20) # 主题材(单选)
equipment_domain: Any | None = Field(default=None, sa_column=Column(JSONB)) # 装备域(多选)
scene_tags: Any | None = Field(default=None, sa_column=Column(JSONB)) # 场景标签(多选)
tech_tags: Any | None = Field(default=None, sa_column=Column(JSONB)) # 技术线标签(多选)
narrative_structure: str | None = Field(default=None, max_length=20) # 叙事结构(单选)
opening_hook: str | None = Field(default=None, max_length=10) # 开篇钩子(单选)
ai_label_confidence: str | None = Field(default=None, max_length=10) # draft/reviewed/inferred
created_at: datetime | None = Field(
default=None,
sa_column=Column(SADateTime(timezone=True), nullable=False, server_default=sa_func.now()),
)
updated_at: datetime | None = Field(
default=None,
sa_column=Column(SADateTime(timezone=True), nullable=False, server_default=sa_func.now()),
)