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:
@@ -0,0 +1,59 @@
|
||||
"""add template fields to monitoring_visit_issues
|
||||
|
||||
Revision ID: 20260509_01
|
||||
Revises: 20260508_03
|
||||
Create Date: 2026-05-09 13:45:00.000000
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "20260509_01"
|
||||
down_revision: Union[str, None] = "20260508_03"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
columns = {col["name"] for col in inspector.get_columns("monitoring_visit_issues")}
|
||||
|
||||
if "severity" not in columns:
|
||||
op.add_column("monitoring_visit_issues", sa.Column("severity", sa.String(length=64), nullable=True))
|
||||
if "mark" not in columns:
|
||||
op.add_column("monitoring_visit_issues", sa.Column("mark", sa.String(length=100), nullable=True))
|
||||
if "visit_cycle" not in columns:
|
||||
op.add_column("monitoring_visit_issues", sa.Column("visit_cycle", sa.String(length=100), nullable=True))
|
||||
if "center_query" not in columns:
|
||||
op.add_column("monitoring_visit_issues", sa.Column("center_query", sa.Text(), nullable=True))
|
||||
if "center_latest_reply" not in columns:
|
||||
op.add_column("monitoring_visit_issues", sa.Column("center_latest_reply", sa.Text(), nullable=True))
|
||||
if "rectification_completed" not in columns:
|
||||
op.add_column(
|
||||
"monitoring_visit_issues",
|
||||
sa.Column("rectification_completed", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
columns = {col["name"] for col in inspector.get_columns("monitoring_visit_issues")}
|
||||
|
||||
if "rectification_completed" in columns:
|
||||
op.drop_column("monitoring_visit_issues", "rectification_completed")
|
||||
if "center_latest_reply" in columns:
|
||||
op.drop_column("monitoring_visit_issues", "center_latest_reply")
|
||||
if "center_query" in columns:
|
||||
op.drop_column("monitoring_visit_issues", "center_query")
|
||||
if "visit_cycle" in columns:
|
||||
op.drop_column("monitoring_visit_issues", "visit_cycle")
|
||||
if "mark" in columns:
|
||||
op.drop_column("monitoring_visit_issues", "mark")
|
||||
if "severity" in columns:
|
||||
op.drop_column("monitoring_visit_issues", "severity")
|
||||
@@ -0,0 +1,55 @@
|
||||
"""add site_id to monitoring_visit_issues
|
||||
|
||||
Revision ID: 20260509_02
|
||||
Revises: 20260509_01
|
||||
Create Date: 2026-05-09 14:30:00.000000
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "20260509_02"
|
||||
down_revision: Union[str, None] = "20260509_01"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
columns = {col["name"] for col in inspector.get_columns("monitoring_visit_issues")}
|
||||
fks = {fk["name"] for fk in inspector.get_foreign_keys("monitoring_visit_issues")}
|
||||
indexes = {idx["name"] for idx in inspector.get_indexes("monitoring_visit_issues")}
|
||||
|
||||
if "site_id" not in columns:
|
||||
op.add_column("monitoring_visit_issues", sa.Column("site_id", postgresql.UUID(as_uuid=True), nullable=True))
|
||||
if "fk_monitoring_visit_issues_site_id" not in fks:
|
||||
op.create_foreign_key(
|
||||
"fk_monitoring_visit_issues_site_id",
|
||||
"monitoring_visit_issues",
|
||||
"sites",
|
||||
["site_id"],
|
||||
["id"],
|
||||
)
|
||||
if "ix_monitoring_visit_issues_site_id" not in indexes:
|
||||
op.create_index("ix_monitoring_visit_issues_site_id", "monitoring_visit_issues", ["site_id"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
columns = {col["name"] for col in inspector.get_columns("monitoring_visit_issues")}
|
||||
fks = {fk["name"] for fk in inspector.get_foreign_keys("monitoring_visit_issues")}
|
||||
indexes = {idx["name"] for idx in inspector.get_indexes("monitoring_visit_issues")}
|
||||
|
||||
if "ix_monitoring_visit_issues_site_id" in indexes:
|
||||
op.drop_index("ix_monitoring_visit_issues_site_id", table_name="monitoring_visit_issues")
|
||||
if "fk_monitoring_visit_issues_site_id" in fks:
|
||||
op.drop_constraint("fk_monitoring_visit_issues_site_id", "monitoring_visit_issues", type_="foreignkey")
|
||||
if "site_id" in columns:
|
||||
op.drop_column("monitoring_visit_issues", "site_id")
|
||||
@@ -0,0 +1,34 @@
|
||||
"""add subject actual medication count
|
||||
|
||||
Revision ID: 20260509_03
|
||||
Revises: 20260509_02
|
||||
Create Date: 2026-05-09 14:58:00.000000
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = "20260509_03"
|
||||
down_revision: Union[str, None] = "20260509_02"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
columns = {col["name"] for col in inspector.get_columns("subjects")}
|
||||
if "actual_medication_count" not in columns:
|
||||
op.add_column("subjects", sa.Column("actual_medication_count", sa.Integer(), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
columns = {col["name"] for col in inspector.get_columns("subjects")}
|
||||
if "actual_medication_count" in columns:
|
||||
op.drop_column("subjects", "actual_medication_count")
|
||||
Reference in New Issue
Block a user