Files
ctms/backend/app/crud/data_query.py
T
2025-12-16 19:16:50 +08:00

118 lines
4.0 KiB
Python

import uuid
from datetime import date, datetime, timezone
from typing import Sequence
from sqlalchemy import select, update as sa_update
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.data_query import DataQuery
from app.models.site import Site
from app.models.subject import Subject
from app.schemas.data_query import DataQueryCreate, DataQueryUpdate
async def _validate_site_subject(db: AsyncSession, study_id: uuid.UUID, site_id: uuid.UUID | None, subject_id: uuid.UUID | None):
if site_id:
result = await db.execute(select(Site).where(Site.id == site_id))
site = result.scalar_one_or_none()
if not site or site.study_id != study_id:
raise ValueError("Site not found in study")
subject_site = None
if subject_id:
result = await db.execute(select(Subject).where(Subject.id == subject_id))
subj = result.scalar_one_or_none()
if not subj or subj.study_id != study_id:
raise ValueError("Subject not found in study")
subject_site = subj.site_id
if site_id and subject_site and site_id != subject_site:
raise ValueError("Site and subject mismatch")
async def create_query(
db: AsyncSession,
study_id: uuid.UUID,
query_in: DataQueryCreate,
*,
created_by: uuid.UUID,
) -> DataQuery:
await _validate_site_subject(db, study_id, query_in.site_id, query_in.subject_id)
dq = DataQuery(
study_id=study_id,
site_id=query_in.site_id,
subject_id=query_in.subject_id,
visit_id=query_in.visit_id,
title=query_in.title,
description=query_in.description,
category=query_in.category,
priority=query_in.priority,
assigned_to=query_in.assigned_to,
due_date=query_in.due_date,
status="OPEN",
resolution=None,
closed_at=None,
created_by=created_by,
)
db.add(dq)
await db.commit()
await db.refresh(dq)
return dq
async def get_query(db: AsyncSession, query_id: uuid.UUID) -> DataQuery | None:
result = await db.execute(select(DataQuery).where(DataQuery.id == query_id))
return result.scalar_one_or_none()
async def list_queries(
db: AsyncSession,
study_id: uuid.UUID,
*,
status: str | None = None,
site_id: uuid.UUID | None = None,
subject_id: uuid.UUID | None = None,
assigned_to: uuid.UUID | None = None,
overdue: bool | None = None,
category: str | None = None,
priority: str | None = None,
skip: int = 0,
limit: int = 100,
) -> Sequence[DataQuery]:
stmt = select(DataQuery).where(DataQuery.study_id == study_id)
if status:
stmt = stmt.where(DataQuery.status == status)
if site_id:
stmt = stmt.where(DataQuery.site_id == site_id)
if subject_id:
stmt = stmt.where(DataQuery.subject_id == subject_id)
if assigned_to:
stmt = stmt.where(DataQuery.assigned_to == assigned_to)
if category:
stmt = stmt.where(DataQuery.category == category)
if priority:
stmt = stmt.where(DataQuery.priority == priority)
if overdue is True:
stmt = stmt.where(DataQuery.due_date < date.today(), DataQuery.status != "CLOSED")
if overdue is False:
stmt = stmt.where((DataQuery.due_date >= date.today()) | (DataQuery.due_date.is_(None)) | (DataQuery.status == "CLOSED"))
stmt = stmt.offset(skip).limit(limit)
result = await db.execute(stmt)
return result.scalars().all()
async def update_query(db: AsyncSession, dq: DataQuery, dq_in: DataQueryUpdate) -> DataQuery:
update_data = dq_in.model_dump(exclude_unset=True)
if "status" in update_data:
if update_data["status"] == "CLOSED":
update_data["closed_at"] = datetime.now(timezone.utc)
else:
update_data["closed_at"] = None
if update_data:
await db.execute(
sa_update(DataQuery)
.where(DataQuery.id == dq.id)
.values(**update_data)
)
await db.commit()
await db.refresh(dq)
return dq