Files
medical_training/apps/case/services/exam_items.py
T

84 lines
3.2 KiB
Python

"""病例检查项:C1 解析归一化、C3 落库校验。"""
from decimal import Decimal, InvalidOperation
from config.exceptions import AppError
EXAM_ITEM_FIELDS = {
'item_code', 'item_name', 'item_type', 'category',
'result_text', 'result_structured',
'is_key', 'is_abnormal', 'score_weight', 'display_order',
}
def normalize_exam_items(raw_items) -> list:
"""归一化检查项列表;按 item_code 去重(保留首次出现)。"""
if not raw_items:
return []
if not isinstance(raw_items, list):
raise AppError('CASE_VALIDATION_ERROR', 'exam_items 必须为数组', status_code=400)
seen = set()
normalized = []
for i, item in enumerate(raw_items):
if not isinstance(item, dict):
raise AppError('CASE_VALIDATION_ERROR', f'exam_items[{i}] 必须为对象', status_code=400)
code = (item.get('item_code') or '').strip()
name = (item.get('item_name') or '').strip()
item_type = (item.get('item_type') or '').strip()
result_text = (item.get('result_text') or '').strip()
if not code:
raise AppError('CASE_VALIDATION_ERROR', f'exam_items[{i}].item_code 必填', status_code=400)
if not name:
raise AppError('CASE_VALIDATION_ERROR', f'exam_items[{i}].item_name 必填', status_code=400)
if not item_type:
raise AppError('CASE_VALIDATION_ERROR', f'exam_items[{i}].item_type 必填', status_code=400)
if not result_text:
raise AppError('CASE_VALIDATION_ERROR', f'exam_items[{i}].result_text 必填', status_code=400)
if code in seen:
continue
seen.add(code)
try:
weight = item.get('score_weight', 1)
weight = Decimal(str(weight))
except (InvalidOperation, TypeError, ValueError):
raise AppError(
'CASE_VALIDATION_ERROR',
f'exam_items[{i}].score_weight 须为数字',
status_code=400,
)
if weight <= 0:
raise AppError(
'CASE_VALIDATION_ERROR',
f'exam_items[{i}].score_weight 须大于 0',
status_code=400,
)
normalized.append({
'item_code': code[:64],
'item_name': name[:128],
'item_type': item_type[:32],
'category': (item.get('category') or '')[:64],
'result_text': result_text,
'result_structured': item.get('result_structured') if isinstance(
item.get('result_structured'), dict
) else None,
'is_key': bool(item.get('is_key', False)),
'is_abnormal': bool(item.get('is_abnormal', False)),
'score_weight': float(weight),
'display_order': int(item.get('display_order', len(normalized) + 1)),
})
return normalized
def assert_no_duplicate_exam_items(items: list):
"""创建前再次校验同一病例内 item_code 不重复。"""
codes = [it['item_code'] for it in items]
if len(codes) != len(set(codes)):
raise AppError(
'CASE_EXAM_ITEM_DUPLICATE',
'同一病例内检查项 item_code 不能重复',
status_code=400,
)