feat: refine subject visits and project workflows

Add early termination visit workflow with ordering, non-applicable visit handling, visit window display, and medication adherence support.

Extend monitoring visit issue template fields, site scoping, setup draft project info handling, login security UI, attachment behavior, and related tests/migrations.
This commit is contained in:
Cheng Zhou
2026-05-09 17:10:34 +08:00
parent 74feca4467
commit 917ab7ccf1
41 changed files with 3463 additions and 701 deletions
+8 -1
View File
@@ -1,7 +1,7 @@
import uuid
from datetime import date, datetime
from sqlalchemy import Date, DateTime, ForeignKey, String, Text, UniqueConstraint, func
from sqlalchemy import Boolean, Date, DateTime, ForeignKey, String, Text, UniqueConstraint, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
@@ -14,8 +14,12 @@ class MonitoringVisitIssue(Base):
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
study_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("studies.id"), index=True, nullable=False)
site_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("sites.id"), index=True, nullable=True)
issue_no: Mapped[str] = mapped_column(String(64), nullable=False)
category: Mapped[str] = mapped_column(String(255), nullable=False)
severity: Mapped[str | None] = mapped_column(String(64), nullable=True)
mark: Mapped[str | None] = mapped_column(String(100), nullable=True)
visit_cycle: Mapped[str | None] = mapped_column(String(100), nullable=True)
subject_code: Mapped[str | None] = mapped_column(String(64), nullable=True)
monitor_item: Mapped[str | None] = mapped_column(Text, nullable=True)
monitor_type: Mapped[str | None] = mapped_column(String(64), nullable=True)
@@ -27,6 +31,9 @@ class MonitoringVisitIssue(Base):
description: Mapped[str | None] = mapped_column(Text, nullable=True)
action_taken: Mapped[str | None] = mapped_column(Text, nullable=True)
follow_up_progress: Mapped[str | None] = mapped_column(Text, nullable=True)
center_query: Mapped[str | None] = mapped_column(Text, nullable=True)
center_latest_reply: Mapped[str | None] = mapped_column(Text, nullable=True)
rectification_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false", default=False)
found_date: Mapped[date | None] = mapped_column(Date, nullable=True)
due_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
actual_resolve_date: Mapped[date | None] = mapped_column(Date, nullable=True)