Files
duocthu/ingestion/tests/test_validation_metrics.py
T

108 lines
4.5 KiB
Python

from ingestion.segment.models import Monograph
from ingestion.validation.back_index import GroundTruthEntry
from ingestion.validation.metrics import compute_recall_precision
def _mono(drug_id, drug_name, start_physical):
return Monograph(drug_id=drug_id, drug_name=drug_name, source_page_range=[start_physical, start_physical + 1])
def test_perfect_match_recall_and_precision_are_one():
monographs = [_mono("abacavir", "ABACAVIR", 100)]
ground_truth = [GroundTruthEntry(name="Abacavir", printed_page=101)]
result = compute_recall_precision(monographs, ground_truth)
assert result.recall == 1.0
assert result.precision == 1.0
assert result.matched_count == 1
def test_missed_ground_truth_entry_lowers_recall_not_precision():
monographs = [_mono("abacavir", "ABACAVIR", 100)]
ground_truth = [
GroundTruthEntry(name="Abacavir", printed_page=101),
GroundTruthEntry(name="Acarbose", printed_page=103),
]
result = compute_recall_precision(monographs, ground_truth)
assert result.recall == 0.5
assert result.precision == 1.0
assert len(result.unmatched_ground_truth) == 1
assert result.unmatched_ground_truth[0].name == "Acarbose"
def test_spurious_detected_monograph_lowers_precision_not_recall():
monographs = [
_mono("abacavir", "ABACAVIR", 100),
_mono("cac_chuyen_luan_thuoc", "CÁC CHUYÊN LUẬN THUỐC", 98),
]
ground_truth = [GroundTruthEntry(name="Abacavir", printed_page=101)]
result = compute_recall_precision(monographs, ground_truth)
assert result.recall == 1.0
assert result.precision == 0.5
assert len(result.unmatched_detected) == 1
def test_page_tolerance_allows_small_offset():
monographs = [_mono("abacavir", "ABACAVIR", 100)]
ground_truth = [GroundTruthEntry(name="Abacavir", printed_page=103)] # +2 tolerance
result = compute_recall_precision(monographs, ground_truth)
assert result.recall == 1.0
def test_page_beyond_tolerance_does_not_match():
monographs = [_mono("abacavir", "ABACAVIR", 100)]
ground_truth = [GroundTruthEntry(name="Abacavir", printed_page=110)]
result = compute_recall_precision(monographs, ground_truth)
assert result.recall == 0.0
def test_qualifier_suffixed_name_still_matches_base_ground_truth_name():
# SALBUTAMOL (Dùng trong hô hấp) should still match a ground-truth
# entry that just says "Salbutamol"
monographs = [_mono("salbutamol_dung_trong_ho_hap", "SALBUTAMOL (Dùng trong hô hấp)", 1261)]
ground_truth = [GroundTruthEntry(name="Salbutamol", printed_page=1262)]
result = compute_recall_precision(monographs, ground_truth)
assert result.recall == 1.0
def test_empty_ground_truth_gives_zero_recall_not_error():
result = compute_recall_precision([_mono("a", "A", 1)], [])
assert result.recall == 0.0
def test_empty_monographs_gives_zero_precision_not_error():
result = compute_recall_precision([], [GroundTruthEntry(name="A", printed_page=1)])
assert result.precision == 0.0
assert result.recall == 0.0
def test_exact_match_preferred_over_substring_steal_confirmed_real_case():
# Confirmed real case from a whole-book `cli validate` run: "ISOSORBID"
# and "ISOSORBID DINITRAT" are two distinct, correctly-segmented
# monographs a page apart. A pure substring match lets the shorter name
# "steal" both ground-truth entries (it's a substring of the longer one
# too) via `next()`'s order-dependent first match, leaving the real
# "ISOSORBID DINITRAT" monograph spuriously unmatched even though an
# exact match for it exists.
monographs = [
_mono("isosorbid", "ISOSORBID", 844),
_mono("isosorbid_dinitrat", "ISOSORBID DINITRAT", 845),
]
ground_truth = [
GroundTruthEntry(name="Isosorbid", printed_page=845),
GroundTruthEntry(name="Isosorbid dinitrat", printed_page=846),
]
result = compute_recall_precision(monographs, ground_truth)
assert result.recall == 1.0
assert result.precision == 1.0
assert len(result.unmatched_detected) == 0
def test_double_space_in_detected_name_still_matches_confirmed_real_case():
# confirmed real case from a whole-book `cli validate` run: "ALVERIN
# CITRAT" (double space) failed to match ground truth's single-spaced
# "Alverin citrat" under plain strip+upper comparison.
monographs = [_mono("alverin_citrat", "ALVERIN CITRAT", 171)]
ground_truth = [GroundTruthEntry(name="Alverin citrat", printed_page=172)]
result = compute_recall_precision(monographs, ground_truth)
assert result.recall == 1.0