feat(questionnaire): integrate revised intake and follow-up monitoring
CI / ruff · pytest (pull_request) Successful in 55s

Add gated TSQ, Russian C-SSRS Recent, durable responses and notifications, diaries, schedules and audited clinical review. Preserve manual assessment when the Russian follow-up source is unavailable.

Refs #52
This commit is contained in:
Pavel Eliseev
2026-10-03 18:19:34 +03:00
parent 8b61eecf1b
commit c42ef621e5
34 changed files with 2556 additions and 85 deletions
+1
View File
@@ -4,6 +4,7 @@ The only module (besides registration code) that touches both schemas — and on
"""
from app.db import audit as _audit # noqa: F401 (registers audit_log in clinical metadata)
from app.db import intake as _intake # noqa: F401
from app.db.clinical import ClinicalBase
from app.db.identity import IdentityBase
from app.db.session import clinical_engine, identity_engine
+55
View File
@@ -0,0 +1,55 @@
"""Versioned questionnaire records, without modifying historical scale results."""
from datetime import date, datetime
from typing import Any
from sqlalchemy import Date, ForeignKey, Integer, String, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from app.db.clinical import BigId, ClinicalBase
from app.db.types import JSONType, TZDateTime
class QuestionnaireRecord(ClinicalBase):
__tablename__ = "questionnaire_records"
id: Mapped[str] = mapped_column(String(32), primary_key=True)
token: Mapped[str] = mapped_column(String(32), index=True)
kind: Mapped[str] = mapped_column(String(32))
version: Mapped[str] = mapped_column(String(64))
policy_version: Mapped[str] = mapped_column(String(64))
started_at: Mapped[datetime] = mapped_column(TZDateTime)
completed_at: Mapped[datetime | None] = mapped_column(TZDateTime)
day: Mapped[date] = mapped_column(Date)
status: Mapped[str] = mapped_column(String(32))
answers: Mapped[dict[str, Any]] = mapped_column(JSONType, default=dict)
# Per-field time, recall period, source and derived values; totals may be null.
details: Mapped[dict[str, Any]] = mapped_column(JSONType, default=dict)
class QuestionnaireNotice(ClinicalBase):
__tablename__ = "questionnaire_notices"
__table_args__ = (UniqueConstraint("token", "event_key"),)
id: Mapped[int] = mapped_column(BigId, primary_key=True, autoincrement=True)
token: Mapped[str] = mapped_column(String(32), index=True)
event_key: Mapped[str] = mapped_column(String(128))
alert_id: Mapped[int] = mapped_column(ForeignKey("alerts.id"))
created_at: Mapped[datetime] = mapped_column(TZDateTime)
attempted_at: Mapped[datetime | None] = mapped_column(TZDateTime)
accepted_at: Mapped[datetime | None] = mapped_column(TZDateTime)
attempts: Mapped[int] = mapped_column(Integer, default=0)
status: Mapped[str] = mapped_column(String(32), default="pending")
error_code: Mapped[str | None] = mapped_column(String(80))
class QuestionnaireTask(ClinicalBase):
__tablename__ = "questionnaire_tasks"
__table_args__ = (UniqueConstraint("token", "task_key"),)
id: Mapped[int] = mapped_column(BigId, primary_key=True, autoincrement=True)
token: Mapped[str] = mapped_column(String(32), index=True)
task_key: Mapped[str] = mapped_column(String(128))
kind: Mapped[str] = mapped_column(String(32))
due_at: Mapped[datetime] = mapped_column(TZDateTime)
status: Mapped[str] = mapped_column(String(32), default="pending")
record_id: Mapped[str | None] = mapped_column(String(32))
delivery: Mapped[list] = mapped_column(JSONType, default=list)
sent_at: Mapped[datetime | None] = mapped_column(TZDateTime)