Files
zkxw/backend/app/crud/research_donation_sample_collection.py
T
2026-07-01 16:37:33 +08:00

261 lines
8.8 KiB
Python

"""科研捐赠样本采集信息列表 CRUD(样本信息管理 · 样本采集信息)"""
from __future__ import annotations
import math
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy import and_, or_
from sqlalchemy.orm import Session
from app.core.utils import format_date
from app.utils.sample_special import sample_special_to_dict
from app.models.models import (
BizCellSampleType,
BizCollectionHospital,
BizCollectionRegion,
)
from app.models.research_donation import (
BizResearchDonation,
BizResearchDonationCollection,
BizResearchDonationParty,
BizResearchDonationSample,
)
def total_pages(total: int, page_size: int) -> int:
if total <= 0:
return 0
return int(math.ceil(total / float(page_size)))
def _fmt_dt(t: Optional[datetime]) -> str:
return t.strftime("%Y-%m-%d %H:%M:%S") if t else ""
def _sample_status_label(status: str) -> str:
return {
"ycj": "已采集",
"jcz": "检测中",
"yrk": "已入库",
"yzf": "已作废",
}.get(status, "未采集" if not status else status)
def _row_to_dict(
*,
donation_sample_id: str,
research_donation_id: str,
donation_no: str,
donation_status: str,
expected_delivery_date: str,
mother_name: str,
newborn_name: str,
sample_code: str,
sample_type_name: str,
sample_status: str,
sample_status_label: str,
region_name: str,
hospital_name: str,
medical_staff: str,
sample_collect_time: str,
maternal_blood_collect_time: str,
) -> Dict[str, Any]:
return {
"donationSampleId": donation_sample_id,
"researchDonationId": research_donation_id,
"donationNo": donation_no,
"donationStatus": donation_status,
"expectedDeliveryDate": expected_delivery_date,
"motherName": mother_name,
"newbornName": newborn_name,
"sampleCode": sample_code,
"sampleTypeName": sample_type_name,
"sampleStatus": sample_status,
"sampleStatusLabel": sample_status_label,
"regionName": region_name,
"hospitalName": hospital_name,
"medicalStaff": medical_staff,
"sampleCollectTime": sample_collect_time,
"maternalBloodCollectTime": maternal_blood_collect_time,
}
def _base_query(db: Session, tenant_no: str):
return (
db.query(
BizResearchDonationSample,
BizResearchDonation,
BizResearchDonationParty,
BizResearchDonationCollection,
)
.join(
BizResearchDonation,
and_(
BizResearchDonation.id == BizResearchDonationSample.research_donation_id,
BizResearchDonation.del_flag == 0,
),
)
.outerjoin(
BizResearchDonationParty,
and_(
BizResearchDonationParty.research_donation_id == BizResearchDonation.id,
BizResearchDonationParty.del_flag == 0,
),
)
.outerjoin(
BizResearchDonationCollection,
and_(
BizResearchDonationCollection.research_donation_id == BizResearchDonation.id,
BizResearchDonationCollection.del_flag == 0,
),
)
.filter(
BizResearchDonationSample.tenant_no == tenant_no,
BizResearchDonationSample.del_flag == 0,
)
)
def _apply_sample_status_filter(q, sample_status: Optional[str]):
st = (sample_status or "").strip()
if not st or st == "all":
return q
if st == "uncollected":
return q.filter(
or_(
BizResearchDonationSample.sample_status.is_(None),
BizResearchDonationSample.sample_status == "",
)
)
return q.filter(BizResearchDonationSample.sample_status == st)
def list_donation_sample_collections(
db: Session,
*,
page: int = 1,
page_size: int = 10,
tenant_no: str,
keyword: Optional[str] = None,
sample_status: Optional[str] = None,
user_info=None,
) -> Tuple[List[Dict[str, Any]], int]:
if not tenant_no:
return [], 0
q = _base_query(db, tenant_no)
q = _apply_sample_status_filter(q, sample_status)
if keyword and keyword.strip():
kw = f"%{keyword.strip()}%"
kw_conds = [
BizResearchDonationParty.mother_name.like(kw),
BizResearchDonationSample.sample_code.like(kw),
]
from app.crud import sample_reception as reception_svc
lab_codes = reception_svc.sample_codes_matching_lab_storage_keyword(
db, tenant_no, keyword, reception_source="research_donation"
)
if lab_codes:
kw_conds.append(BizResearchDonationSample.sample_code.in_(lab_codes))
q = q.filter(or_(*kw_conds))
if user_info:
from app.core.data_scope import apply_data_scope
q = apply_data_scope(q, BizResearchDonation, user_info, db, tenant_no)
total = q.count()
page = max(1, page)
page_size = min(max(1, page_size), 100)
rows = (
q.order_by(
BizResearchDonationParty.expected_delivery_date.is_(None),
BizResearchDonationParty.expected_delivery_date.desc(),
BizResearchDonation.create_time.desc(),
BizResearchDonationSample.id.desc(),
)
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
sample_type_ids = list({
ds.sample_type_id for ds, _, _, _ in rows if ds and ds.sample_type_id
})
type_map: Dict[str, str] = {}
if sample_type_ids:
for t in (
db.query(BizCellSampleType)
.filter(BizCellSampleType.id.in_(sample_type_ids), BizCellSampleType.del_flag == 0)
.all()
):
type_map[t.id] = t.type_name or ""
region_ids = list({
(ci.region_id or "").strip()
for _, _, _, ci in rows
if ci and (ci.region_id or "").strip()
})
hospital_ids = list({
(ci.hospital_id or "").strip()
for _, _, _, ci in rows
if ci and (ci.hospital_id or "").strip()
})
region_name_map: Dict[str, str] = {}
hospital_name_map: Dict[str, str] = {}
if region_ids:
for r in (
db.query(BizCollectionRegion.id, BizCollectionRegion.region_name)
.filter(BizCollectionRegion.id.in_(region_ids), BizCollectionRegion.del_flag == 0)
.all()
):
region_name_map[r.id] = r.region_name or ""
if hospital_ids:
for h in (
db.query(BizCollectionHospital.id, BizCollectionHospital.hospital_name)
.filter(BizCollectionHospital.id.in_(hospital_ids), BizCollectionHospital.del_flag == 0)
.all()
):
hospital_name_map[h.id] = h.hospital_name or ""
items: List[Dict[str, Any]] = []
for dsample, donation, party, ci in rows:
status = (dsample.sample_status or "").strip() if dsample else ""
region_id = (ci.region_id or "").strip() if ci else ""
hospital_id = (ci.hospital_id or "").strip() if ci else ""
item = _row_to_dict(
donation_sample_id=dsample.id,
research_donation_id=donation.id if donation else "",
donation_no=(donation.donation_no or "") if donation else "",
donation_status=(donation.donation_status or "") if donation else "",
expected_delivery_date=format_date(party.expected_delivery_date) if party else "",
mother_name=(party.mother_name or "") if party else "",
newborn_name=(party.newborn_name or "") if party else "",
sample_code=dsample.sample_code or "",
sample_type_name=type_map.get(dsample.sample_type_id or "", ""),
sample_status=status,
sample_status_label=_sample_status_label(status),
region_name=region_name_map.get(region_id, ""),
hospital_name=hospital_name_map.get(hospital_id, ""),
medical_staff=(ci.medical_staff or "") if ci else "",
sample_collect_time=_fmt_dt(ci.sample_collect_time) if ci else "",
maternal_blood_collect_time=_fmt_dt(ci.maternal_blood_collect_time) if ci else "",
)
item.update(sample_special_to_dict(dsample))
items.append(item)
if items and tenant_no:
from app.crud import sample_reception as reception_svc
codes = [i["sampleCode"] for i in items if i.get("sampleCode")]
code_map = reception_svc.get_lab_storage_code_map_by_sample_codes(
db, tenant_no, codes, reception_source="research_donation"
)
for item in items:
sc = (item.get("sampleCode") or "").strip()
item["labStorageCode"] = code_map.get(sc, "") if sc else ""
return items, total