AI 回复新增用药速览,中药/西药药名摘出并高亮
- 新增 ai_consult_drug_names:中药饮片、方剂/中成药、西药显式词表,长词优先识别,西药按通用名去重 - AI 回复标题下新增“用药速览”卡片:中药绿、西药蓝色块 - 正文药名同色浅底加粗高亮,剂量数字不着色;Markdown 回退路径同样保留底色 - 新增药名识别测试,更新药物剂量着色测试 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,7 @@ from shiboken6 import isValid
|
||||
from .. import icons
|
||||
from ..chat_items import parse_chat_item as _parse_chat_item
|
||||
from ..diagnosis_drawer import CaseGrid, _RemoteImageButton
|
||||
from ..diagnosis_index_widgets import FlowWidget
|
||||
from ..diagnosis_media import (
|
||||
ImagePreviewDialog,
|
||||
is_previewable_image,
|
||||
@@ -100,6 +101,7 @@ from .ai_consult_metric_state import (
|
||||
combined_metric_state,
|
||||
risk_color,
|
||||
)
|
||||
from .ai_consult_drug_names import TCM as DRUG_TCM, WESTERN as DRUG_WESTERN, summarize_drugs
|
||||
from .ai_consult_metric_text import MetricTextState, metric_text_context, metric_text_html
|
||||
from .ai_consult_report_visuals import (
|
||||
ReportVisual,
|
||||
@@ -812,6 +814,51 @@ QDialog#AiConsultDialog QLabel#AiConsultSideRequestStatus[tone="failed"] {
|
||||
background-color: #FFF1F3;
|
||||
border-color: #F5C2C7;
|
||||
}
|
||||
QDialog#AiConsultDialog QFrame#AiConsultDrugStrip {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #E3E6EF;
|
||||
border-radius: 12px;
|
||||
}
|
||||
QDialog#AiConsultDialog QLabel#AiConsultDrugStripTitle {
|
||||
color: #1A1C1F;
|
||||
background-color: transparent;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
QDialog#AiConsultDialog QLabel#AiConsultDrugStripMeta {
|
||||
color: #8A8B8D;
|
||||
background-color: transparent;
|
||||
font-size: 12px;
|
||||
}
|
||||
QDialog#AiConsultDialog QLabel#AiConsultDrugKind {
|
||||
min-width: 40px;
|
||||
max-width: 40px;
|
||||
min-height: 26px;
|
||||
max-height: 26px;
|
||||
color: #FFFFFF;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
QDialog#AiConsultDialog QLabel#AiConsultDrugKind[kind="tcm"] { background-color: #1F7A4D; }
|
||||
QDialog#AiConsultDialog QLabel#AiConsultDrugKind[kind="western"] { background-color: #3347B8; }
|
||||
QDialog#AiConsultDialog QLabel#AiConsultDrugChip {
|
||||
min-height: 24px;
|
||||
padding: 0 10px;
|
||||
border-radius: 7px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
QDialog#AiConsultDialog QLabel#AiConsultDrugChip[kind="tcm"] {
|
||||
color: #1F7A4D;
|
||||
background-color: #EAF7F0;
|
||||
border: 1px solid #BFE6D0;
|
||||
}
|
||||
QDialog#AiConsultDialog QLabel#AiConsultDrugChip[kind="western"] {
|
||||
color: #3347B8;
|
||||
background-color: #EEF1FA;
|
||||
border: 1px solid #C9D1F3;
|
||||
}
|
||||
QDialog#AiConsultDialog QLabel#AiConsultSideRequestStatus[tone="cancelled"] {
|
||||
color: #9A5B00;
|
||||
background-color: #FFF7E6;
|
||||
@@ -2682,6 +2729,9 @@ def _highlight_document_metrics(document: QTextDocument) -> None:
|
||||
style = QTextCharFormat()
|
||||
style.setForeground(foreground)
|
||||
style.setFontWeight(fragment.charFormat().fontWeight())
|
||||
background = fragment.charFormat().background()
|
||||
if background.style() != Qt.BrushStyle.NoBrush:
|
||||
style.setBackground(background)
|
||||
cursor.setPosition(fragment.position())
|
||||
cursor.setPosition(fragment.position() + fragment.length(), QTextCursor.MoveMode.KeepAnchor)
|
||||
cursor.mergeCharFormat(style)
|
||||
@@ -2712,6 +2762,59 @@ def split_report_lead(item: str) -> tuple[str, str]:
|
||||
return label, body
|
||||
|
||||
|
||||
class _DrugSummaryStrip(QFrame):
|
||||
"""用药速览:把回复中提到的中药、西药摘成色块,医生不读正文也能一眼看到用药。"""
|
||||
|
||||
def __init__(self, summary: Mapping[str, Sequence[str]], parent: QWidget | None = None) -> None:
|
||||
super().__init__(parent)
|
||||
self.setObjectName("AiConsultDrugStrip")
|
||||
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum)
|
||||
_style_surface(self)
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(14, 11, 14, 12)
|
||||
layout.setSpacing(8)
|
||||
|
||||
total = sum(len(names) for names in summary.values())
|
||||
head = QHBoxLayout()
|
||||
head.setContentsMargins(0, 0, 0, 0)
|
||||
head.setSpacing(8)
|
||||
title = QLabel("用药速览", self)
|
||||
title.setObjectName("AiConsultDrugStripTitle")
|
||||
head.addWidget(title)
|
||||
count = QLabel(f"共 {total} 种,正文中同色高亮", self)
|
||||
count.setObjectName("AiConsultDrugStripMeta")
|
||||
head.addWidget(count)
|
||||
head.addStretch(1)
|
||||
layout.addLayout(head)
|
||||
|
||||
for kind, label in ((DRUG_TCM, "中药"), (DRUG_WESTERN, "西药")):
|
||||
names = list(summary.get(kind) or ())
|
||||
if not names:
|
||||
continue
|
||||
row = QHBoxLayout()
|
||||
row.setContentsMargins(0, 0, 0, 0)
|
||||
row.setSpacing(10)
|
||||
tag = QLabel(label, self)
|
||||
tag.setObjectName("AiConsultDrugKind")
|
||||
tag.setProperty("kind", kind)
|
||||
tag.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
row.addWidget(tag, 0, Qt.AlignmentFlag.AlignTop)
|
||||
chips = FlowWidget(self, horizontal_spacing=6, vertical_spacing=6)
|
||||
for name in names:
|
||||
chip = QLabel(name, chips)
|
||||
chip.setObjectName("AiConsultDrugChip")
|
||||
chip.setProperty("kind", kind)
|
||||
chip.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
|
||||
chips.flow.addWidget(chip)
|
||||
row.addWidget(chips, 1)
|
||||
layout.addLayout(row)
|
||||
|
||||
@staticmethod
|
||||
def for_text(text: str, parent: QWidget | None = None) -> "_DrugSummaryStrip | None":
|
||||
summary = summarize_drugs(text)
|
||||
return _DrugSummaryStrip(summary, parent) if any(summary.values()) else None
|
||||
|
||||
|
||||
class _StructuredReportPanel(QWidget):
|
||||
"""Scan-first report for any sectioned reply.
|
||||
|
||||
@@ -3645,6 +3748,7 @@ class _ChatBubble(QWidget):
|
||||
parent=self._bubble_frame,
|
||||
)
|
||||
self._reset_clinical_panel()
|
||||
drug_strip = _DrugSummaryStrip.for_text(self._raw_payload)
|
||||
if panel is None:
|
||||
document = self.body.document()
|
||||
layout_enabled = document.isLayoutEnabled()
|
||||
@@ -3660,9 +3764,17 @@ class _ChatBubble(QWidget):
|
||||
visual = _report_visual_widget(report_visuals, self._bubble_frame)
|
||||
self._standalone_visuals.append(visual)
|
||||
self._inner.insertWidget(1, visual)
|
||||
if drug_strip is not None:
|
||||
drug_strip.setParent(self._bubble_frame)
|
||||
self._standalone_visuals.append(drug_strip)
|
||||
self._inner.insertWidget(1, drug_strip)
|
||||
return False
|
||||
if report_visuals:
|
||||
panel.layout().insertWidget(1, _report_visual_widget(report_visuals, panel))
|
||||
# 用药速览紧跟标题行,排在图表与正文之前。
|
||||
if drug_strip is not None:
|
||||
drug_strip.setParent(panel)
|
||||
panel.layout().insertWidget(1, drug_strip)
|
||||
panel.setParent(self._bubble_frame)
|
||||
self._clinical_panel = panel
|
||||
self._role_name.hide()
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
"""从 AI 回复中摘出药名(中药 / 西药),供用药速览与正文高亮使用。
|
||||
|
||||
只用显式词表匹配,不按“…汤/…片”等后缀猜测,宁可漏标也不把普通词误标成药。
|
||||
匹配按长词优先,所以「六味地黄丸」不会被拆成「地黄」。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import NamedTuple
|
||||
|
||||
TCM = "tcm"
|
||||
WESTERN = "western"
|
||||
|
||||
# 中药绿、西药蓝;与侧栏状态卡的完成/进行中色一致。
|
||||
DRUG_COLORS = {
|
||||
TCM: ("#1F7A4D", "#EAF7F0", "#BFE6D0"),
|
||||
WESTERN: ("#3347B8", "#EEF1FA", "#C9D1F3"),
|
||||
}
|
||||
DRUG_STYLES = {
|
||||
kind: f"color: {fg}; background-color: {bg}; font-weight: 700;"
|
||||
for kind, (fg, bg, _border) in DRUG_COLORS.items()
|
||||
}
|
||||
|
||||
# 常用中药饮片(两字及以上,避免单字误配)。
|
||||
_TCM_HERBS = """
|
||||
黄芪 党参 人参 西洋参 太子参 红参 白术 苍术 茯苓 茯神 土茯苓 猪苓 泽泻 薏苡仁 甘草 炙甘草
|
||||
当归 川芎 白芍 赤芍 熟地黄 生地黄 熟地 生地 地黄 山茱萸 山萸肉 牡丹皮 丹皮 丹参 三七 红花 桃仁
|
||||
黄连 黄芩 黄柏 栀子 知母 石膏 天花粉 葛根 麦冬 天冬 玉竹 黄精 石斛 沙参 北沙参 南沙参 百合
|
||||
五味子 枸杞子 女贞子 墨旱莲 菟丝子 桑椹 覆盆子 金樱子 芡实 怀山药 鸡内金 神曲 麦芽 山楂
|
||||
陈皮 青皮 枳实 枳壳 厚朴 半夏 法半夏 姜半夏 竹茹 干姜 炮姜 桂枝 肉桂 附子 制附子
|
||||
柴胡 升麻 防风 荆芥 羌活 独活 秦艽 威灵仙 桑枝 桑叶 菊花 薄荷 蝉蜕 牛蒡子 连翘 金银花 蒲公英
|
||||
板蓝根 大青叶 鱼腥草 败酱草 白花蛇舌草 夏枯草 决明子 车前子 车前草 木通 通草 滑石 萹蓄 瞿麦
|
||||
茵陈 金钱草 虎杖 大黄 芒硝 火麻仁 郁李仁 杏仁 苦杏仁 桔梗 紫菀 款冬花 百部 川贝母 浙贝母 贝母
|
||||
瓜蒌 全瓜蒌 薤白 远志 酸枣仁 柏子仁 夜交藤 首乌藤 合欢皮 龙骨 牡蛎 珍珠母 磁石 天麻 钩藤 石决明
|
||||
地龙 全蝎 蜈蚣 僵蚕 水蛭 土鳖虫 鸡血藤 牛膝 怀牛膝 川牛膝 杜仲 续断 桑寄生 狗脊 骨碎补 补骨脂
|
||||
淫羊藿 仙茅 巴戟天 肉苁蓉 锁阳 益智仁 乌药 小茴香 吴茱萸 木香 香附 郁金 延胡索 川楝子 佛手
|
||||
砂仁 白豆蔻 豆蔻 草豆蔻 藿香 佩兰 苍耳子 辛夷 白芷 细辛 麻黄 紫苏 苏叶 紫苏叶 苏子 紫苏子
|
||||
乌梅 五倍子 诃子 浮小麦 麻黄根 山茱萸肉 何首乌 制何首乌 阿胶 鹿角胶 龟甲 鳖甲 玄参 牛黄 冰片
|
||||
麝香 苏合香 石菖蒲 益母草 泽兰 王不留行 莪术 三棱 乳香 没药 五灵脂 蒲黄 茜草 白茅根 侧柏叶
|
||||
仙鹤草 艾叶 地榆 槐花 小蓟 大蓟 白及 荷叶 绞股蓝 桑白皮 地骨皮 青蒿 银柴胡 胡黄连 白薇
|
||||
苦参 白鲜皮 地肤子 蛇床子 黄药子 海藻 昆布 荔枝核 橘核 鬼箭羽 翻白草 玉米须 冬瓜皮 赤小豆
|
||||
""".split()
|
||||
|
||||
# 常用方剂与中成药(含糖尿病常用)。
|
||||
_TCM_FORMULAS = """
|
||||
六味地黄丸 知柏地黄丸 杞菊地黄丸 麦味地黄丸 金匮肾气丸 济生肾气丸 桂附地黄丸 左归丸 右归丸
|
||||
玉泉丸 消渴丸 参芪降糖颗粒 津力达颗粒 天芪降糖胶囊 渴乐宁胶囊 金芪降糖片 降糖舒胶囊 糖脉康颗粒
|
||||
芪蛭降糖胶囊 木丹颗粒 通心络胶囊 复方丹参滴丸 银杏叶片 血塞通 血栓通 丹参酮 脑心通胶囊
|
||||
补中益气丸 补中益气汤 归脾丸 归脾汤 逍遥丸 逍遥散 加味逍遥丸 丹栀逍遥散 柴胡疏肝散 四逆散
|
||||
四君子汤 六君子汤 香砂六君子汤 参苓白术散 理中丸 附子理中丸 四物汤 八珍汤 十全大补汤 当归补血汤
|
||||
生脉饮 生脉散 玉女煎 白虎加人参汤 白虎汤 葛根芩连汤 黄连解毒汤 半夏泻心汤 大柴胡汤 小柴胡汤
|
||||
血府逐瘀汤 补阳还五汤 桃红四物汤 二陈汤 温胆汤 黄连温胆汤 天麻钩藤饮 镇肝熄风汤 酸枣仁汤
|
||||
天王补心丹 一贯煎 增液汤 沙参麦冬汤 益胃汤 竹叶石膏汤 五苓散 猪苓汤 真武汤 实脾饮 防己黄芪汤
|
||||
玉屏风散 桂枝汤 麻黄汤 银翘散 桑菊饮 藿香正气散 藿香正气水 保和丸 平胃散 二妙丸 三妙丸 四妙丸
|
||||
当归四逆汤 黄芪桂枝五物汤 独活寄生汤 身痛逐瘀汤 大补阴丸 二至丸 交泰丸 安宫牛黄丸 浓缩水丸
|
||||
""".split()
|
||||
|
||||
# 西药(以内分泌、心血管、常见合并用药为主)。
|
||||
_WESTERN = """
|
||||
二甲双胍 盐酸二甲双胍 格列美脲 格列齐特 格列吡嗪 格列本脲 格列喹酮 瑞格列奈 那格列奈 阿卡波糖
|
||||
伏格列波糖 米格列醇 吡格列酮 罗格列酮 西格列汀 沙格列汀 维格列汀 利格列汀 阿格列汀 达格列净
|
||||
恩格列净 卡格列净 艾托格列净 利拉鲁肽 司美格鲁肽 度拉糖肽 艾塞那肽 利司那肽 贝那鲁肽 替尔泊肽
|
||||
胰岛素 门冬胰岛素 赖脯胰岛素 谷赖胰岛素 甘精胰岛素 地特胰岛素 德谷胰岛素 精蛋白锌胰岛素
|
||||
重组人胰岛素 生物合成人胰岛素 预混胰岛素 硝苯地平 氨氯地平 左旋氨氯地平 非洛地平 尼群地平
|
||||
贝尼地平 厄贝沙坦 缬沙坦 氯沙坦 替米沙坦 坎地沙坦 奥美沙坦 沙库巴曲缬沙坦 依那普利 贝那普利
|
||||
培哚普利 卡托普利 福辛普利 美托洛尔 比索洛尔 卡维地洛 阿替洛尔 氢氯噻嗪 吲达帕胺 呋塞米
|
||||
螺内酯 托拉塞米 阿托伐他汀 瑞舒伐他汀 辛伐他汀 普伐他汀 匹伐他汀 依折麦布 非诺贝特 苯扎贝特
|
||||
阿司匹林 氯吡格雷 替格瑞洛 华法林 利伐沙班 达比加群 硫酸氢氯吡格雷 甲钴胺 依帕司他 硫辛酸
|
||||
α-硫辛酸 胰激肽原酶 羟苯磺酸钙 前列地尔 贝前列素钠 别嘌醇 非布司他 苯溴马隆 碳酸氢钠
|
||||
左甲状腺素 甲巯咪唑 丙硫氧嘧啶 奥美拉唑 泮托拉唑 雷贝拉唑 埃索美拉唑 雷尼替丁 法莫替丁
|
||||
多潘立酮 莫沙必利 二甲硅油 乳果糖 阿莫西林 头孢呋辛 左氧氟沙星 莫西沙星 阿奇霉素 布洛芬
|
||||
对乙酰氨基酚 塞来昔布 加巴喷丁 普瑞巴林 碳酸钙 骨化三醇 维生素D 维生素B12 叶酸 硝酸甘油
|
||||
单硝酸异山梨酯 曲美他嗪 地高辛 胺碘酮 坦索罗辛 非那雄胺 西地那非 他达拉非
|
||||
""".split()
|
||||
|
||||
# 西药常带剂型后缀;一起摘出,展示时保留原文写法。
|
||||
_WESTERN_FORM = r"(?:缓释片|控释片|肠溶片|分散片|片|缓释胶囊|肠溶胶囊|胶囊|注射液|注射剂|笔|颗粒)?"
|
||||
|
||||
|
||||
class DrugMention(NamedTuple):
|
||||
start: int
|
||||
end: int
|
||||
# 去掉剂型后缀的药名,用于速览去重;高亮范围仍覆盖原文中的完整写法。
|
||||
name: str
|
||||
kind: str
|
||||
|
||||
|
||||
def _alternation(names: list[str]) -> str:
|
||||
return "|".join(re.escape(name) for name in sorted(set(names), key=len, reverse=True))
|
||||
|
||||
|
||||
# 单一正则、长词优先:中成药 > 西药 > 饮片,保证一个位置只被归入一类。
|
||||
_DRUG_RE = re.compile(
|
||||
rf"(?P<tcm_formula>{_alternation(_TCM_FORMULAS)})"
|
||||
rf"|(?P<western>(?P<western_base>{_alternation(_WESTERN)}){_WESTERN_FORM})"
|
||||
rf"|(?P<herb>{_alternation(_TCM_HERBS)})"
|
||||
)
|
||||
|
||||
|
||||
def find_drug_mentions(text: str) -> list[DrugMention]:
|
||||
"""返回正文中每一处药名的位置与类别(不重叠、按出现顺序)。"""
|
||||
|
||||
mentions: list[DrugMention] = []
|
||||
for match in _DRUG_RE.finditer(str(text or "")):
|
||||
if match.group("western"):
|
||||
mentions.append(DrugMention(match.start(), match.end(), match.group("western_base"), WESTERN))
|
||||
else:
|
||||
mentions.append(DrugMention(match.start(), match.end(), match.group(), TCM))
|
||||
return mentions
|
||||
|
||||
|
||||
def summarize_drugs(text: str) -> dict[str, list[str]]:
|
||||
"""按类别去重汇总药名,保留首次出现的顺序。"""
|
||||
|
||||
summary: dict[str, list[str]] = {TCM: [], WESTERN: []}
|
||||
for mention in find_drug_mentions(text):
|
||||
names = summary[mention.kind]
|
||||
if mention.name not in names:
|
||||
names.append(mention.name)
|
||||
return summary
|
||||
@@ -15,6 +15,8 @@ from contextvars import ContextVar
|
||||
from dataclasses import dataclass
|
||||
from html import escape
|
||||
|
||||
from .ai_consult_drug_names import DRUG_STYLES, find_drug_mentions
|
||||
|
||||
from .ai_consult_metric_state import (
|
||||
ABNORMAL_COLOR,
|
||||
NORMAL_COLOR,
|
||||
@@ -393,18 +395,23 @@ def metric_text_html(text: str, *, metric_label: str = "") -> str:
|
||||
if metric_label and _MISSING_PLACEHOLDER_RE.fullmatch(text):
|
||||
spans.append((0, len(text), UNKNOWN_COLOR))
|
||||
|
||||
# 药名高亮:中药绿、西药蓝,浅底加粗,让医生在正文里一眼看到用药。
|
||||
styled = [(start, end, f"color: {color}; font-weight: 600;") for start, end, color in spans]
|
||||
styled.extend(
|
||||
(mention.start, mention.end, DRUG_STYLES[mention.kind])
|
||||
for mention in find_drug_mentions(text)
|
||||
)
|
||||
|
||||
# Prefer a full reading over a contained unitless value, then consume each
|
||||
# original character once. Generated markup is never scanned by a regex.
|
||||
spans.sort(key=lambda span: (span[0], -(span[1] - span[0])))
|
||||
styled.sort(key=lambda span: (span[0], -(span[1] - span[0])))
|
||||
pieces = ['<span style="white-space: pre-wrap;">']
|
||||
position = 0
|
||||
for start, end, color in spans:
|
||||
for start, end, style in styled:
|
||||
if start < position:
|
||||
continue
|
||||
pieces.append(escape(text[position:start]))
|
||||
pieces.append(
|
||||
f'<span style="color: {color}; font-weight: 600;">{escape(text[start:end])}</span>'
|
||||
)
|
||||
pieces.append(f'<span style="{style}">{escape(text[start:end])}</span>')
|
||||
position = end
|
||||
pieces.append(escape(text[position:]))
|
||||
pieces.append("</span>")
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from doctor_workstation.ui.dialogs.ai_consult_drug_names import (
|
||||
TCM,
|
||||
WESTERN,
|
||||
find_drug_mentions,
|
||||
summarize_drugs,
|
||||
)
|
||||
|
||||
|
||||
def test_formula_wins_over_contained_herb() -> None:
|
||||
mentions = find_drug_mentions("可参考六味地黄丸加减")
|
||||
assert [(m.name, m.kind) for m in mentions] == [("六味地黄丸", TCM)]
|
||||
|
||||
|
||||
def test_western_dosage_form_is_highlighted_but_deduplicated_by_base_name() -> None:
|
||||
text = "在用二甲双胍胶囊,二甲双胍早晚各1片,加用阿卡波糖"
|
||||
mentions = find_drug_mentions(text)
|
||||
assert text[mentions[0].start:mentions[0].end] == "二甲双胍胶囊"
|
||||
assert summarize_drugs(text) == {TCM: [], WESTERN: ["二甲双胍", "阿卡波糖"]}
|
||||
|
||||
|
||||
def test_herbs_and_western_drugs_are_split_by_kind() -> None:
|
||||
summary = summarize_drugs("黄芪、丹参益气活血;格列美脲注意低血糖;黄芪可加量")
|
||||
assert summary == {TCM: ["黄芪", "丹参"], WESTERN: ["格列美脲"]}
|
||||
|
||||
|
||||
def test_plain_text_and_diet_words_are_not_drugs() -> None:
|
||||
assert summarize_drugs("建议多吃山药、大枣,少吃甜食,规律运动") == {TCM: [], WESTERN: []}
|
||||
@@ -118,7 +118,6 @@ def test_current_readings_after_targets_do_not_keep_reference_color() -> None:
|
||||
"诊单ID:123456;patient_id=1501;流水ABC123456;电话13800138000",
|
||||
"2026-08-18,2026/08/19,08:30,第1章,1. 检查,2. 处理",
|
||||
"病程3年,持续2周,随访7天,每次30分钟",
|
||||
"二甲双胍500mg,每日2次;胰岛素10U;阿司匹林100 mg",
|
||||
"剂量0.2mg/kg、浓度5mg/mL,外用2%利多卡因",
|
||||
"统计显示80%的患者改善20%,目标完成率90%,报销比例50%",
|
||||
"BMI2026-08-18;eGFR2026/08/19;heart_rate2026-08-18",
|
||||
@@ -131,6 +130,17 @@ def test_unrelated_numbers_and_drug_doses_remain_unemphasized(text: str) -> None
|
||||
assert "color:" not in rendered
|
||||
|
||||
|
||||
def test_drug_names_are_highlighted_but_their_doses_stay_plain() -> None:
|
||||
text = "二甲双胍500mg,每日2次;胰岛素10U;阿司匹林100 mg;黄芪30g"
|
||||
rendered = metric_text_html(text)
|
||||
for name in ("二甲双胍", "胰岛素", "阿司匹林"):
|
||||
assert f'background-color: #EEF1FA; font-weight: 700;">{name}</span>' in rendered
|
||||
assert 'background-color: #EAF7F0; font-weight: 700;">黄芪</span>' in rendered
|
||||
for dose in ("500mg", "10U", "100 mg", "30g"):
|
||||
assert f'font-weight: 600;">{dose}' not in rendered
|
||||
assert f'font-weight: 700;">{dose}' not in rendered
|
||||
|
||||
|
||||
def test_explicit_statuses_are_emphasized_without_classifying_the_numbers() -> None:
|
||||
document = _document("血糖6.0 mmol/L未达标;血压140/90mmHg正常;血糖偏高;检查异常")
|
||||
assert _color(document, "6.0 mmol/L") == "#BD4B58"
|
||||
|
||||
Reference in New Issue
Block a user