protocol_sample_collection.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. """采集协议样本采集信息列表 CRUD(样本信息管理 · 样本采集信息 Tab)"""
  2. from __future__ import annotations
  3. import math
  4. from datetime import datetime
  5. from typing import Any, Dict, List, Optional, Tuple
  6. from sqlalchemy import and_, or_
  7. from sqlalchemy.orm import Session
  8. from app.core.utils import format_date
  9. from app.utils.sample_special import sample_special_to_dict
  10. from app.models.models import (
  11. BizCellSampleType,
  12. BizCollectionHospital,
  13. BizCollectionProtocol,
  14. BizCollectionRegion,
  15. BizProtocolCollectionInfo,
  16. BizProtocolPartyCustomer,
  17. BizProtocolSample,
  18. )
  19. def total_pages(total: int, page_size: int) -> int:
  20. if total <= 0:
  21. return 0
  22. return int(math.ceil(total / float(page_size)))
  23. def _fmt_dt(t: Optional[datetime]) -> str:
  24. return t.strftime("%Y-%m-%d %H:%M:%S") if t else ""
  25. def _sample_status_label(status: str) -> str:
  26. return {
  27. "ycj": "已采集",
  28. "jcz": "检测中",
  29. "yrk": "已入库",
  30. "yzf": "已作废",
  31. }.get(status, "未采集" if not status else status)
  32. def _row_to_dict(
  33. *,
  34. protocol_sample_id: str,
  35. collection_protocol_id: str,
  36. protocol_no: str,
  37. protocol_status: str,
  38. expected_delivery_date: str,
  39. mother_name: str,
  40. newborn_name: str,
  41. sample_code: str,
  42. sample_type_name: str,
  43. sample_status: str,
  44. sample_status_label: str,
  45. region_name: str,
  46. hospital_name: str,
  47. medical_staff: str,
  48. sample_collect_time: str,
  49. maternal_blood_collect_time: str,
  50. ) -> Dict[str, Any]:
  51. return {
  52. "protocolSampleId": protocol_sample_id,
  53. "collectionProtocolId": collection_protocol_id,
  54. "protocolNo": protocol_no,
  55. "protocolStatus": protocol_status,
  56. "expectedDeliveryDate": expected_delivery_date,
  57. "motherName": mother_name,
  58. "newbornName": newborn_name,
  59. "sampleCode": sample_code,
  60. "sampleTypeName": sample_type_name,
  61. "sampleStatus": sample_status,
  62. "sampleStatusLabel": sample_status_label,
  63. "regionName": region_name,
  64. "hospitalName": hospital_name,
  65. "medicalStaff": medical_staff,
  66. "sampleCollectTime": sample_collect_time,
  67. "maternalBloodCollectTime": maternal_blood_collect_time,
  68. }
  69. def _base_query(db: Session, tenant_no: str):
  70. return (
  71. db.query(
  72. BizProtocolSample,
  73. BizCollectionProtocol,
  74. BizProtocolPartyCustomer,
  75. BizProtocolCollectionInfo,
  76. )
  77. .join(
  78. BizCollectionProtocol,
  79. and_(
  80. BizCollectionProtocol.id == BizProtocolSample.collection_protocol_id,
  81. BizCollectionProtocol.del_flag == 0,
  82. ),
  83. )
  84. .outerjoin(
  85. BizProtocolPartyCustomer,
  86. and_(
  87. BizProtocolPartyCustomer.collection_protocol_id == BizCollectionProtocol.id,
  88. BizProtocolPartyCustomer.del_flag == 0,
  89. ),
  90. )
  91. .outerjoin(
  92. BizProtocolCollectionInfo,
  93. and_(
  94. BizProtocolCollectionInfo.collection_protocol_id == BizCollectionProtocol.id,
  95. BizProtocolCollectionInfo.del_flag == 0,
  96. ),
  97. )
  98. .filter(
  99. BizProtocolSample.tenant_no == tenant_no,
  100. BizProtocolSample.del_flag == 0,
  101. )
  102. )
  103. def _apply_sample_status_filter(q, sample_status: Optional[str]):
  104. st = (sample_status or "").strip()
  105. if not st or st == "all":
  106. return q
  107. if st == "uncollected":
  108. return q.filter(
  109. or_(
  110. BizProtocolSample.sample_status.is_(None),
  111. BizProtocolSample.sample_status == "",
  112. )
  113. )
  114. return q.filter(BizProtocolSample.sample_status == st)
  115. def list_protocol_sample_collections(
  116. db: Session,
  117. *,
  118. page: int = 1,
  119. page_size: int = 10,
  120. tenant_no: str,
  121. keyword: Optional[str] = None,
  122. sample_status: Optional[str] = None,
  123. user_info=None,
  124. ) -> Tuple[List[Dict[str, Any]], int]:
  125. if not tenant_no:
  126. return [], 0
  127. q = _base_query(db, tenant_no)
  128. q = _apply_sample_status_filter(q, sample_status)
  129. if keyword and keyword.strip():
  130. kw = f"%{keyword.strip()}%"
  131. kw_conds = [
  132. BizProtocolPartyCustomer.mother_name.like(kw),
  133. BizProtocolSample.sample_code.like(kw),
  134. ]
  135. from app.crud import sample_reception as reception_svc
  136. lab_codes = reception_svc.sample_codes_matching_lab_storage_keyword(
  137. db, tenant_no, keyword, reception_source="cell_storage"
  138. )
  139. if lab_codes:
  140. kw_conds.append(BizProtocolSample.sample_code.in_(lab_codes))
  141. q = q.filter(or_(*kw_conds))
  142. if user_info:
  143. from app.core.data_scope import apply_data_scope
  144. q = apply_data_scope(q, BizProtocolSample, user_info, db, tenant_no)
  145. total = q.count()
  146. page = max(1, page)
  147. page_size = min(max(1, page_size), 100)
  148. rows = (
  149. q.order_by(
  150. BizProtocolPartyCustomer.expected_delivery_date.is_(None),
  151. BizProtocolPartyCustomer.expected_delivery_date.desc(),
  152. BizCollectionProtocol.create_time.desc(),
  153. BizProtocolSample.id.desc(),
  154. )
  155. .offset((page - 1) * page_size)
  156. .limit(page_size)
  157. .all()
  158. )
  159. sample_type_ids = list({
  160. ps.sample_type_id for ps, _, _, _ in rows if ps and ps.sample_type_id
  161. })
  162. type_map: Dict[str, str] = {}
  163. if sample_type_ids:
  164. for t in (
  165. db.query(BizCellSampleType)
  166. .filter(BizCellSampleType.id.in_(sample_type_ids), BizCellSampleType.del_flag == 0)
  167. .all()
  168. ):
  169. type_map[t.id] = t.type_name or ""
  170. region_ids = list({
  171. (ci.region_id or "").strip()
  172. for _, _, _, ci in rows
  173. if ci and (ci.region_id or "").strip()
  174. })
  175. hospital_ids = list({
  176. (ci.hospital_id or "").strip()
  177. for _, _, _, ci in rows
  178. if ci and (ci.hospital_id or "").strip()
  179. })
  180. region_name_map: Dict[str, str] = {}
  181. hospital_name_map: Dict[str, str] = {}
  182. if region_ids:
  183. for r in (
  184. db.query(BizCollectionRegion.id, BizCollectionRegion.region_name)
  185. .filter(BizCollectionRegion.id.in_(region_ids), BizCollectionRegion.del_flag == 0)
  186. .all()
  187. ):
  188. region_name_map[r.id] = r.region_name or ""
  189. if hospital_ids:
  190. for h in (
  191. db.query(BizCollectionHospital.id, BizCollectionHospital.hospital_name)
  192. .filter(BizCollectionHospital.id.in_(hospital_ids), BizCollectionHospital.del_flag == 0)
  193. .all()
  194. ):
  195. hospital_name_map[h.id] = h.hospital_name or ""
  196. items: List[Dict[str, Any]] = []
  197. for psample, proto, party, ci in rows:
  198. status = (psample.sample_status or "").strip() if psample else ""
  199. region_id = (ci.region_id or "").strip() if ci else ""
  200. hospital_id = (ci.hospital_id or "").strip() if ci else ""
  201. item = _row_to_dict(
  202. protocol_sample_id=psample.id,
  203. collection_protocol_id=proto.id if proto else "",
  204. protocol_no=(proto.protocol_no or "") if proto else "",
  205. protocol_status=(proto.protocol_status or "") if proto else "",
  206. expected_delivery_date=format_date(party.expected_delivery_date) if party else "",
  207. mother_name=(party.mother_name or "") if party else "",
  208. newborn_name=(party.newborn_name or "") if party else "",
  209. sample_code=psample.sample_code or "",
  210. sample_type_name=type_map.get(psample.sample_type_id or "", ""),
  211. sample_status=status,
  212. sample_status_label=_sample_status_label(status),
  213. region_name=region_name_map.get(region_id, ""),
  214. hospital_name=hospital_name_map.get(hospital_id, ""),
  215. medical_staff=(ci.medical_staff or "") if ci else "",
  216. sample_collect_time=_fmt_dt(ci.sample_collect_time) if ci else "",
  217. maternal_blood_collect_time=_fmt_dt(ci.maternal_blood_collect_time) if ci else "",
  218. )
  219. item.update(sample_special_to_dict(psample))
  220. items.append(item)
  221. if items and tenant_no:
  222. from app.crud import sample_reception as reception_svc
  223. codes = [i["sampleCode"] for i in items if i.get("sampleCode")]
  224. code_map = reception_svc.get_lab_storage_code_map_by_sample_codes(
  225. db, tenant_no, codes, reception_source="cell_storage"
  226. )
  227. for item in items:
  228. sc = (item.get("sampleCode") or "").strip()
  229. item["labStorageCode"] = code_map.get(sc, "") if sc else ""
  230. return items, total