参与者管理- PD模块内容补充
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import re
|
||||
import uuid
|
||||
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.subject_pd import SubjectPd
|
||||
from app.schemas.subject_pd import SubjectPdCreate, SubjectPdUpdate
|
||||
|
||||
|
||||
_PD_NO_PATTERN = re.compile(r"PD(\d+)$", re.IGNORECASE)
|
||||
|
||||
|
||||
def _extract_pd_sequence(pd_no: str | None) -> int:
|
||||
if not pd_no:
|
||||
return 0
|
||||
matched = _PD_NO_PATTERN.search(str(pd_no).strip())
|
||||
if not matched:
|
||||
return 0
|
||||
try:
|
||||
return int(matched.group(1))
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
|
||||
async def _next_pd_no(db: AsyncSession, study_id: uuid.UUID) -> str:
|
||||
rows = await db.execute(select(SubjectPd.pd_no).where(SubjectPd.study_id == study_id))
|
||||
max_seq = 0
|
||||
for (pd_no,) in rows.all():
|
||||
max_seq = max(max_seq, _extract_pd_sequence(pd_no))
|
||||
return f"PD{max_seq + 1:04d}"
|
||||
|
||||
|
||||
async def list_subject_pds(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
subject_id: uuid.UUID,
|
||||
*,
|
||||
skip: int = 0,
|
||||
limit: int = 200,
|
||||
) -> Sequence[SubjectPd]:
|
||||
stmt = (
|
||||
select(SubjectPd)
|
||||
.where(SubjectPd.study_id == study_id, SubjectPd.subject_id == subject_id)
|
||||
.order_by(SubjectPd.pd_no.asc(), SubjectPd.created_at.asc())
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
rows = await db.execute(stmt)
|
||||
return rows.scalars().all()
|
||||
|
||||
|
||||
async def list_study_subject_pds(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
*,
|
||||
skip: int = 0,
|
||||
limit: int = 500,
|
||||
site_ids: set[uuid.UUID] | None = None,
|
||||
) -> list[tuple[SubjectPd, str, uuid.UUID]]:
|
||||
if site_ids is not None and not site_ids:
|
||||
return []
|
||||
stmt = (
|
||||
select(SubjectPd, Subject.subject_no, Subject.site_id)
|
||||
.join(Subject, Subject.id == SubjectPd.subject_id)
|
||||
.where(
|
||||
SubjectPd.study_id == study_id,
|
||||
Subject.study_id == study_id,
|
||||
)
|
||||
.order_by(SubjectPd.pd_no.asc(), SubjectPd.created_at.asc())
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
if site_ids is not None:
|
||||
stmt = stmt.where(Subject.site_id.in_(site_ids))
|
||||
rows = await db.execute(stmt)
|
||||
return [(row[0], row[1], row[2]) for row in rows.all()]
|
||||
|
||||
|
||||
async def get_subject_pd(db: AsyncSession, pd_id: uuid.UUID) -> SubjectPd | None:
|
||||
row = await db.execute(select(SubjectPd).where(SubjectPd.id == pd_id))
|
||||
return row.scalar_one_or_none()
|
||||
|
||||
|
||||
async def create_subject_pd(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
pd_in: SubjectPdCreate,
|
||||
*,
|
||||
created_by: uuid.UUID | None = None,
|
||||
) -> SubjectPd:
|
||||
payload = pd_in.model_dump(exclude_unset=True)
|
||||
# PD编号统一由后端按项目全局顺序生成,避免被外部覆盖。
|
||||
payload.pop("pd_no", None)
|
||||
pd_no = await _next_pd_no(db, study_id)
|
||||
item = SubjectPd(study_id=study_id, pd_no=pd_no, created_by=created_by, **payload)
|
||||
db.add(item)
|
||||
await db.commit()
|
||||
await db.refresh(item)
|
||||
return item
|
||||
|
||||
|
||||
async def update_subject_pd(db: AsyncSession, item: SubjectPd, pd_in: SubjectPdUpdate) -> SubjectPd:
|
||||
update_data = pd_in.model_dump(exclude_unset=True)
|
||||
if update_data:
|
||||
await db.execute(
|
||||
sa_update(SubjectPd)
|
||||
.where(SubjectPd.id == item.id)
|
||||
.values(**update_data)
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(item)
|
||||
return item
|
||||
|
||||
|
||||
async def delete_subject_pd(db: AsyncSession, item: SubjectPd) -> None:
|
||||
await db.delete(item)
|
||||
await db.commit()
|
||||
Reference in New Issue
Block a user