""" database/models.py SQLAlchemy ORM 模型,与 db.md / init.sql 对应。 """ from datetime import datetime from typing import Optional from sqlalchemy import Boolean, DateTime, Float, ForeignKey, Integer, JSON, String, Text, UniqueConstraint from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship class Base(DeclarativeBase): pass class Project(Base): """项目表(统一:知识库 + 撰写)""" __tablename__ = "projects" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) uuid: Mapped[str] = mapped_column( String(32), unique=True, nullable=False, ) name: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) doc_count: Mapped[int] = mapped_column(Integer, default=0) eval_reports_count: Mapped[int] = mapped_column(Integer, default=0) total_size: Mapped[str] = mapped_column(String(32), default="0 B") tags: Mapped[Optional[str]] = mapped_column(Text, nullable=True) status: Mapped[str] = mapped_column(String(16), default="active") color: Mapped[str] = mapped_column(String(16), default="#3b82f6") sync_suppressed_table_names: Mapped[Optional[str]] = mapped_column(Text, nullable=True) kb_documents: Mapped[list["KbDocument"]] = relationship( "KbDocument", back_populates="project", cascade="all, delete-orphan" ) kb_directories: Mapped[list["KbDirectory"]] = relationship( "KbDirectory", back_populates="project", cascade="all, delete-orphan" ) write_documents: Mapped[list["WriteDocumentModel"]] = relationship( "WriteDocumentModel", back_populates="project", cascade="all, delete-orphan" ) class WriteDocumentModel(Base): """撰写文档表(后评价报告)。project_id 关联 projects.uuid""" __tablename__ = "write_documents" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) title: Mapped[str] = mapped_column(String(255), nullable=False) content: Mapped[Optional[str]] = mapped_column(Text, nullable=True) word_count: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) status: Mapped[str] = mapped_column(String(16), default="draft") sort_order: Mapped[int] = mapped_column(Integer, default=0) project: Mapped["Project"] = relationship("Project", back_populates="write_documents") doc_versions: Mapped[list["DocumentVersion"]] = relationship( "DocumentVersion", back_populates="document", cascade="all, delete-orphan" ) class DocumentVersion(Base): """撰写文档版本表(对应 doc_versions)。""" __tablename__ = "doc_versions" id: Mapped[str] = mapped_column(String(64), primary_key=True) document_id: Mapped[str] = mapped_column( ForeignKey("write_documents.id", ondelete="CASCADE"), nullable=False ) version: Mapped[str] = mapped_column(String(32), nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False) citation_payload: Mapped[Optional[str]] = mapped_column(Text, nullable=True) saved_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) author: Mapped[str] = mapped_column(String(64), nullable=False) note: Mapped[Optional[str]] = mapped_column(Text, nullable=True) document: Mapped["WriteDocumentModel"] = relationship("WriteDocumentModel", back_populates="doc_versions") class KbDocument(Base): """知识库文档表。project_id 关联 projects.uuid。status: 0=失败 2=排队中 3=处理中 4=可用""" __tablename__ = "kb_documents" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) directory_id: Mapped[Optional[str]] = mapped_column( ForeignKey("kb_directories.id", ondelete="SET NULL"), nullable=True ) name: Mapped[str] = mapped_column(String(255), nullable=False) upload_filename: Mapped[Optional[str]] = mapped_column( String(255), nullable=True ) # 上传/解压时的原始文件名(含扩展名),与智能展示名 name 区分 size: Mapped[str] = mapped_column(String(32), nullable=False) file_path: Mapped[Optional[str]] = mapped_column(String(512), nullable=True) # 仅目录路径,不含文件名 storage_rel_path: Mapped[Optional[str]] = mapped_column( String(512), nullable=True ) # 项目内完整相对路径(含文件名),用于精确定位磁盘文件 word_count: Mapped[int] = mapped_column(Integer, default=0) uploaded_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) status: Mapped[int] = mapped_column(Integer, default=2) # 0=失败 2=排队中 3=处理中 4=可用 error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True) category: Mapped[Optional[str]] = mapped_column(String(32), nullable=True, default=None) project: Mapped["Project"] = relationship("Project", back_populates="kb_documents") directory: Mapped[Optional["KbDirectory"]] = relationship("KbDirectory", back_populates="documents") class KbDirectory(Base): """知识库目录表。project_id 关联 projects.uuid;parent_id 形成目录树。""" __tablename__ = "kb_directories" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) parent_id: Mapped[Optional[str]] = mapped_column( ForeignKey("kb_directories.id", ondelete="CASCADE"), nullable=True ) name: Mapped[str] = mapped_column(String(255), nullable=False) full_path: Mapped[str] = mapped_column(String(1024), nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) project: Mapped["Project"] = relationship("Project", back_populates="kb_directories") documents: Mapped[list["KbDocument"]] = relationship("KbDocument", back_populates="directory") class Task(Base): """独立后台任务表:pdf2md 转换和 element-agent 要素抽取。""" __tablename__ = "tasks" id: Mapped[str] = mapped_column(String(64), primary_key=True) project: Mapped[str] = mapped_column(String(64), nullable=False) task_type: Mapped[int] = mapped_column(Integer, nullable=False) file_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) file_path: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True) status: Mapped[int] = mapped_column(Integer, nullable=False, default=1) payload_json: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) result_path: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True) error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True) add_time: Mapped[datetime] = mapped_column(DateTime, nullable=False) finish_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) class ElementTable(Base): __tablename__ = "element_tables" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) table_type: Mapped[str] = mapped_column(String(32), nullable=False) # global/time table_name: Mapped[str] = mapped_column(String(255), nullable=False) year: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) is_time_dimension: Mapped[bool] = mapped_column(Boolean, default=False) sort_order: Mapped[int] = mapped_column(Integer, default=0) # JSON 数组字符串,row_key 列表;sync 模版时跳过为这些行补格子,避免用户删行后一同步又出现 sync_suppressed_row_keys: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # JSON 数组:界面行键展示顺序(含用户加行) custom_row_order: Mapped[Optional[str]] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ElementCell(Base): __tablename__ = "element_cells" id: Mapped[str] = mapped_column(String(64), primary_key=True) table_id: Mapped[str] = mapped_column(ForeignKey("element_tables.id", ondelete="CASCADE"), nullable=False) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) row_key: Mapped[str] = mapped_column(String(255), nullable=False) col_key: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) year: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) source_document_id: Mapped[Optional[str]] = mapped_column( ForeignKey("kb_documents.id", ondelete="SET NULL"), nullable=True ) source_line_no: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) source_line_end: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) source_quote: Mapped[Optional[str]] = mapped_column(Text, nullable=True) confidence: Mapped[Optional[float]] = mapped_column(Float, nullable=True) extraction_batch_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) extraction_model: Mapped[Optional[str]] = mapped_column(String(128), nullable=True) source_type: Mapped[Optional[str]] = mapped_column(String(16), nullable=True) # extract | manual conflict_status: Mapped[str] = mapped_column(String(16), default="none") created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ExtractionResult(Base): __tablename__ = "extraction_results" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) document_id: Mapped[str] = mapped_column(ForeignKey("kb_documents.id", ondelete="CASCADE"), nullable=False) batch_id: Mapped[str] = mapped_column(String(64), nullable=False) result_type: Mapped[str] = mapped_column(String(16), nullable=False) # table/element table_type: Mapped[Optional[str]] = mapped_column(String(32), nullable=True) table_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) year: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) item_key: Mapped[str] = mapped_column(String(255), nullable=False) item_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) source_line_no: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) source_line_end: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) confidence: Mapped[Optional[float]] = mapped_column(Float, nullable=True) raw_payload: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) extracted_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) # 抽取业务时间(旧库迁移前可为空) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ElementExtractionResult(Base): """ 要素抽取结果明细表(面向“细则章节/小节提示词 -> 项目材料”抽取)。 字段对齐(用户侧语义): - 表类型 -> table_type - 年份 -> year - 表名称 -> table_name - 时间 -> extracted_at - 键 -> item_key - 值 -> item_value - 来源文档ID -> source_document_id - 来源行数 -> source_line_no / source_line_end """ __tablename__ = "element_extraction_results" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) table_type: Mapped[str] = mapped_column(String(32), nullable=False) year: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) table_name: Mapped[str] = mapped_column(String(255), nullable=False) extracted_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) item_key: Mapped[str] = mapped_column(String(255), nullable=False) item_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) source_document_id: Mapped[Optional[str]] = mapped_column( ForeignKey("kb_documents.id", ondelete="SET NULL"), nullable=True ) source_line_no: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) source_line_end: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ElementConflict(Base): __tablename__ = "element_conflicts" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) table_id: Mapped[Optional[str]] = mapped_column(ForeignKey("element_tables.id", ondelete="SET NULL"), nullable=True) cell_id: Mapped[Optional[str]] = mapped_column(ForeignKey("element_cells.id", ondelete="SET NULL"), nullable=True) item_key: Mapped[str] = mapped_column(String(255), nullable=False) old_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) new_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) selected_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) source_document_id: Mapped[Optional[str]] = mapped_column( ForeignKey("kb_documents.id", ondelete="SET NULL"), nullable=True ) source_line_no: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) status: Mapped[str] = mapped_column(String(16), default="pending") created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class DocumentMarkdown(Base): __tablename__ = "document_markdowns" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) document_id: Mapped[str] = mapped_column(ForeignKey("kb_documents.id", ondelete="CASCADE"), nullable=False) extracted_filename: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) markdown_content: Mapped[str] = mapped_column(Text, nullable=False) content_hash: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class DocumentChunk(Base): __tablename__ = "document_chunks" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) document_id: Mapped[str] = mapped_column(ForeignKey("kb_documents.id", ondelete="CASCADE"), nullable=False) markdown_id: Mapped[Optional[str]] = mapped_column(ForeignKey("document_markdowns.id", ondelete="CASCADE"), nullable=True) heading: Mapped[Optional[str]] = mapped_column(String(512), nullable=True) chunk_text: Mapped[str] = mapped_column(Text, nullable=False) chunk_index: Mapped[int] = mapped_column(Integer, default=0) source_line_start: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) source_line_end: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) vector_id: Mapped[Optional[str]] = mapped_column(String(128), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ReportTemplate(Base): __tablename__ = "report_templates" id: Mapped[str] = mapped_column(String(64), primary_key=True) name: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) is_default: Mapped[bool] = mapped_column(Boolean, default=False) is_active: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ReportTemplateSection(Base): __tablename__ = "report_template_sections" id: Mapped[str] = mapped_column(String(64), primary_key=True) template_id: Mapped[str] = mapped_column(ForeignKey("report_templates.id", ondelete="CASCADE"), nullable=False) section_key: Mapped[str] = mapped_column(String(64), nullable=False) section_title: Mapped[str] = mapped_column(String(255), nullable=False) section_prompt: Mapped[Optional[str]] = mapped_column(Text, nullable=True) section_output_contract: Mapped[Optional[str]] = mapped_column(Text, nullable=True) section_order: Mapped[int] = mapped_column(Integer, default=0) examples: Mapped[Optional[str]] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ReportGenerationJob(Base): __tablename__ = "report_generation_jobs" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) template_id: Mapped[Optional[str]] = mapped_column( ForeignKey("report_templates.id", ondelete="SET NULL"), nullable=True ) status: Mapped[str] = mapped_column(String(16), default="pending") # pending/running/completed/failed progress: Mapped[int] = mapped_column(Integer, default=0) current_section_key: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True) requested_by: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) options: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) snapshot: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) class ReportGenerationChapter(Base): __tablename__ = "report_generation_chapters" id: Mapped[str] = mapped_column(String(64), primary_key=True) job_id: Mapped[str] = mapped_column( ForeignKey("report_generation_jobs.id", ondelete="CASCADE"), nullable=False ) section_key: Mapped[str] = mapped_column(String(64), nullable=False) section_title: Mapped[str] = mapped_column(String(255), nullable=False) section_order: Mapped[int] = mapped_column(Integer, default=0) status: Mapped[str] = mapped_column(String(16), default="pending") # pending/running/completed/failed content: Mapped[Optional[str]] = mapped_column(Text, nullable=True) prompt_text: Mapped[Optional[str]] = mapped_column(Text, nullable=True) evidence_payload: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) validation_payload: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) class ReportSectionReference(Base): """章节参考范文(独立于模板配置,用于报告生成时拼入 prompt)""" __tablename__ = "report_section_references" id: Mapped[str] = mapped_column(String(64), primary_key=True) template_id: Mapped[Optional[str]] = mapped_column( ForeignKey("report_templates.id", ondelete="CASCADE"), nullable=True ) source_file: Mapped[str] = mapped_column(String(255), nullable=False) section_key: Mapped[str] = mapped_column(String(64), nullable=False) section_title: Mapped[str] = mapped_column(String(255), nullable=False) section_order: Mapped[int] = mapped_column(Integer, default=0) content: Mapped[str] = mapped_column(Text, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class Department(Base): __tablename__ = "department" id: Mapped[str] = mapped_column(String(64), primary_key=True) name: Mapped[str] = mapped_column(String(255), nullable=False) parent_id: Mapped[Optional[str]] = mapped_column(ForeignKey("departments.id", ondelete="SET NULL"), nullable=True) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class User(Base): __tablename__ = "users" id: Mapped[str] = mapped_column(String(64), primary_key=True) username: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) password_hash: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) department_id: Mapped[Optional[str]] = mapped_column(ForeignKey("departments.id", ondelete="SET NULL"), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class Role(Base): __tablename__ = "roles" id: Mapped[str] = mapped_column(String(64), primary_key=True) name: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class Permission(Base): __tablename__ = "permissions" id: Mapped[str] = mapped_column(String(64), primary_key=True) perm_key: Mapped[str] = mapped_column(String(128), nullable=False, unique=True) perm_type: Mapped[str] = mapped_column(String(32), nullable=False) # menu/project description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class RolePermission(Base): __tablename__ = "role_permissions" id: Mapped[str] = mapped_column(String(64), primary_key=True) role_id: Mapped[str] = mapped_column(ForeignKey("roles.id", ondelete="CASCADE"), nullable=False) permission_id: Mapped[str] = mapped_column(ForeignKey("permissions.id", ondelete="CASCADE"), nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class UserRole(Base): __tablename__ = "user_roles" id: Mapped[str] = mapped_column(String(64), primary_key=True) user_id: Mapped[str] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False) role_id: Mapped[str] = mapped_column(ForeignKey("roles.id", ondelete="CASCADE"), nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ProjectMember(Base): __tablename__ = "project_members" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) user_id: Mapped[str] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False) role: Mapped[str] = mapped_column(String(32), default="editor") created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class ProjectDepartment(Base): """项目可见部门:绑定后,仅这些部门下的用户可访问(另有管理员与 project_members 例外)。""" __tablename__ = "project_departments" __table_args__ = (UniqueConstraint("project_id", "department_id", name="uq_project_department"),) id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) department_id: Mapped[str] = mapped_column(ForeignKey("departments.id", ondelete="CASCADE"), nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) class FillRecord(Base): """回填记录:每次要素回填均留痕,支持证据追溯。""" __tablename__ = "fill_records" id: Mapped[str] = mapped_column(String(64), primary_key=True) project_id: Mapped[str] = mapped_column(ForeignKey("projects.uuid", ondelete="CASCADE"), nullable=False) cell_id: Mapped[Optional[str]] = mapped_column( ForeignKey("element_cells.id", ondelete="SET NULL"), nullable=True ) table_id: Mapped[Optional[str]] = mapped_column( ForeignKey("element_tables.id", ondelete="SET NULL"), nullable=True ) row_key: Mapped[str] = mapped_column(String(255), nullable=False) col_key: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) year: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) filled_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) previous_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) source_document_id: Mapped[Optional[str]] = mapped_column( ForeignKey("kb_documents.id", ondelete="SET NULL"), nullable=True ) source_document_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) source_line_no: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) source_line_end: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) source_quote: Mapped[Optional[str]] = mapped_column(Text, nullable=True) confidence: Mapped[Optional[float]] = mapped_column(Float, nullable=True) extraction_batch_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) extraction_model: Mapped[Optional[str]] = mapped_column(String(128), nullable=True) fill_type: Mapped[str] = mapped_column(String(16), nullable=False, default="auto") created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)