feat: add DisclosureRequest model and migration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
CCS Admin 2026-02-26 16:02:52 +00:00
parent 00bc92322f
commit bb13ec80a2
3 changed files with 120 additions and 2 deletions

View file

@ -0,0 +1,72 @@
"""add disclosure_requests table
Revision ID: 005_disclosure
Revises: 5717043d0f9d
Create Date: 2026-02-26 14:00:00.000000
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "005_disclosure"
down_revision: Union[str, None] = "5717043d0f9d"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.execute(
"""
CREATE TABLE disclosure_requests (
id INT AUTO_INCREMENT PRIMARY KEY,
case_id INT NOT NULL,
requester_id INT NOT NULL,
reason VARCHAR(500) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
reviewed_by INT NULL,
reviewed_at DATETIME NULL,
expires_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_dr_case FOREIGN KEY (case_id) REFERENCES cases(id),
CONSTRAINT fk_dr_requester FOREIGN KEY (requester_id) REFERENCES users(id),
CONSTRAINT fk_dr_reviewer FOREIGN KEY (reviewed_by) REFERENCES users(id),
CONSTRAINT chk_dr_status CHECK (status IN ('pending', 'approved', 'rejected')),
INDEX idx_dr_case_status (case_id, status),
INDEX idx_dr_requester (requester_id),
INDEX idx_dr_status (status)
)
"""
)
# Update Notification CHECK constraint to include disclosure types
op.execute("ALTER TABLE notifications DROP CONSTRAINT chk_notif")
op.execute(
"""
ALTER TABLE notifications ADD CONSTRAINT chk_notif CHECK (
notification_type IN (
'new_cases_uploaded','icd_entered','icd_uploaded',
'report_ready','coding_completed','disclosure_request','disclosure_resolved'
)
)
"""
)
def downgrade() -> None:
# Restore original Notification CHECK constraint
op.execute("ALTER TABLE notifications DROP CONSTRAINT chk_notif")
op.execute(
"""
ALTER TABLE notifications ADD CONSTRAINT chk_notif CHECK (
notification_type IN (
'new_cases_uploaded','icd_entered','icd_uploaded',
'report_ready','coding_completed'
)
)
"""
)
op.execute("DROP TABLE disclosure_requests")

View file

@ -3,7 +3,7 @@
from app.models.user import AllowedDomain, InvitationLink, RefreshToken, User from app.models.user import AllowedDomain, InvitationLink, RefreshToken, User
from app.models.case import Case, CaseICDCode from app.models.case import Case, CaseICDCode
from app.models.report import WeeklyReport, YearlySummary from app.models.report import WeeklyReport, YearlySummary
from app.models.audit import AuditLog, ImportLog, Notification from app.models.audit import AuditLog, DisclosureRequest, ImportLog, Notification
__all__ = [ __all__ = [
# User & Auth # User & Auth
@ -21,4 +21,6 @@ __all__ = [
"ImportLog", "ImportLog",
"AuditLog", "AuditLog",
"Notification", "Notification",
# Disclosure / Data Access
"DisclosureRequest",
] ]

View file

@ -124,7 +124,51 @@ class Notification(Base):
CheckConstraint( CheckConstraint(
"notification_type IN (" "notification_type IN ("
"'new_cases_uploaded','icd_entered','icd_uploaded'," "'new_cases_uploaded','icd_entered','icd_uploaded',"
"'report_ready','coding_completed')", "'report_ready','coding_completed',"
"'disclosure_request','disclosure_resolved')",
name="chk_notif", name="chk_notif",
), ),
) )
class DisclosureRequest(Base):
__tablename__ = "disclosure_requests"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
case_id: Mapped[int] = mapped_column(
Integer, ForeignKey("cases.id"), nullable=False
)
requester_id: Mapped[int] = mapped_column(
Integer, ForeignKey("users.id"), nullable=False
)
reason: Mapped[str] = mapped_column(String(500), nullable=False)
status: Mapped[str] = mapped_column(
String(20), nullable=False, server_default="pending"
)
reviewed_by: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("users.id"), nullable=True
)
reviewed_at: Mapped[Optional[dt.datetime]] = mapped_column(
DateTime, nullable=True
)
expires_at: Mapped[Optional[dt.datetime]] = mapped_column(
DateTime, nullable=True
)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime, nullable=False, server_default=func.now()
)
# Relationships
case: Mapped["Case"] = relationship(foreign_keys=[case_id])
requester: Mapped["User"] = relationship(foreign_keys=[requester_id])
reviewer: Mapped[Optional["User"]] = relationship(foreign_keys=[reviewed_by])
__table_args__ = (
CheckConstraint(
"status IN ('pending','approved','rejected')",
name="chk_dr_status",
),
Index("idx_dr_case_status", "case_id", "status"),
Index("idx_dr_requester", "requester_id"),
Index("idx_dr_status", "status"),
)