|
|
@@ -356,7 +356,15 @@ def extract_all_triplet(all_lines):
|
|
|
if all_lines[i] == '抗生素风险评估':
|
|
|
j = i + 1
|
|
|
abx_rows, _ = parse_triplet_until(all_lines[j:], ['抗生素耐药风险', '个体化食物推荐表'])
|
|
|
- result['抗生素风险评估'] = abx_rows
|
|
|
+ # 去重:同名只保留第一条
|
|
|
+ seen = set()
|
|
|
+ deduped = []
|
|
|
+ for r in abx_rows:
|
|
|
+ n = r.get('名称', '')
|
|
|
+ if n and n not in seen:
|
|
|
+ seen.add(n)
|
|
|
+ deduped.append(r)
|
|
|
+ result['抗生素风险评估'] = deduped
|
|
|
break
|
|
|
|
|
|
return result
|
|
|
@@ -644,7 +652,7 @@ def extract_pathogens_inline(text):
|
|
|
# ==========================================
|
|
|
BACTERIA_TABLE_TITLES = [
|
|
|
'核心菌属构成表', '益生菌', '有害菌属构成表',
|
|
|
- '其它重要菌属构成表', '病原菌属构成表', '病原菌',
|
|
|
+ '其它重要菌属构成表', '病原菌属构成表',
|
|
|
]
|
|
|
PHYLUM_TABLE_TITLES = ['菌门构成表', '菌群门水平构成表', '门水平菌群构成']
|
|
|
CLASS_TABLE_TITLES = ['菌纲构成表', '菌群纲水平构成表', '纲水平菌群构成']
|
|
|
@@ -778,6 +786,13 @@ def _parse_bacteria_table(reader, pages_text, full_text, title, fmt, skip_header
|
|
|
if sidx == -1:
|
|
|
return results
|
|
|
|
|
|
+ # 病原菌检出表特殊处理:找"仅列出检出的病原菌"(跳过前面的说明文字中的"病原菌")
|
|
|
+ if title == '病原菌':
|
|
|
+ # 搜索更精确的表头
|
|
|
+ better_sidx = full_text.find('仅列出检出的病原菌')
|
|
|
+ if better_sidx != -1:
|
|
|
+ sidx = better_sidx
|
|
|
+
|
|
|
# 找表格结束位置(下一个标题或页尾)
|
|
|
end_pos = len(full_text)
|
|
|
for t in BACTERIA_TABLE_TITLES + PHYLUM_TABLE_TITLES + CLASS_TABLE_TITLES + ORDER_TABLE_TITLES + FAMILY_TABLE_TITLES + GENUS_TABLE_TITLES + SPECIES_TABLE_TITLES + DISEASE_BACTERIA_TITLES + ['指标范围', '个体化食物推荐表', '报告总结', '健康总分']:
|
|
|
@@ -788,7 +803,13 @@ def _parse_bacteria_table(reader, pages_text, full_text, title, fmt, skip_header
|
|
|
|
|
|
region = full_text[sidx:end_pos]
|
|
|
|
|
|
- if fmt == 'triplet':
|
|
|
+ # 检测区域实际格式:如果换行数很少(<3)则是inline格式,即使全局fmt=triplet
|
|
|
+ lines_from_region = [l.strip() for l in region.split('\n') if l.strip()]
|
|
|
+ actual_fmt = fmt
|
|
|
+ if fmt == 'triplet' and len(lines_from_region) <= 5:
|
|
|
+ actual_fmt = 'inline'
|
|
|
+
|
|
|
+ if actual_fmt == 'triplet':
|
|
|
# 三元组格式:每个字段单独一行
|
|
|
lines = [l.strip() for l in region.split('\n') if l.strip()]
|
|
|
# 找到数据开始的位置(跳过标题行)
|
|
|
@@ -859,12 +880,58 @@ def _parse_bacteria_table(reader, pages_text, full_text, title, fmt, skip_header
|
|
|
else:
|
|
|
i += 1
|
|
|
else:
|
|
|
- # inline 格式:数据挤在一行且数字段无分隔符,无法可靠自动解析
|
|
|
- # 见 朱评估报告 page 23-26,格式为 名称+丰度+范围+水平%+检出率% 无缝拼接
|
|
|
- # 示例:梭菌属 Clostridium1.00450.0306-6.961566%99.52%
|
|
|
- # 其中 1.00450.0306 无法区分 1.0045 + 0.0306 vs 1.004 + 50.0306
|
|
|
- # 跳过自动提取,用户可参考 raw text
|
|
|
- pass
|
|
|
+ # inline 格式:数据挤在一行,如 "嗜血杆菌属 Haemophilus ND 0-0.7752 22% 94.23% 说明:..."
|
|
|
+ # 适用于 pathogen 表、pathogen 检出表等 inline 格式嵌在 triplet PDF 中的情况
|
|
|
+ # 使用 re.finditer 扫描整段文本,不依赖换行分割
|
|
|
+ # 先清理标题行和说明文字
|
|
|
+ region_clean = region
|
|
|
+ for hdr in ['名称', '丰度%', '正常范围%', '处于人群%水平', '%正常人有检出', '人群水平%', '%人检出']:
|
|
|
+ region_clean = region_clean.replace(hdr, '')
|
|
|
+ # 移除说明文本(非数据行的说明段)
|
|
|
+ region_clean = re.sub(r'说明:[\u4e00-\u9fff\s,。、;:,.;:()()、/a-zA-Z0-9\-]{10,}?(?=[\u4e00-\u9fff]|$)', '', region_clean)
|
|
|
+ # 扫描所有匹配的数据行
|
|
|
+ # 模式1:中文名[(英文备注)] 英文名 丰度(ND/数字) 正常范围(可选) 人群水平% 检出率%
|
|
|
+ for m in re.finditer(
|
|
|
+ r'([\u4e00-\u9fff]{2,12}(?:[((][\u4e00-\u9fff\w]+[))])?)\s+' # 中文名
|
|
|
+ r'(?:[A-Z][a-z]+(?:\s[A-Z][a-z]+)*\s+)?' # 可选英文名
|
|
|
+ r'(ND|[\d]+\.?[\d]*%?)\s+' # 丰度
|
|
|
+ r'([\d]+\.?[\d]*-[\d]+\.?[\d]*)?\s*' # 可选正常范围
|
|
|
+ r'(\d+\.?\d*%?)\s+' # 人群水平%
|
|
|
+ r'(\d+\.?\d*%)', # 检出率%
|
|
|
+ region_clean):
|
|
|
+ name = m.group(1).strip()
|
|
|
+ pct = m.group(2)
|
|
|
+ if '病原菌' in name or '构成表' in name or '说明' in name or len(name) <= 1:
|
|
|
+ continue
|
|
|
+ normal_range = m.group(3) or ''
|
|
|
+ pop_level = m.group(4)
|
|
|
+ if not pop_level.endswith('%'):
|
|
|
+ pop_level += '%'
|
|
|
+ detection_rate = m.group(5)
|
|
|
+ entry = {'名称': name, '丰度%': pct}
|
|
|
+ if normal_range:
|
|
|
+ entry['正常范围%'] = normal_range
|
|
|
+ entry['人群水平%'] = pop_level
|
|
|
+ entry['检出率%'] = detection_rate
|
|
|
+ results.append(entry)
|
|
|
+ # 如果模式1没有匹配,尝试模式2:仅中文名+丰度+人群水平(+检出率)
|
|
|
+ if not results:
|
|
|
+ for m in re.finditer(
|
|
|
+ r'([\u4e00-\u9fff]{2,10}[\u4e00-\u9fff]?)\s+'
|
|
|
+ r'(ND|[\d]+\.?[\d]*%?)\s+'
|
|
|
+ r'(\d+\.?\d*%)\s+'
|
|
|
+ r'(\d+\.?\d*%)?',
|
|
|
+ region_clean):
|
|
|
+ name = m.group(1).strip()
|
|
|
+ pct = m.group(2)
|
|
|
+ if '病原菌' in name or '构成表' in name or '说明' in name or len(name) <= 1:
|
|
|
+ continue
|
|
|
+ pop_level = m.group(3)
|
|
|
+ detection_rate = m.group(4) or ''
|
|
|
+ entry = {'名称': name, '丰度%': pct, '人群水平%': pop_level}
|
|
|
+ if detection_rate:
|
|
|
+ entry['检出率%'] = detection_rate
|
|
|
+ results.append(entry)
|
|
|
|
|
|
return results
|
|
|
|