fix: the imperative detector only ever saw sentence starts
'When delay is suspected, obtain a detailed history' is as much an instruction to the reader as 'Obtain a detailed history', and the first pass could not see it — so the 819 I reported cleared was the count of one kind. 71 more were buried mid-sentence, after a comma or a conjunction. The detector now finds both, and export can be limited to particular variants so a pass does not collide with one already running over another view. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
This commit is contained in:
parent
a5f5e3a536
commit
efa0af2244
1 changed files with 26 additions and 10 deletions
|
|
@ -38,24 +38,38 @@ from app.models.article import Article
|
||||||
|
|
||||||
MARKER = re.compile(r"\[\[(\d+)\|([^\]]+)\]\]")
|
MARKER = re.compile(r"\[\[(\d+)\|([^\]]+)\]\]")
|
||||||
NUMBER = re.compile(r"\d+(?:\.\d+)?")
|
NUMBER = re.compile(r"\d+(?:\.\d+)?")
|
||||||
#: Verbs that open a sentence or a bullet as an instruction to the reader.
|
_VERBS = (r"Obtain|Order|Check|Give|Start|Administer|Consider|Assess|Evaluate|Perform|"
|
||||||
IMPERATIVE = re.compile(
|
r"Measure|Repeat|Refer|Avoid|Ensure|Monitor|Treat|Begin|Initiate|Stop|Use|Send|"
|
||||||
r"(?:(?<=^)|(?<=[.!?] )|(?<=^- )|(?<=^\* )|(?<=\n- )|(?<=\n\* ))"
|
r"Screen|Look|Ask|Confirm|Rule out|Do not|Don't|Reserve|Counsel|Admit|Discharge|"
|
||||||
r"(Obtain|Order|Check|Give|Start|Administer|Consider|Assess|Evaluate|Perform|"
|
r"Suspect|Exclude|Document|Reassess|Titrate|Correct|Repair|Remove|Apply|Prescribe")
|
||||||
r"Measure|Repeat|Refer|Avoid|Ensure|Monitor|Treat|Begin|Initiate|Stop|Use|Send|"
|
|
||||||
r"Screen|Look|Ask|Confirm|Rule out|Do not|Don't|Reserve|Counsel|Admit|Discharge|"
|
#: An instruction to the reader opening a sentence or a bullet.
|
||||||
r"Suspect|Exclude|Document|Reassess|Titrate|Correct|Repair|Remove|Apply|Prescribe)\b")
|
IMPERATIVE_START = re.compile(
|
||||||
|
r"(?:(?<=^)|(?<=[.!?] )|(?<=^- )|(?<=^\* )|(?<=\n- )|(?<=\n\* ))(" + _VERBS + r")\b")
|
||||||
|
|
||||||
|
#: And one buried mid-sentence, after a clause break — "…is suspected, obtain a
|
||||||
|
#: detailed history". These are the ones the first pass missed entirely,
|
||||||
|
#: because it only ever looked at what a sentence started with.
|
||||||
|
IMPERATIVE_MID = re.compile(
|
||||||
|
r"(?:,|;|\band\b|\bthen\b|\bor\b)\s+(" + _VERBS + r")\b\s+"
|
||||||
|
r"(?:a|an|the|for|all|both|age|serum|blood|urine|IV|oral)\b", re.I)
|
||||||
|
|
||||||
|
|
||||||
|
def imperatives(text: str) -> list[str]:
|
||||||
|
return IMPERATIVE_START.findall(text) + IMPERATIVE_MID.findall(text)
|
||||||
|
|
||||||
MIN_RATIO, MAX_RATIO = 0.7, 1.4
|
MIN_RATIO, MAX_RATIO = 0.7, 1.4
|
||||||
|
|
||||||
|
|
||||||
def affected(db):
|
def affected(db, variants=None):
|
||||||
"""Every section carrying the instructional voice, with its article."""
|
"""Every section carrying the instructional voice, with its article."""
|
||||||
out = []
|
out = []
|
||||||
for article in db.query(Article).order_by(Article.id).all():
|
for article in db.query(Article).order_by(Article.id).all():
|
||||||
for section in article.sections or []:
|
for section in article.sections or []:
|
||||||
|
if variants and (section.get("variant") or "long") not in variants:
|
||||||
|
continue
|
||||||
content = section.get("content") or ""
|
content = section.get("content") or ""
|
||||||
hits = IMPERATIVE.findall(content)
|
hits = imperatives(content)
|
||||||
if hits:
|
if hits:
|
||||||
out.append({
|
out.append({
|
||||||
"article_id": article.id,
|
"article_id": article.id,
|
||||||
|
|
@ -100,7 +114,7 @@ def cmd_export(args) -> int:
|
||||||
out.mkdir(parents=True, exist_ok=True)
|
out.mkdir(parents=True, exist_ok=True)
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
rows = affected(db)
|
rows = affected(db, args.variants.split(",") if args.variants else None)
|
||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
if not rows:
|
if not rows:
|
||||||
|
|
@ -191,6 +205,8 @@ def main() -> int:
|
||||||
export = sub.add_parser("export")
|
export = sub.add_parser("export")
|
||||||
export.add_argument("--out", required=True)
|
export.add_argument("--out", required=True)
|
||||||
export.add_argument("--batches", type=int, default=8)
|
export.add_argument("--batches", type=int, default=8)
|
||||||
|
export.add_argument("--variants", default=None,
|
||||||
|
help="Comma-separated variants to export, e.g. short,long")
|
||||||
export.set_defaults(func=cmd_export)
|
export.set_defaults(func=cmd_export)
|
||||||
|
|
||||||
imp = sub.add_parser("import")
|
imp = sub.add_parser("import")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue