fix(lab-sample): 细胞编码、母血等字段与传染四项显示
列表细胞编码改展示实验室入库编码;从母血采集时间、协议附件与收样检查推导是否有母血等列;传染四项 zc/bzc 显示为正常/不正常。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
2878c6889d
commit
2aecb69cc4
2 files changed
+267
-28
No files matched your search
@@ -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("保存成功但读取失败")
|
||||
|
||||
|
||||
Reference in new issue
Block a user