fix(lab-sample): 细胞编码、母血等字段与传染四项显示

列表细胞编码改展示实验室入库编码;从母血采集时间、协议附件与收样检查推导是否有母血等列;传染四项 zc/bzc 显示为正常/不正常。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-11-12 17:01:05 +08:00
1 parent 2878c6889d
commit 2aecb69cc4
2 files changed
+267 -28

No files matched your search

+234 -26
View File
@@ -19,11 +19,13 @@ from sqlalchemy.orm import Session
from app.models.models import (
BizCellSampleType,
BizCollectionHospital,
BizCollectionProtocol,
BizProtocolCollectionInfo,
BizProtocolPartyCustomer,
BizProtocolSample,
BizSampleProductionReception,
BizSampleReception,
BizSampleReceptionCheckResult,
)
from app.services.lab_sample_processing_fields import (
DATASET_HSC,
@@ -112,10 +114,98 @@ def _code_prefix_filters(dataset: str) -> List[str]:
def _match_keyword(row: Dict[str, Any], kw: str) -> bool:
if not kw:
return True
text = f"{row.get('sampleCode') or ''} {row.get('motherName') or ''}"
text = (
f"{row.get('sampleCode') or ''} "
f"{row.get('protocolSampleCode') or ''} "
f"{row.get('motherName') or ''}"
)
return kw.lower() in text.lower()
def _infectious_label(code: Optional[str]) -> Optional[str]:
c = ("" if code is None else str(code)).strip()
if not c:
return None
if c == "zc":
return "正常"
if c == "bzc":
return "不正常"
return c
def _infectious_code(label: Optional[Any]) -> Optional[str]:
if label is None:
return None
s = str(label).strip()
if not s:
return None
if s == "正常":
return "zc"
if s == "不正常":
return "bzc"
return s
def _yes_no_from_bool(flag: Optional[bool]) -> Optional[str]:
if flag is None:
return None
return "是" if flag else "否"
def _derive_receive_check(flags: Dict[str, bool]) -> Optional[str]:
if not flags:
return None
skip = {"知情同意书", "健康调查表", "样本信息正确,标识清晰"}
preferred = [
"针路密闭",
"容器无破损",
"外观正常",
"样本无破损",
"针卡关紧",
"两端系紧",
"温度4-25",
"采集至到库时间≤24小时",
]
parts = [name for name in preferred if flags.get(name)]
if not parts:
parts = [name for name, checked in flags.items() if checked and name not in skip]
return "、".join(parts) if parts else None
def _check_flag_map(
db: Session, tenant_no: str, reception_ids: List[str]
) -> Dict[str, Dict[str, bool]]:
ids = [i for i in reception_ids if i]
if not ids:
return {}
rows = (
db.query(BizSampleReceptionCheckResult)
.filter(
BizSampleReceptionCheckResult.tenant_no == tenant_no,
BizSampleReceptionCheckResult.del_flag == 0,
BizSampleReceptionCheckResult.reception_id.in_(ids),
)
.all()
)
out: Dict[str, Dict[str, bool]] = {}
for r in rows:
bucket = out.setdefault(r.reception_id, {})
name = (r.item_name or "").strip()
if name:
bucket[name] = bool(r.checked)
return out
def _pick_extra(
extras: Dict[str, Any], *codes: Optional[str]
) -> Dict[str, Any]:
for code in codes:
key = (code or "").strip()
if key and key in extras:
return _parse_extra(extras[key].payload_json)
return {}
def _match_date_range(row: Dict[str, Any], date_from: Optional[str], date_to: Optional[str]) -> bool:
if not date_from and not date_to:
return True
@@ -266,13 +356,44 @@ def list_rows(
):
productions.setdefault(p.reception_id, p)
protocol_ids = sorted(
{
s.collection_protocol_id
for s, *_ in filtered
if getattr(s, "collection_protocol_id", None)
}
)
protocols: Dict[str, BizCollectionProtocol] = {}
if protocol_ids:
for p in (
db.query(BizCollectionProtocol)
.filter(
BizCollectionProtocol.tenant_no == tenant_no,
BizCollectionProtocol.del_flag == 0,
BizCollectionProtocol.id.in_(protocol_ids),
)
.all()
):
protocols[p.id] = p
reception_ids_for_flags = list(
{
*(r.id for r in receptions_by_sample_id.values() if r and r.id),
*(r.id for r in receptions_by_code.values() if r and r.id),
}
)
check_flags_by_reception = _check_flag_map(db, tenant_no, reception_ids_for_flags)
built_map: Dict[str, Dict[str, Any]] = {}
for sample, stype, party, coll, hospital in filtered:
code = sample.sample_code
reception = receptions_by_sample_id.get(sample.id) or receptions_by_code.get(code)
production = productions.get(reception.id) if reception else None
extra = _parse_extra(extras[code].payload_json if code in extras else None)
built_map[code] = _build_row(
lab_code = (reception.lab_storage_code if reception else None) or ""
extra = _pick_extra(extras, lab_code, code)
protocol = protocols.get(sample.collection_protocol_id) if sample.collection_protocol_id else None
check_flags = check_flags_by_reception.get(reception.id, {}) if reception else {}
row = _build_row(
dataset=dataset,
sample=sample,
type_name=(stype.type_name if stype else None),
@@ -282,7 +403,10 @@ def list_rows(
reception=reception,
production=production,
extra=extra,
protocol=protocol,
check_flags=check_flags,
)
built_map[row.get("sampleCode") or code] = row
# 单个核:业务已发生数据主要在科研捐献接收(lab_storage_code=UBMC*),无细胞储存协议样本时仍需展示
if dataset == DATASET_UBMC:
@@ -335,16 +459,23 @@ def list_rows(
):
productions.setdefault(p.reception_id, p)
ubmc_flags = _check_flag_map(
db,
tenant_no,
[r.id for r in ubmc_recs if r.id and r.id not in check_flags_by_reception],
)
check_flags_by_reception.update(ubmc_flags)
for reception in ubmc_recs:
code = (reception.lab_storage_code or reception.sample_code or "").strip()
if not code or code in built_map:
continue
production = productions.get(reception.id)
extra = _parse_extra(extras[code].payload_json if code in extras else None)
extra = _pick_extra(extras, code, reception.sample_code)
mother = mother_by_donation.get(str(reception.research_donation_id or ""))
fake_sample = SimpleNamespace(
id=reception.donation_sample_id or reception.id,
sample_code=code,
sample_code=reception.sample_code or code,
collection_protocol_id=None,
)
fake_party = SimpleNamespace(mother_name=mother) if mother else None
@@ -358,6 +489,8 @@ def list_rows(
reception=reception,
production=production,
extra=extra,
protocol=None,
check_flags=check_flags_by_reception.get(reception.id, {}),
)
kw = (keyword or "").strip()
@@ -390,9 +523,23 @@ def _build_row(
reception: Optional[BizSampleReception],
production: Optional[BizSampleProductionReception],
extra: Dict[str, Any],
protocol: Optional[BizCollectionProtocol] = None,
check_flags: Optional[Dict[str, bool]] = None,
) -> Dict[str, Any]:
flags = check_flags or {}
protocol_code = (
(sample.sample_code if sample else None)
or extra.get("protocolSampleCode")
or ""
)
lab_code = ((reception.lab_storage_code if reception else None) or "").strip()
# 台账「细胞编码」展示实验室入库编码(B000…),协议样本号保留检索
display_code = lab_code if lab_code and len(lab_code) > 1 else (
protocol_code or extra.get("sampleCode") or ""
)
protocol_vals = {
"sample_code": sample.sample_code if sample else extra.get("sampleCode"),
"sample_code": display_code,
"mother_name": party.mother_name if party else None,
"sample_collect_time": coll.sample_collect_time if coll else None,
"hospital_name": hospital.hospital_name if hospital else None,
@@ -425,15 +572,11 @@ def _build_row(
"available_cell_count_1e7": production.available_cell_count_1e7,
}
sample_code = (
(sample.sample_code if sample else None)
or extra.get("sampleCode")
or ""
)
data: Dict[str, Any] = {
"id": (sample.id if sample else f"extra:{dataset}:{sample_code}"),
"id": (sample.id if sample else f"extra:{dataset}:{display_code}"),
"dataset": dataset,
"sampleCode": sample_code,
"sampleCode": display_code,
"protocolSampleCode": protocol_code or None,
"sampleTypeName": type_name,
"protocolSampleId": sample.id if sample else None,
"collectionProtocolId": sample.collection_protocol_id if sample else None,
@@ -458,8 +601,43 @@ def _build_row(
elif isinstance(val, Decimal):
val = _fmt_num(val)
data[f.key] = val
if not data.get("sampleCode"):
data["sampleCode"] = sample_code
data["sampleCode"] = display_code
# 从协议采集信息 / 附件 / 收样检查项推导补充列(补充表有值时不覆盖)
if not data.get("hasMaternalBlood"):
if coll is not None:
data["hasMaternalBlood"] = "是" if coll.maternal_blood_collect_time else "否"
if not data.get("hasConsent"):
if protocol and (protocol.consent_form_urls or "").strip():
data["hasConsent"] = "是"
else:
flagged = _yes_no_from_bool(flags.get("知情同意书"))
if flagged is not None:
data["hasConsent"] = flagged
elif protocol is not None:
data["hasConsent"] = "否"
if not data.get("hasHealthSurvey"):
if protocol and (protocol.health_survey_form_urls or "").strip():
data["hasHealthSurvey"] = "是"
else:
flagged = _yes_no_from_bool(flags.get("健康调查表"))
if flagged is not None:
data["hasHealthSurvey"] = flagged
elif protocol is not None:
data["hasHealthSurvey"] = "否"
if not data.get("labelClear"):
data["labelClear"] = _yes_no_from_bool(flags.get("样本信息正确,标识清晰"))
if not data.get("receiveCheck"):
data["receiveCheck"] = _derive_receive_check(flags)
if data.get("infectiousFourCard") is not None:
data["infectiousFourCard"] = _infectious_label(data.get("infectiousFourCard"))
return data
@@ -549,6 +727,19 @@ def update_row(
if not accepted:
raise ValueError("没有可保存的字段")
reception = (
db.query(BizSampleReception)
.filter(
BizSampleReception.tenant_no == tenant_no,
BizSampleReception.del_flag == 0,
or_(
BizSampleReception.lab_storage_code == code,
BizSampleReception.sample_code == code,
),
)
.order_by(BizSampleReception.reception_time.desc())
.first()
)
sample = (
db.query(BizProtocolSample)
.filter(
@@ -558,16 +749,30 @@ def update_row(
)
.first()
)
reception = (
db.query(BizSampleReception)
.filter(
BizSampleReception.tenant_no == tenant_no,
BizSampleReception.sample_code == code,
BizSampleReception.del_flag == 0,
if sample is None and reception is not None and reception.protocol_sample_id:
sample = (
db.query(BizProtocolSample)
.filter(
BizProtocolSample.tenant_no == tenant_no,
BizProtocolSample.id == reception.protocol_sample_id,
BizProtocolSample.del_flag == 0,
)
.first()
)
if reception is None and sample is not None:
reception = (
db.query(BizSampleReception)
.filter(
BizSampleReception.tenant_no == tenant_no,
BizSampleReception.del_flag == 0,
or_(
BizSampleReception.protocol_sample_id == sample.id,
BizSampleReception.sample_code == sample.sample_code,
),
)
.order_by(BizSampleReception.reception_time.desc())
.first()
)
.order_by(BizSampleReception.reception_time.desc())
.first()
)
production = None
if reception:
production = (
@@ -661,9 +866,12 @@ def update_row(
elif f.attr in numeric_attrs:
production_val = None if value in (None, "") else Decimal(str(value))
setattr(production, f.attr, production_val)
elif f.attr == "infectious_four_test_result":
production.infectious_four_test_result = (
None if value in (None, "") else _infectious_code(value)
)
elif f.attr in {
"blood_type",
"infectious_four_test_result",
"handler_name",
"remark",
"inoculation_amount",
@@ -690,7 +898,7 @@ def update_row(
rows, _ = list_rows(db, tenant_no=tenant_no, dataset=dataset, keyword=code, page=1, page_size=20)
for row in rows:
if row.get("sampleCode") == code:
if row.get("sampleCode") == code or row.get("protocolSampleCode") == code:
return row
raise LookupError("保存成功但读取失败")