Step 8:数据管理(Queries + SDV/SDR 进度)
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
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
|
||||
@@ -0,0 +1,108 @@
|
||||
import uuid
|
||||
from datetime import date
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import select, update as sa_update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.subject import Subject
|
||||
from app.models.verification import VerificationProgress
|
||||
from app.schemas.verification import VerificationCreate, VerificationUpdate
|
||||
|
||||
|
||||
async def _validate_site_subject(db: AsyncSession, study_id: uuid.UUID, site_id: uuid.UUID, subject_id: uuid.UUID):
|
||||
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")
|
||||
if subj.site_id != site_id:
|
||||
raise ValueError("Site does not match subject")
|
||||
|
||||
|
||||
async def upsert_progress(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
data: VerificationCreate,
|
||||
) -> VerificationProgress:
|
||||
await _validate_site_subject(db, study_id, data.site_id, data.subject_id)
|
||||
|
||||
result = await db.execute(
|
||||
select(VerificationProgress).where(
|
||||
VerificationProgress.study_id == study_id,
|
||||
VerificationProgress.subject_id == data.subject_id,
|
||||
VerificationProgress.level == data.level,
|
||||
)
|
||||
)
|
||||
existing = result.scalar_one_or_none()
|
||||
if existing:
|
||||
update_data = data.model_dump()
|
||||
await db.execute(
|
||||
sa_update(VerificationProgress)
|
||||
.where(VerificationProgress.id == existing.id)
|
||||
.values(**update_data)
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(existing)
|
||||
return existing
|
||||
|
||||
vp = VerificationProgress(
|
||||
study_id=study_id,
|
||||
site_id=data.site_id,
|
||||
subject_id=data.subject_id,
|
||||
level=data.level,
|
||||
percent=data.percent,
|
||||
last_verified_at=data.last_verified_at,
|
||||
verifier_id=data.verifier_id,
|
||||
notes=data.notes,
|
||||
)
|
||||
db.add(vp)
|
||||
await db.commit()
|
||||
await db.refresh(vp)
|
||||
return vp
|
||||
|
||||
|
||||
async def get_progress(db: AsyncSession, study_id: uuid.UUID, subject_id: uuid.UUID, level: str) -> VerificationProgress | None:
|
||||
result = await db.execute(
|
||||
select(VerificationProgress).where(
|
||||
VerificationProgress.study_id == study_id,
|
||||
VerificationProgress.subject_id == subject_id,
|
||||
VerificationProgress.level == level,
|
||||
)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_by_id(db: AsyncSession, verification_id: uuid.UUID) -> VerificationProgress | None:
|
||||
result = await db.execute(select(VerificationProgress).where(VerificationProgress.id == verification_id))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_progress(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
site_id: uuid.UUID | None = None,
|
||||
subject_id: uuid.UUID | None = None,
|
||||
level: str | None = None,
|
||||
) -> Sequence[VerificationProgress]:
|
||||
stmt = select(VerificationProgress).where(VerificationProgress.study_id == study_id)
|
||||
if site_id:
|
||||
stmt = stmt.where(VerificationProgress.site_id == site_id)
|
||||
if subject_id:
|
||||
stmt = stmt.where(VerificationProgress.subject_id == subject_id)
|
||||
if level:
|
||||
stmt = stmt.where(VerificationProgress.level == level)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
async def update_progress(db: AsyncSession, vp: VerificationProgress, data: VerificationUpdate) -> VerificationProgress:
|
||||
update_data = data.model_dump(exclude_unset=True)
|
||||
if update_data:
|
||||
await db.execute(
|
||||
sa_update(VerificationProgress)
|
||||
.where(VerificationProgress.id == vp.id)
|
||||
.values(**update_data)
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(vp)
|
||||
return vp
|
||||
Reference in New Issue
Block a user