Files
fastapi/app/models/training_record.py
T

78 lines
5.5 KiB
Python
Raw Normal View History

from __future__ import annotations
from datetime import datetime
from decimal import Decimal
2026-06-03 15:51:46 +08:00
from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer, JSON, Numeric, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db.base import Base
from app.models.mixins import TimestampMixin
BIGINT_PK = BigInteger().with_variant(Integer, "sqlite")
class TrainingRecord(TimestampMixin, Base):
"""训练记录表:完整完成问诊、诊断、治疗和评价后写入长期记录。"""
__tablename__ = "training_record"
id: Mapped[int] = mapped_column(BIGINT_PK, primary_key=True, autoincrement=True, comment="训练记录ID")
training_mode: Mapped[str] = mapped_column(String(50), nullable=False, index=True, comment="训练模式")
case_type: Mapped[str] = mapped_column(String(30), nullable=False, index=True, comment="病例/训练类型")
start_time: Mapped[datetime] = mapped_column(DateTime, nullable=False, comment="训练开始时间")
end_time: Mapped[datetime | None] = mapped_column(DateTime, comment="训练结束时间")
duration_seconds: Mapped[int | None] = mapped_column(Integer, comment="训练持续秒数")
total_score: Mapped[Decimal | None] = mapped_column(Numeric(5, 2), comment="总分")
ai_score: Mapped[Decimal | None] = mapped_column(Numeric(5, 2), comment="AI评分")
teacher_score: Mapped[Decimal | None] = mapped_column(Numeric(5, 2), comment="教师评分")
evaluation_level: Mapped[str] = mapped_column(String(20), nullable=False, default="", comment="评价等级")
status: Mapped[str] = mapped_column(String(30), nullable=False, index=True, comment="记录状态")
feedback: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="总体反馈")
thinking_chain: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="诊断循证与评分依据摘要")
diagnosis_path: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="诊断路径摘要")
wrong_points: Mapped[list] = mapped_column(JSON, nullable=False, default=list, comment="错误点/扣分点")
missed_questions: Mapped[list] = mapped_column(JSON, nullable=False, default=list, comment="遗漏问题")
recommendation_result: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict, comment="改进建议和导出结果")
ai_feedback_structured: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict, comment="AI结构化评价")
osce_station_score: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict, comment="OSCE站点评分")
interruption_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="中断次数")
emotion_analysis: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict, comment="情绪分析")
prompt_version: Mapped[str] = mapped_column(String(50), nullable=False, default="v1", comment="提示词版本")
rag_context_version: Mapped[str] = mapped_column(String(50), nullable=False, default="none", comment="RAG上下文版本")
case_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True, comment="病例ID")
teacher_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, comment="教师ID")
2026-06-03 15:51:46 +08:00
user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, comment="Django用户中心数字ID")
external_user_id: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="Django用户中心ID")
session_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, comment="训练会话ID")
evaluation_record_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, comment="兼容旧评价记录ID")
score_type: Mapped[str] = mapped_column(String(20), nullable=False, default="percentage", comment="分数类型")
pdf_file_path: Mapped[str | None] = mapped_column(String(512), comment="PDF报告路径")
__table_args__ = (
UniqueConstraint("session_id", name="uk_training_record_session"),
{"comment": "训练记录表"},
)
2026-06-03 15:51:46 +08:00
score_details = relationship("TrainingScoreDetail", back_populates="record", cascade="all, delete-orphan")
class TrainingScoreDetail(TimestampMixin, Base):
"""评分明细表:保存每条 scoring_rule 对应的 AI 评分、扣分原因、证据和置信度。"""
__tablename__ = "training_score_detail"
id: Mapped[int] = mapped_column(BIGINT_PK, primary_key=True, autoincrement=True, comment="评分明细ID")
record_id: Mapped[int] = mapped_column(ForeignKey("training_record.id"), nullable=False, index=True, comment="训练记录ID")
rule_id: Mapped[int | None] = mapped_column(ForeignKey("scoring_rule.id"), nullable=True, index=True, comment="评分规则ID")
dimension: Mapped[str] = mapped_column(String(50), nullable=False, index=True, comment="评分维度")
score: Mapped[Decimal] = mapped_column(Numeric(5, 2), nullable=False, default=0, comment="分数")
deducted_reason: Mapped[str | None] = mapped_column(Text, comment="扣分原因")
evidence_message_ids: Mapped[list] = mapped_column(JSON, nullable=False, default=list, comment="对应对话证据")
ai_confidence: Mapped[Decimal | None] = mapped_column(Numeric(5, 2), comment="AI评分置信度")
comment: Mapped[str | None] = mapped_column(Text, comment="评语")
record = relationship("TrainingRecord", back_populates="score_details")
__table_args__ = {"comment": "评分明细表"}