|
@@ -159,6 +159,12 @@ def parse_triplet_until(lines, stop_markers):
|
|
|
def parse_report_pdf(file_path: str) -> dict:
|
|
def parse_report_pdf(file_path: str) -> dict:
|
|
|
"""主函数:解析 PDF 返回结构化数据"""
|
|
"""主函数:解析 PDF 返回结构化数据"""
|
|
|
lines = extract_text(file_path)
|
|
lines = extract_text(file_path)
|
|
|
|
|
+ full_text = '\n'.join(lines)
|
|
|
|
|
+
|
|
|
|
|
+ # 指纹检测:北京菌群报告
|
|
|
|
|
+ if detect_beijing_format(full_text):
|
|
|
|
|
+ return _parse_beijing_report(file_path)
|
|
|
|
|
+
|
|
|
fmt = detect_format(lines)
|
|
fmt = detect_format(lines)
|
|
|
result = {'format': fmt, 'overview': parse_overview(lines)}
|
|
result = {'format': fmt, 'overview': parse_overview(lines)}
|
|
|
|
|
|
|
@@ -204,6 +210,21 @@ def parse_report_pdf(file_path: str) -> dict:
|
|
|
'数据': food_rows
|
|
'数据': food_rows
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ # 统一结构:北京报告特有字段(标准报告为空)
|
|
|
|
|
+ result['肠道微生物健康指数'] = {}
|
|
|
|
|
+ result['菌群多样性'] = {}
|
|
|
|
|
+ result['肠道黏膜屏障'] = {}
|
|
|
|
|
+ result['菌群表型评估'] = {}
|
|
|
|
|
+ result['短链脂肪酸合成能力'] = {}
|
|
|
|
|
+ result['肠道菌群精准分布'] = []
|
|
|
|
|
+ result['营养物质及营养素代谢评估'] = {}
|
|
|
|
|
+ result['抗生素风险评估'] = {}
|
|
|
|
|
+ result['毒性物质清除能力评估'] = {}
|
|
|
|
|
+ result['趣味肠菌评估'] = {}
|
|
|
|
|
+ result['胃肠道感染病原体'] = []
|
|
|
|
|
+ result['健康整体评估'] = {}
|
|
|
|
|
+ result['肠道菌群主要检测结果'] = {}
|
|
|
|
|
+
|
|
|
return result
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
@@ -751,3 +772,694 @@ def _extract_food_rows(pdf_path):
|
|
|
continue
|
|
continue
|
|
|
food_rows, food_fmt = extract_food_table(pdf_path, ref_nutrition or None)
|
|
food_rows, food_fmt = extract_food_table(pdf_path, ref_nutrition or None)
|
|
|
return food_rows, food_fmt
|
|
return food_rows, food_fmt
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# ============================================================
|
|
|
|
|
+# 北京菌群报告 — 指纹检测
|
|
|
|
|
+# ============================================================
|
|
|
|
|
+
|
|
|
|
|
+FINGERPRINTS_BEIJING = [
|
|
|
|
|
+ "肠道微生物健康指数",
|
|
|
|
|
+ "GMHI",
|
|
|
|
|
+ "肠道菌群主要检测结果",
|
|
|
|
|
+ "核心菌属综合评分",
|
|
|
|
|
+ "有益菌综合评分",
|
|
|
|
|
+ "有害菌综合评分",
|
|
|
|
|
+ "肠道菌群精准分布",
|
|
|
|
|
+ "营养物质及营养素代谢评估",
|
|
|
|
|
+ "趣味肠菌评估",
|
|
|
|
|
+ "高通量测序",
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def detect_beijing_format(text):
|
|
|
|
|
+ """检测是否为北京菌群报告格式"""
|
|
|
|
|
+ beijing_matches = sum(1 for f in FINGERPRINTS_BEIJING if f in text)
|
|
|
|
|
+ if beijing_matches >= 3:
|
|
|
|
|
+ return True
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# ============================================================
|
|
|
|
|
+# 北京菌群报告 — 章节提取(PyPDF2 版)
|
|
|
|
|
+# ============================================================
|
|
|
|
|
+
|
|
|
|
|
+# 疾病风险关键词(北京报告特有)
|
|
|
|
|
+DISEASE_KEYWORDS_BJ = [
|
|
|
|
|
+ "溃疡性结肠炎", "克罗恩病", "腹泻型肠易激综合征",
|
|
|
|
|
+ "硬化性胆管炎", "非酒精性脂肪性肝病", "肝硬化",
|
|
|
|
|
+ "便秘", "IgA肾病", "肾结石", "格雷夫斯病",
|
|
|
|
|
+ "慢性淋巴细胞性甲状腺炎", "强直性脊柱炎",
|
|
|
|
|
+ "精神分裂症", "痛经",
|
|
|
|
|
+ "结直肠癌", "胃癌", "甲状腺癌", "肺癌", "乳腺癌",
|
|
|
|
|
+ "高血压", "冠心病", "2型糖尿病", "高脂血症",
|
|
|
|
|
+ "食管癌", "胆结石", "哮喘", "心脏病",
|
|
|
|
|
+ "膜性肾病", "帕金森病", "类风湿关节炎",
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+# 抗生素分类(北京报告格式)
|
|
|
|
|
+ANTIBIOTIC_CATEGORIES_BJ = [
|
|
|
|
|
+ "β内酰胺类", "氨基糖苷类", "大环内酯类", "四环素类",
|
|
|
|
|
+ "氯霉素类", "磺酰胺类", "喹诺酮类", "万古霉素类",
|
|
|
|
|
+ "头孢菌素类", "磷霉素类", "甲氧苄氨嘧啶类",
|
|
|
|
|
+ "N-乙酰基转移酶类", "O-磷酸转移酶类", "16S甲基转移酶",
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+# 核心菌属 22 种(北京报告)
|
|
|
|
|
+CORE_GENERA_BJ = [
|
|
|
|
|
+ ("阿克曼菌属", "Akkermansia"), ("另枝菌属", "Alistipes"),
|
|
|
|
|
+ ("拟杆菌属", "Bacteroides"), ("双歧杆菌属", "Bifidobacterium"),
|
|
|
|
|
+ ("布劳特氏菌属", "Blautia"), ("梭菌属", "Clostridium"),
|
|
|
|
|
+ ("粪球菌属", "Coprococcus"), ("戴阿利斯特杆菌属", "Dialister"),
|
|
|
|
|
+ ("多尔氏菌属", "Dorea"), ("真杆菌属", "Eubacterium"),
|
|
|
|
|
+ ("粪杆菌属", "Faecalibacterium"), ("Lachnoclostridium属", "Lachnoclostridium"),
|
|
|
|
|
+ ("毛螺菌属", "Lachnospira"), ("乳杆菌属", "Lactobacillus"),
|
|
|
|
|
+ ("巨单胞菌属", "Megamonas"), ("颤螺旋菌属", "Oscillospira"),
|
|
|
|
|
+ ("副拟杆菌属", "Parabacteroides"), ("考拉杆菌属", "Phascolarctobacterium"),
|
|
|
|
|
+ ("普雷沃氏菌属", "Prevotella"), ("罗氏菌属", "Roseburia"),
|
|
|
|
|
+ ("瘤胃球菌属", "Ruminococcus"), ("萨特氏菌属", "Sutterella"),
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+# 病原体列表(北京报告)
|
|
|
|
|
+PATHOGEN_LIST_BJ = [
|
|
|
|
|
+ "气单胞菌属", "蜡样芽孢杆菌", "唐菖蒲伯克霍尔德氏菌",
|
|
|
|
|
+ "弯曲杆菌属", "艰难梭菌", "肉毒梭菌", "产气荚膜梭菌",
|
|
|
|
|
+ "幽门螺杆菌", "肺炎克雷伯菌", "类志贺邻单胞菌",
|
|
|
|
|
+ "沙门氏菌属", "志贺氏菌属", "金黄色葡萄球菌",
|
|
|
|
|
+ "肺炎链球菌", "霍乱弧菌", "拟态弧菌", "副溶血弧菌",
|
|
|
|
|
+ "小肠结肠炎耶尔森氏菌", "假结核耶尔森氏菌",
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _find_content_after_toc(text):
|
|
|
|
|
+ """找到北京报告中 TOC 之后的内容起始位置"""
|
|
|
|
|
+ # 找"第一部分 健康整体评估"后的内容,或者"健康整体评估"正文
|
|
|
|
|
+ markers = ['第一部分 健康整体评估', '菌群得分总览', '您属于肠']
|
|
|
|
|
+ for m in markers:
|
|
|
|
|
+ idx = text.find(m)
|
|
|
|
|
+ if idx != -1:
|
|
|
|
|
+ return idx
|
|
|
|
|
+ return 0
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _find_last_section(text, section_title):
|
|
|
|
|
+ """找到章节标题的最后一次出现(实际内容,非目录)"""
|
|
|
|
|
+ last = -1
|
|
|
|
|
+ start = 0
|
|
|
|
|
+ while True:
|
|
|
|
|
+ idx = text.find(section_title, start)
|
|
|
|
|
+ if idx == -1:
|
|
|
|
|
+ break
|
|
|
|
|
+ last = idx
|
|
|
|
|
+ start = idx + 1
|
|
|
|
|
+ return last
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_gmhi(text):
|
|
|
|
|
+ """提取 GMHI 肠道微生物健康指数(从内容区提取)"""
|
|
|
|
|
+ gmhi = {}
|
|
|
|
|
+ # 找最后一次出现的 GMHI 值(内容区,非目录)
|
|
|
|
|
+ content_start = _find_content_after_toc(text)
|
|
|
|
|
+ content = text[content_start:]
|
|
|
|
|
+
|
|
|
|
|
+ m = re.search(r'GMHI[)\)]?[为是](\d+)', content)
|
|
|
|
|
+ if m: gmhi['GMHI'] = float(m.group(1))
|
|
|
|
|
+ m = re.search(r'肠道微生物健康指数[为](\d+)', content)
|
|
|
|
|
+ if m: gmhi['肠道微生物健康指数'] = float(m.group(1))
|
|
|
|
|
+ m = re.search(r'肠道菌群状态[为::\s]*(\S+?)[。,\n]', content)
|
|
|
|
|
+ if m: gmhi['肠道菌群状态'] = m.group(1)
|
|
|
|
|
+
|
|
|
|
|
+ # 健康状态推断
|
|
|
|
|
+ if gmhi.get('肠道微生物健康指数') or gmhi.get('GMHI'):
|
|
|
|
|
+ score = gmhi.get('肠道微生物健康指数') or gmhi.get('GMHI', 0)
|
|
|
|
|
+ if score >= 90: gmhi['健康状态'] = '健康'
|
|
|
|
|
+ elif score >= 80: gmhi['健康状态'] = '健康倾向'
|
|
|
|
|
+ elif score >= 40: gmhi['健康状态'] = '亚健康'
|
|
|
|
|
+ elif score >= 20: gmhi['健康状态'] = '不健康倾向'
|
|
|
|
|
+ else: gmhi['健康状态'] = '不健康'
|
|
|
|
|
+ return gmhi
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_gut_type(text):
|
|
|
|
|
+ """提取肠型"""
|
|
|
|
|
+ content_start = _find_content_after_toc(text)
|
|
|
|
|
+ content = text[content_start:]
|
|
|
|
|
+ m = re.search(r'属于\s*肠[型道]\s*([IVX]+)', content)
|
|
|
|
|
+ if m: return f'肠型{m.group(1)}'
|
|
|
|
|
+ m = re.search(r'属于\s*肠[型道]\s*(\S+?)[,。,.\n]', content)
|
|
|
|
|
+ if m: return m.group(1).strip()
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_balance(text):
|
|
|
|
|
+ """提取肠道菌群平衡评估"""
|
|
|
|
|
+ m = re.search(r'(I{1,3}度失衡)', text)
|
|
|
|
|
+ if m: return m.group(1)
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_barrier(text):
|
|
|
|
|
+ """提取肠道黏膜屏障/BE比值"""
|
|
|
|
|
+ barrier = {}
|
|
|
|
|
+ # 在内容区找表格
|
|
|
|
|
+ content_start = _find_content_after_toc(text)
|
|
|
|
|
+ content = text[content_start:]
|
|
|
|
|
+ # 找 BE比值 表格行
|
|
|
|
|
+ m = re.search(r'BE比[值例][::\s]*(\d+\.?\d*)', text)
|
|
|
|
|
+ if m: barrier['BE比值'] = float(m.group(1))
|
|
|
|
|
+ m = re.search(r'双歧杆菌[属菌][::\s]*(\d+\.?\d*)', text)
|
|
|
|
|
+ if m: barrier['双歧杆菌属'] = float(m.group(1))
|
|
|
|
|
+ m = re.search(r'肠杆菌[科属][::\s]*(\d+\.?\d*)', text)
|
|
|
|
|
+ if m: barrier['肠杆菌科'] = float(m.group(1))
|
|
|
|
|
+ # 综合评估在"BE比值"附近找
|
|
|
|
|
+ be_idx = content.find('BE比')
|
|
|
|
|
+ if be_idx != -1:
|
|
|
|
|
+ chunk = content[be_idx:be_idx + 300]
|
|
|
|
|
+ m = re.search(r'(正常|轻度损伤|中度损伤|重度损伤|损伤)', chunk)
|
|
|
|
|
+ if m: barrier['综合评估'] = m.group(1)
|
|
|
|
|
+ return barrier
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_diversity(text):
|
|
|
|
|
+ """提取菌群多样性"""
|
|
|
|
|
+ div = {}
|
|
|
|
|
+ content_start = _find_content_after_toc(text)
|
|
|
|
|
+ content = text[content_start:]
|
|
|
|
|
+ m = re.search(r'多样性指数[为](\d+\.?\d*)', content)
|
|
|
|
|
+ if m: div['Shannon指数'] = float(m.group(1))
|
|
|
|
|
+ m = re.search(r'高于[::\s]*(\d+)%', content)
|
|
|
|
|
+ if m: div['人群百分位'] = f'高于{m.group(1)}%'
|
|
|
|
|
+ m = re.search(r'菌种总数[::\s]*(\d+)', content)
|
|
|
|
|
+ if m: div['菌种总数'] = int(m.group(1))
|
|
|
|
|
+ return div
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_phenotype(text):
|
|
|
|
|
+ """提取菌群表型评估"""
|
|
|
|
|
+ pheno = {}
|
|
|
|
|
+ # 找表型评估表格区域
|
|
|
|
|
+ last_idx = _find_last_section(text, '菌群表型评估')
|
|
|
|
|
+ if last_idx == -1:
|
|
|
|
|
+ last_idx = _find_last_section(text, '肠道菌群表型评估')
|
|
|
|
|
+ if last_idx == -1:
|
|
|
|
|
+ return pheno
|
|
|
|
|
+ # 取表格区域
|
|
|
|
|
+ region = text[last_idx:last_idx + 1500]
|
|
|
|
|
+ # 解析表格行: 指标名 评估值 结果
|
|
|
|
|
+ for label, kw in [('革兰氏阳性菌', '阳性菌'), ('革兰氏阴性菌', '阴性菌'),
|
|
|
|
|
+ ('生物膜合成', '生物膜'), ('好氧菌', '好氧'),
|
|
|
|
|
+ ('厌氧菌', '厌氧'), ('兼性厌氧菌', '兼性'),
|
|
|
|
|
+ ('氧化胁迫耐受', '氧化'), ('致病潜力', '致病')]:
|
|
|
|
|
+ # 找该行: 值在指标名附近
|
|
|
|
|
+ idx = region.find(kw)
|
|
|
|
|
+ if idx == -1:
|
|
|
|
|
+ continue
|
|
|
|
|
+ chunk = region[idx:idx + 100]
|
|
|
|
|
+ val_m = re.search(r'(\d+\.?\d*)', chunk)
|
|
|
|
|
+ if val_m:
|
|
|
|
|
+ entry = {'评估值': float(val_m.group(1))}
|
|
|
|
|
+ for status in ['正常', '异常']:
|
|
|
|
|
+ if status in chunk:
|
|
|
|
|
+ entry['结果'] = status
|
|
|
|
|
+ break
|
|
|
|
|
+ pheno[label] = entry
|
|
|
|
|
+ return pheno
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_scfa(text):
|
|
|
|
|
+ """提取短链脂肪酸合成能力"""
|
|
|
|
|
+ scfa = {}
|
|
|
|
|
+ # 找 SCFA 表格区域(最后一次出现)
|
|
|
|
|
+ last_idx = _find_last_section(text, '短链脂肪酸合成能力')
|
|
|
|
|
+ if last_idx == -1:
|
|
|
|
|
+ last_idx = _find_last_section(text, '短链脂肪酸')
|
|
|
|
|
+ if last_idx == -1:
|
|
|
|
|
+ return scfa
|
|
|
|
|
+ region = text[last_idx:last_idx + 800]
|
|
|
|
|
+ for acid in ['甲酸', '乙酸', '丙酸', '丁酸', '戊酸', '己酸']:
|
|
|
|
|
+ m = re.search(rf'{acid}[^a-zA-Z]*?(\d+)', region)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ scfa[acid] = int(m.group(1))
|
|
|
|
|
+ return scfa
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_core_bacteria(text):
|
|
|
|
|
+ """提取核心菌属精准分布"""
|
|
|
|
|
+ bacteria = []
|
|
|
|
|
+ for cn_name, lat_name in CORE_GENERA_BJ:
|
|
|
|
|
+ idx = text.find(lat_name)
|
|
|
|
|
+ if idx == -1: idx = text.find(cn_name)
|
|
|
|
|
+ if idx == -1: continue
|
|
|
|
|
+ chunk = text[max(0, idx - 50):idx + 200]
|
|
|
|
|
+ val_m = re.search(r'(\d+\.?\d*)', chunk)
|
|
|
|
|
+ status_m = re.search(r'(缺失|严重超标|超标|偏低|正常)', chunk)
|
|
|
|
|
+ pct_m = re.search(r'(\d+\.?\d*)%', chunk)
|
|
|
|
|
+ range_m = re.search(r'([\d.]+-[\d.]+)', chunk)
|
|
|
|
|
+ entry = {
|
|
|
|
|
+ '拉丁名': lat_name,
|
|
|
|
|
+ '中文名': cn_name,
|
|
|
|
|
+ '检测结果': float(val_m.group(1)) if val_m else None,
|
|
|
|
|
+ '状态': status_m.group(1) if status_m else '未知',
|
|
|
|
|
+ '人群百分位': float(pct_m.group(1)) if pct_m else None,
|
|
|
|
|
+ '参考范围': range_m.group(1) if range_m else None,
|
|
|
|
|
+ }
|
|
|
|
|
+ bacteria.append(entry)
|
|
|
|
|
+ return bacteria
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_disease_risks(text):
|
|
|
|
|
+ """提取疾病风险评估"""
|
|
|
|
|
+ diseases = []
|
|
|
|
|
+ for keyword in DISEASE_KEYWORDS_BJ:
|
|
|
|
|
+ idx = text.find(keyword)
|
|
|
|
|
+ if idx == -1: continue
|
|
|
|
|
+ chunk = text[idx:idx + 200]
|
|
|
|
|
+ val_m = re.search(r'(\d+\.\d+)', chunk)
|
|
|
|
|
+ level_m = re.search(r'(低风险|较低风险|中度风险|较高风险|高风险)', chunk)
|
|
|
|
|
+ if val_m:
|
|
|
|
|
+ diseases.append({
|
|
|
|
|
+ '疾病': keyword,
|
|
|
|
|
+ '风险指数': float(val_m.group(1)),
|
|
|
|
|
+ '风险等级': level_m.group(1) if level_m else '',
|
|
|
|
|
+ })
|
|
|
|
|
+ return diseases
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_nutrient_metabolism(text):
|
|
|
|
|
+ """提取营养物质及营养素代谢评估"""
|
|
|
|
|
+ metabolism = {}
|
|
|
|
|
+ # 主要营养物质
|
|
|
|
|
+ for n in ['碳水化合物', '蛋白质', '脂肪', '矿物质']:
|
|
|
|
|
+ m = re.search(rf'{n}[^估]*?(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ metabolism.setdefault('主要营养物质代谢', {})[n] = int(m.group(1))
|
|
|
|
|
+ # 糖类
|
|
|
|
|
+ for item in ['葡萄糖(糖酵解途径)', '葡萄糖(磷酸戊糖途径)', '果糖和甘露糖',
|
|
|
|
|
+ '半乳糖', '淀粉和蔗糖', '氨基糖和核苷酸糖', '丙酮酸']:
|
|
|
|
|
+ m = re.search(rf'{item[:4]}.*?(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ metabolism.setdefault('糖类代谢', {})[item] = int(m.group(1))
|
|
|
|
|
+ # 脂类
|
|
|
|
|
+ for item in ['甘油酯', '甘油磷脂', '甘油三酯', '鞘脂类', '脂肪酸',
|
|
|
|
|
+ '硬脂酸', '棕榈酸', '花生四烯酸', '二十二碳六烯酸',
|
|
|
|
|
+ 'Omega-3', 'Omega-6']:
|
|
|
|
|
+ m = re.search(rf'{item}.*?(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ metabolism.setdefault('脂类代谢', {})[item] = int(m.group(1))
|
|
|
|
|
+ # 维生素
|
|
|
|
|
+ for item in ['维生素A', '维生素B1', '维生素B2', '维生素B3', '维生素B5',
|
|
|
|
|
+ '维生素B6', '维生素B7', '维生素B12', '维生素C', '维生素D',
|
|
|
|
|
+ '维生素E', '维生素K1', '维生素K2', '维生素K3', '叶酸']:
|
|
|
|
|
+ m = re.search(rf'{item}.*?(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ metabolism.setdefault('维生素', {})[item] = int(m.group(1))
|
|
|
|
|
+ # 微量元素
|
|
|
|
|
+ for item in ['铁', '锌', '钙', '镁', '硒', '锰', '铜', '钴', '镍', '钼', '铬', '钒']:
|
|
|
|
|
+ m = re.search(rf'{item}[^估]*?(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ metabolism.setdefault('微量元素', {})[item] = int(m.group(1))
|
|
|
|
|
+ # 氨基酸
|
|
|
|
|
+ for item in ['赖氨酸', '丝氨酸', '亮氨酸', '色氨酸', '苯丙氨酸',
|
|
|
|
|
+ '缬氨酸', '组氨酸', '半胱氨酸', '酪氨酸', '丙氨酸',
|
|
|
|
|
+ '脯氨酸', '苏氨酸', '谷氨酸', '异亮氨酸', '精氨酸',
|
|
|
|
|
+ '蛋氨酸', '甘氨酸', '天冬氨酸', '牛磺酸']:
|
|
|
|
|
+ m = re.search(rf'{item}[^估]*?(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ metabolism.setdefault('氨基酸', {})[item] = int(m.group(1))
|
|
|
|
|
+ # 神经递质
|
|
|
|
|
+ for item in ['5-羟色胺', 'γ-氨基丁酸', '多巴胺', '乙酰胆碱', '组胺', '去甲肾上腺素']:
|
|
|
|
|
+ m = re.search(rf'{item}.*?(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ metabolism.setdefault('神经递质', {})[item] = int(m.group(1))
|
|
|
|
|
+ # 其他单项
|
|
|
|
|
+ for pattern, key in [
|
|
|
|
|
+ (r'嘌呤代谢.*?(\d+)', '嘌呤代谢'),
|
|
|
|
|
+ (r'谷胱甘肽.*?(\d+)', '三肽(谷胱甘肽)'),
|
|
|
|
|
+ (r'胆汁酸.*?(\d+)', '胆汁酸代谢'),
|
|
|
|
|
+ (r'硫辛酸.*?(\d+)', '抗自由基(硫辛酸)'),
|
|
|
|
|
+ (r'辅酶Q.*?(\d+)', '抗自由基(辅酶Q)'),
|
|
|
|
|
+ ]:
|
|
|
|
|
+ m = re.search(pattern, text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ metabolism[key] = int(m.group(1))
|
|
|
|
|
+ return metabolism
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_antibiotics(text):
|
|
|
|
|
+ """提取抗生素风险评估"""
|
|
|
|
|
+ ab = {}
|
|
|
|
|
+ for cat in ANTIBIOTIC_CATEGORIES_BJ:
|
|
|
|
|
+ m = re.search(rf'{cat}[::\s]*(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ ab[cat] = int(m.group(1))
|
|
|
|
|
+ return ab
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_toxins(text):
|
|
|
|
|
+ """提取毒性物质清除能力评估"""
|
|
|
|
|
+ toxins = {}
|
|
|
|
|
+ toxin_items = [
|
|
|
|
|
+ "苯甲酸酯", "对氨基苯甲酸乙酯", "对氟苯甲酸乙酯",
|
|
|
|
|
+ "氯烷烃和氯烯烃", "氯代环己烷", "氯苯",
|
|
|
|
|
+ "甲苯", "二甲苯", "硝基甲苯", "乙苯", "苯乙烯",
|
|
|
|
|
+ "阿特拉津", "己内酰胺", "双酚", "二恶英",
|
|
|
|
|
+ "萘", "多环芳烃",
|
|
|
|
|
+ ]
|
|
|
|
|
+ for item in toxin_items:
|
|
|
|
|
+ m = re.search(rf'{item}.*?(\d+)', text)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ toxins[item] = int(m.group(1))
|
|
|
|
|
+ return toxins
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_pathogens(text):
|
|
|
|
|
+ """提取胃肠道感染病原体评估"""
|
|
|
|
|
+ pathogens = []
|
|
|
|
|
+ for p in PATHOGEN_LIST_BJ:
|
|
|
|
|
+ idx = text.find(p)
|
|
|
|
|
+ if idx == -1: continue
|
|
|
|
|
+ chunk = text[idx:idx + 150]
|
|
|
|
|
+ val_m = re.search(r'(\d+\.?\d*)', chunk)
|
|
|
|
|
+ status_m = re.search(r'(检出|超标|未检出|正常)', chunk)
|
|
|
|
|
+ if val_m:
|
|
|
|
|
+ entry = {'名称': p, '检测值': float(val_m.group(1)) if '.' in val_m.group(1) else int(val_m.group(1))}
|
|
|
|
|
+ if status_m: entry['状态'] = status_m.group(1)
|
|
|
|
|
+ pathogens.append(entry)
|
|
|
|
|
+ return pathogens
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_psych_eval(text, find_table_val=None):
|
|
|
|
|
+ """提取趣味肠菌评估"""
|
|
|
|
|
+ psych = {}
|
|
|
|
|
+ m = re.search(r'最高概率[::\s]*(\S+)', text)
|
|
|
|
|
+ if m: psych['人格特征'] = {'最高概率': m.group(1)}
|
|
|
|
|
+ # 认知功能(从表格)
|
|
|
|
|
+ if find_table_val:
|
|
|
|
|
+ cog = find_table_val('认知能力', val_col=1)
|
|
|
|
|
+ if cog:
|
|
|
|
|
+ psych['认知功能'] = {'认知能力评分': int(cog)}
|
|
|
|
|
+ return psych
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_scfa_from_tables(find_table_rows):
|
|
|
|
|
+ """从表格提取短链脂肪酸合成能力"""
|
|
|
|
|
+ scfa = {}
|
|
|
|
|
+ for acid in ['甲酸', '乙酸', '丙酸', '丁酸', '戊酸', '己酸']:
|
|
|
|
|
+ rows = find_table_rows(acid)
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ if acid in name:
|
|
|
|
|
+ scfa[acid] = int(val)
|
|
|
|
|
+ break
|
|
|
|
|
+ return scfa
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_core_bacteria_from_tables(all_tables):
|
|
|
|
|
+ """从表格提取核心菌属精准分布"""
|
|
|
|
|
+ bacteria = []
|
|
|
|
|
+ for cn_name, lat_name in CORE_GENERA_BJ:
|
|
|
|
|
+ for table in all_tables:
|
|
|
|
|
+ for row in table:
|
|
|
|
|
+ row_str = ' '.join(str(c) for c in row if c)
|
|
|
|
|
+ if lat_name in row_str or cn_name in row_str:
|
|
|
|
|
+ vals = [str(c).strip() for c in row if c and str(c).strip()]
|
|
|
|
|
+ entry = {'拉丁名': lat_name, '中文名': cn_name}
|
|
|
|
|
+ for v in vals:
|
|
|
|
|
+ v_clean = v.replace('"', '').replace('#', '').strip()
|
|
|
|
|
+ # 判断是否为范围值(如 0.0041-0.0339)
|
|
|
|
|
+ if '-' in v_clean and v_clean.replace('.','').replace('-',' ').replace(' ','').isdigit():
|
|
|
|
|
+ entry['参考范围'] = v_clean
|
|
|
|
|
+ elif v_clean == 'ND':
|
|
|
|
|
+ if '检测结果' not in entry:
|
|
|
|
|
+ entry['检测结果'] = 0.0
|
|
|
|
|
+ elif v_clean.replace('.','').replace('-','').isdigit() and v_clean != '':
|
|
|
|
|
+ val = float(v_clean)
|
|
|
|
|
+ if '检测结果' not in entry:
|
|
|
|
|
+ entry['检测结果'] = val
|
|
|
|
|
+ elif '人群百分位' not in entry:
|
|
|
|
|
+ entry['人群百分位'] = val
|
|
|
|
|
+ if '检测结果' in entry:
|
|
|
|
|
+ bacteria.append(entry)
|
|
|
|
|
+ break
|
|
|
|
|
+ if bacteria and bacteria[-1].get('拉丁名') == lat_name:
|
|
|
|
|
+ break
|
|
|
|
|
+ return bacteria
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_nutrient_from_tables(find_table_rows, find_table_val):
|
|
|
|
|
+ """从表格提取营养物质及营养素代谢评估"""
|
|
|
|
|
+ metabolism = {}
|
|
|
|
|
+ # 主要营养物质
|
|
|
|
|
+ for n in ['碳水化合物', '蛋白质', '脂肪', '矿物质']:
|
|
|
|
|
+ rows = find_table_rows(n)
|
|
|
|
|
+ if rows:
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ if n in name:
|
|
|
|
|
+ metabolism.setdefault('主要营养物质代谢', {})[n] = int(val)
|
|
|
|
|
+ # 糖类
|
|
|
|
|
+ sugar_items = ['葡萄糖(糖酵解途径)', '葡萄糖(磷酸戊糖途径)', '果糖和甘露糖',
|
|
|
|
|
+ '半乳糖', '淀粉和蔗糖', '氨基糖和核苷酸糖', '丙酮酸']
|
|
|
|
|
+ for item in sugar_items:
|
|
|
|
|
+ rows = find_table_rows(item[:3])
|
|
|
|
|
+ if rows:
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ metabolism.setdefault('糖类代谢', {})[item] = int(val)
|
|
|
|
|
+ # 脂类
|
|
|
|
|
+ for item in ['甘油酯', '甘油磷脂', '甘油三酯', '鞘脂类', '脂肪酸',
|
|
|
|
|
+ '硬脂酸', '棕榈酸', '花生四烯酸', '二十二碳六烯酸',
|
|
|
|
|
+ 'Omega-3', 'Omega-6']:
|
|
|
|
|
+ rows = find_table_rows(item[:3])
|
|
|
|
|
+ if rows:
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ metabolism.setdefault('脂类代谢', {})[item] = int(val)
|
|
|
|
|
+ # 维生素
|
|
|
|
|
+ for item in ['维生素A', '维生素B1', '维生素B2', '维生素B3', '维生素B5',
|
|
|
|
|
+ '维生素B6', '维生素B7', '维生素B12', '维生素C', '维生素D',
|
|
|
|
|
+ '维生素E', '维生素K1', '维生素K2', '维生素K3', '叶酸']:
|
|
|
|
|
+ rows = find_table_rows(item)
|
|
|
|
|
+ if rows:
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ metabolism.setdefault('维生素', {})[item] = int(val)
|
|
|
|
|
+ # 微量元素
|
|
|
|
|
+ for item in ['铁', '锌', '钙', '镁', '硒', '锰', '铜', '钴', '镍', '钼', '铬', '钒']:
|
|
|
|
|
+ rows = find_table_rows(item)
|
|
|
|
|
+ if rows:
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ if name == item:
|
|
|
|
|
+ metabolism.setdefault('微量元素', {})[item] = int(val)
|
|
|
|
|
+ # 氨基酸
|
|
|
|
|
+ for item in ['赖氨酸', '丝氨酸', '亮氨酸', '色氨酸', '苯丙氨酸',
|
|
|
|
|
+ '缬氨酸', '组氨酸', '半胱氨酸', '酪氨酸', '丙氨酸',
|
|
|
|
|
+ '脯氨酸', '苏氨酸', '谷氨酸', '异亮氨酸', '精氨酸',
|
|
|
|
|
+ '蛋氨酸', '甘氨酸', '天冬氨酸', '牛磺酸']:
|
|
|
|
|
+ rows = find_table_rows(item[:2])
|
|
|
|
|
+ if rows:
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ metabolism.setdefault('氨基酸', {})[item] = int(val)
|
|
|
|
|
+ # 神经递质
|
|
|
|
|
+ for item in ['5-羟色胺', 'γ-氨基丁酸', '多巴胺', '乙酰胆碱', '组胺', '去甲肾上腺素']:
|
|
|
|
|
+ rows = find_table_rows(item[:3])
|
|
|
|
|
+ if rows:
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ metabolism.setdefault('神经递质', {})[item] = int(val)
|
|
|
|
|
+ # 其他单项
|
|
|
|
|
+ for item, key in [('嘌呤代谢', '嘌呤代谢'), ('谷胱甘肽', '三肽(谷胱甘肽)'),
|
|
|
|
|
+ ('胆汁酸', '胆汁酸代谢'), ('硫辛酸', '抗自由基(硫辛酸)'),
|
|
|
|
|
+ ('辅酶Q', '抗自由基(辅酶Q)')]:
|
|
|
|
|
+ v = find_table_val(item)
|
|
|
|
|
+ if v: metabolism[key] = int(v)
|
|
|
|
|
+ return metabolism
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_antibiotics_from_tables(find_table_rows):
|
|
|
|
|
+ """从表格提取抗生素风险评估"""
|
|
|
|
|
+ ab = {}
|
|
|
|
|
+ for cat in ANTIBIOTIC_CATEGORIES_BJ:
|
|
|
|
|
+ rows = find_table_rows(cat, val_col=2)
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ ab[cat] = int(val)
|
|
|
|
|
+ return ab
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_toxins_from_tables(all_tables, find_table_rows):
|
|
|
|
|
+ """从表格提取毒性物质清除能力评估"""
|
|
|
|
|
+ toxins = {}
|
|
|
|
|
+ toxin_items = [
|
|
|
|
|
+ "苯甲酸酯", "对氨基苯甲酸乙酯", "对氟苯甲酸乙酯",
|
|
|
|
|
+ "氯烷烃和氯烯烃", "氯代环己烷", "氯苯",
|
|
|
|
|
+ "甲苯", "二甲苯", "硝基甲苯", "乙苯", "苯乙烯",
|
|
|
|
|
+ "阿特拉津", "己内酰胺", "双酚", "二恶英",
|
|
|
|
|
+ "萘", "多环芳烃",
|
|
|
|
|
+ ]
|
|
|
|
|
+ for item in toxin_items:
|
|
|
|
|
+ rows = find_table_rows(item[:3], name_col=1, val_col=2)
|
|
|
|
|
+ for name, val in rows:
|
|
|
|
|
+ toxins[item] = int(val)
|
|
|
|
|
+ return toxins
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_bj_overview(text):
|
|
|
|
|
+ """提取北京报告概述(统一到标准报告 overview 结构)"""
|
|
|
|
|
+ r = {}
|
|
|
|
|
+ m = re.search(r'检测编号[::\s]*(\S+)', text)
|
|
|
|
|
+ if m: r['report_number'] = m.group(1)
|
|
|
|
|
+ m = re.search(r'姓\s*名[::\s]*(\S+)', text)
|
|
|
|
|
+ if m: r['person_name'] = m.group(1)
|
|
|
|
|
+ m = re.search(r'年\s*龄[::\s]*(\S+)', text)
|
|
|
|
|
+ if m: r['age'] = m.group(1)
|
|
|
|
|
+ m = re.search(r'性\s*别[::\s]*(\S+)', text)
|
|
|
|
|
+ if m: r['gender'] = 'male' if m.group(1) in ('男', 'M') else 'female'
|
|
|
|
|
+ # 肠型
|
|
|
|
|
+ gut_type = _parse_bj_gut_type(text)
|
|
|
|
|
+ if gut_type: r['gut_type'] = gut_type
|
|
|
|
|
+ # GMHI → 健康总分
|
|
|
|
|
+ m = re.search(r'肠道微生物健康指数[::\s]*(\d+)', text)
|
|
|
|
|
+ if m: r['overallScore'] = int(m.group(1))
|
|
|
|
|
+ return r
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _parse_beijing_report(file_path: str) -> dict:
|
|
|
|
|
+ """解析北京菌群报告 PDF,输出统一结构(pdfplumber 表格+文本混合提取)"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ import pdfplumber
|
|
|
|
|
+ with pdfplumber.open(file_path) as pdf:
|
|
|
|
|
+ text = '\n'.join(page.extract_text() or '' for page in pdf.pages)
|
|
|
|
|
+ # 提取所有表格
|
|
|
|
|
+ all_tables = []
|
|
|
|
|
+ for page in pdf.pages:
|
|
|
|
|
+ tables = page.extract_tables()
|
|
|
|
|
+ for table in tables:
|
|
|
|
|
+ if table and len(table) >= 2:
|
|
|
|
|
+ all_tables.append(table)
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ from PyPDF2 import PdfReader
|
|
|
|
|
+ reader = PdfReader(file_path)
|
|
|
|
|
+ text = '\n'.join((page.extract_text() or '') for page in reader.pages)
|
|
|
|
|
+ all_tables = []
|
|
|
|
|
+
|
|
|
|
|
+ def find_table_rows(keyword, val_col=1, name_col=0, try_all=False):
|
|
|
|
|
+ """从表格中找指定关键词的数据行"""
|
|
|
|
|
+ rows = []
|
|
|
|
|
+ for table in all_tables:
|
|
|
|
|
+ for row in table:
|
|
|
|
|
+ row_str = ' '.join(str(c) for c in row if c)
|
|
|
|
|
+ if keyword in row_str:
|
|
|
|
|
+ if name_col < len(row) and val_col < len(row):
|
|
|
|
|
+ name = str(row[name_col] or '').strip()
|
|
|
|
|
+ val = str(row[val_col] or '').strip()
|
|
|
|
|
+ if name and val and val.replace('.','').replace('-','').isdigit():
|
|
|
|
|
+ rows.append((name, val))
|
|
|
|
|
+ if rows and not try_all:
|
|
|
|
|
+ break
|
|
|
|
|
+ return rows
|
|
|
|
|
+
|
|
|
|
|
+ def find_table_val(keyword, val_col=1, name_col=0):
|
|
|
|
|
+ """从表格中找单个值"""
|
|
|
|
|
+ for table in all_tables:
|
|
|
|
|
+ for row in table:
|
|
|
|
|
+ row_str = ' '.join(str(c) for c in row if c)
|
|
|
|
|
+ if keyword in row_str:
|
|
|
|
|
+ if val_col < len(row):
|
|
|
|
|
+ val = str(row[val_col] or '').strip()
|
|
|
|
|
+ if val and val.replace('.','').isdigit():
|
|
|
|
|
+ return val
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ result = {
|
|
|
|
|
+ 'format': 'beijing',
|
|
|
|
|
+ 'overview': _parse_bj_overview(text),
|
|
|
|
|
+ 'disease_risks': [],
|
|
|
|
|
+ 'nutrition': [],
|
|
|
|
|
+ 'amino_acids': [],
|
|
|
|
|
+ 'vitamins': [],
|
|
|
|
|
+ 'trace_elements': [],
|
|
|
|
|
+ '菌群检出详细列表': {},
|
|
|
|
|
+ '个体化食物推荐表': {'格式': 'not_found', '条目数': 0, '数据': []},
|
|
|
|
|
+ # 北京报告特有指标
|
|
|
|
|
+ '肠道微生物健康指数': _parse_bj_gmhi(text),
|
|
|
|
|
+ '菌群多样性': _parse_bj_diversity(text),
|
|
|
|
|
+ '肠道黏膜屏障': _parse_bj_barrier(text),
|
|
|
|
|
+ '菌群表型评估': _parse_bj_phenotype(text),
|
|
|
|
|
+ '短链脂肪酸合成能力': _parse_bj_scfa_from_tables(find_table_rows),
|
|
|
|
|
+ '肠道菌群精准分布': _parse_bj_core_bacteria_from_tables(all_tables),
|
|
|
|
|
+ '营养物质及营养素代谢评估': _parse_bj_nutrient_from_tables(find_table_rows, find_table_val),
|
|
|
|
|
+ '抗生素风险评估': _parse_bj_antibiotics_from_tables(find_table_rows),
|
|
|
|
|
+ '毒性物质清除能力评估': _parse_bj_toxins_from_tables(all_tables, find_table_rows),
|
|
|
|
|
+ '趣味肠菌评估': _parse_bj_psych_eval(text, find_table_val),
|
|
|
|
|
+ '胃肠道感染病原体': [],
|
|
|
|
|
+ '健康整体评估': {},
|
|
|
|
|
+ '肠道菌群主要检测结果': {},
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ # 从表格更新 GMHI/抗炎/免疫/纤维/多样性/屏障
|
|
|
|
|
+ gmhi_val = find_table_val('肠道微生物健康指数', val_col=1)
|
|
|
|
|
+ if gmhi_val:
|
|
|
|
|
+ result['肠道微生物健康指数']['肠道微生物健康指数'] = float(gmhi_val)
|
|
|
|
|
+ score = float(gmhi_val)
|
|
|
|
|
+ if score >= 90: result['肠道微生物健康指数']['健康状态'] = '健康'
|
|
|
|
|
+ elif score >= 80: result['肠道微生物健康指数']['健康状态'] = '健康倾向'
|
|
|
|
|
+ elif score >= 40: result['肠道微生物健康指数']['健康状态'] = '亚健康'
|
|
|
|
|
+ elif score >= 20: result['肠道微生物健康指数']['健康状态'] = '不健康倾向'
|
|
|
|
|
+ else: result['肠道微生物健康指数']['健康状态'] = '不健康'
|
|
|
|
|
+ for attr, kw in [('肠道抗炎能力', '抗炎能力'), ('肠道免疫力', '免疫力'),
|
|
|
|
|
+ ('肠道膳食纤维需求', '膳食纤维需求')]:
|
|
|
|
|
+ v = find_table_val(kw)
|
|
|
|
|
+ if v:
|
|
|
|
|
+ result.setdefault('肠道菌群主要检测结果', {})[attr] = int(v)
|
|
|
|
|
+ if '抗炎' in kw: result['overview']['inflammationScore'] = int(v)
|
|
|
|
|
+ elif '免疫' in kw: result['overview']['immunityScore'] = int(v)
|
|
|
|
|
+ # 多样性
|
|
|
|
|
+ div_val = find_table_val('肠道微生物多样性', val_col=1)
|
|
|
|
|
+ if div_val:
|
|
|
|
|
+ result['菌群多样性']['Shannon指数'] = float(div_val)
|
|
|
|
|
+ # BE比值
|
|
|
|
|
+ be_val = find_table_val('B/E比值', val_col=1)
|
|
|
|
|
+ if be_val:
|
|
|
|
|
+ result['肠道黏膜屏障']['BE比值'] = float(be_val)
|
|
|
|
|
+ bi_val = find_table_val('双歧杆菌属', val_col=1)
|
|
|
|
|
+ if bi_val:
|
|
|
|
|
+ result['肠道黏膜屏障']['双歧杆菌属'] = float(bi_val)
|
|
|
|
|
+ ent_val = find_table_val('肠杆菌科', val_col=1)
|
|
|
|
|
+ if ent_val:
|
|
|
|
|
+ result['肠道黏膜屏障']['肠杆菌科'] = float(ent_val)
|
|
|
|
|
+ # 肠型
|
|
|
|
|
+ gut_type = _parse_bj_gut_type(text)
|
|
|
|
|
+ balance = _parse_bj_balance(text)
|
|
|
|
|
+ if gut_type or balance:
|
|
|
|
|
+ result['健康整体评估'] = {}
|
|
|
|
|
+ if gut_type: result['健康整体评估']['肠型'] = gut_type
|
|
|
|
|
+ if balance: result['健康整体评估']['菌群平衡评估'] = balance
|
|
|
|
|
+
|
|
|
|
|
+ # 疾病风险评估(文本提取)
|
|
|
|
|
+ disease_risks = _parse_bj_disease_risks(text)
|
|
|
|
|
+ if disease_risks:
|
|
|
|
|
+ result['disease_risks'] = disease_risks
|
|
|
|
|
+
|
|
|
|
|
+ # 病原体
|
|
|
|
|
+ pathogens = _parse_bj_pathogens(text)
|
|
|
|
|
+ if pathogens:
|
|
|
|
|
+ result['胃肠道感染病原体'] = pathogens
|
|
|
|
|
+
|
|
|
|
|
+ # 营养物质 → 标准字段映射
|
|
|
|
|
+ metabolism = result.get('营养物质及营养素代谢评估', {})
|
|
|
|
|
+ if metabolism.get('主要营养物质代谢'):
|
|
|
|
|
+ result['nutrition'] = [
|
|
|
|
|
+ {'name': k, 'value': str(v), 'status': '正常' if v >= 60 else '偏低'}
|
|
|
|
|
+ for k, v in metabolism['主要营养物质代谢'].items()
|
|
|
|
|
+ ]
|
|
|
|
|
+ if metabolism.get('氨基酸'):
|
|
|
|
|
+ result['amino_acids'] = [
|
|
|
|
|
+ {'name': k, 'value': str(v), 'status': '正常' if v >= 60 else '偏低'}
|
|
|
|
|
+ for k, v in metabolism['氨基酸'].items()
|
|
|
|
|
+ ]
|
|
|
|
|
+ if metabolism.get('维生素'):
|
|
|
|
|
+ result['vitamins'] = [
|
|
|
|
|
+ {'name': k, 'value': str(v), 'status': '正常' if v >= 60 else '偏低'}
|
|
|
|
|
+ for k, v in metabolism['维生素'].items()
|
|
|
|
|
+ ]
|
|
|
|
|
+ if metabolism.get('微量元素'):
|
|
|
|
|
+ result['trace_elements'] = [
|
|
|
|
|
+ {'name': k, 'value': str(v), 'status': '正常' if v >= 60 else '偏低'}
|
|
|
|
|
+ for k, v in metabolism['微量元素'].items()
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ return result
|