Fix every real lint finding and drop degenerate splice fragments
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
from ingestion.segment.atc import extract_atc_codes, is_stated_absent, normalize_atc_candidate
|
||||
|
||||
|
||||
def test_stray_whitespace_split_j04a_c01_recovered():
|
||||
assert normalize_atc_candidate("J04A C01") == "J04AC01"
|
||||
|
||||
|
||||
def test_stray_whitespace_split_n05b_a06_recovered():
|
||||
assert normalize_atc_candidate("N05B A06") == "N05BA06"
|
||||
|
||||
|
||||
def test_stray_whitespace_split_l01x_x02_recovered():
|
||||
assert normalize_atc_candidate("L01X X02") == "L01XX02"
|
||||
|
||||
|
||||
def test_digit_letter_confusion_no3ax12_recovered():
|
||||
assert normalize_atc_candidate("NO3AX12") == "N03AX12"
|
||||
|
||||
|
||||
def test_digit_letter_confusion_jo1dc07_recovered():
|
||||
assert normalize_atc_candidate("JO1DC07") == "J01DC07"
|
||||
|
||||
|
||||
def test_clean_code_passes_through():
|
||||
assert normalize_atc_candidate("N03AX12") == "N03AX12"
|
||||
|
||||
|
||||
def test_garbage_not_recovered():
|
||||
assert normalize_atc_candidate("NOT AN ATC CODE") is None
|
||||
assert normalize_atc_candidate("") is None
|
||||
|
||||
|
||||
def test_stated_absent_chua_co():
|
||||
assert is_stated_absent("Mã ATC: Chưa có.") is True
|
||||
|
||||
|
||||
def test_stated_absent_khong_co():
|
||||
assert is_stated_absent("Không có.") is True
|
||||
|
||||
|
||||
def test_stated_present_not_flagged_absent():
|
||||
assert is_stated_absent("N03AX12") is False
|
||||
|
||||
|
||||
def test_extract_single_code():
|
||||
result = extract_atc_codes("N03AX12")
|
||||
assert result.codes == ["N03AX12"]
|
||||
assert result.stated_absent is False
|
||||
|
||||
|
||||
def test_extract_multi_code_insulin_style():
|
||||
result = extract_atc_codes("A10AB01, A10AC01, A10AD01")
|
||||
assert result.codes == ["A10AB01", "A10AC01", "A10AD01"]
|
||||
|
||||
|
||||
def test_extract_multi_code_with_noise_mixed_in():
|
||||
# one clean code, one noisy code recovered, matching the real corpus
|
||||
# pattern where a monograph has some clean and some noisy ATC entries
|
||||
result = extract_atc_codes("N03AX12, J04A C01")
|
||||
assert result.codes == ["N03AX12", "J04AC01"]
|
||||
|
||||
|
||||
def test_extract_stated_absent_returns_no_codes():
|
||||
result = extract_atc_codes("Mã ATC: Chưa có.")
|
||||
assert result.codes == []
|
||||
assert result.stated_absent is True
|
||||
|
||||
|
||||
def test_trailing_period_recovered_confirmed_real_abacavir_case():
|
||||
# real field text is "J05AF06." — a sentence-ending period, not part of
|
||||
# the code; an earlier version silently produced zero codes here.
|
||||
assert normalize_atc_candidate("J05AF06.") == "J05AF06"
|
||||
result = extract_atc_codes("J05AF06.")
|
||||
assert result.codes == ["J05AF06"]
|
||||
|
||||
|
||||
def test_species_annotation_stripped_confirmed_real_insulin_case():
|
||||
# annotation-stripping is extract_atc_codes's job (must run before the
|
||||
# comma/semicolon split, see below) — normalize_atc_candidate itself
|
||||
# only normalizes an already-isolated code token.
|
||||
result = extract_atc_codes("A10AB01 (người); A10AB02 (bò)")
|
||||
assert result.codes == ["A10AB01", "A10AB02"]
|
||||
|
||||
|
||||
def test_leading_colon_from_value_span_stripped_confirmed_real_alcuronium_case():
|
||||
# real field text for ALCURONIUM CLORID (physical page 152): the bold
|
||||
# label span is "Mã ATC" with no colon, and the plain value span is
|
||||
# ": M03AA01." — the colon belongs to the value side here, not the
|
||||
# label side (Abacavir's equivalent has it on the label side instead:
|
||||
# "Mã ATC: " + "J05AF06."). See atc.py module docstring, defect 5.
|
||||
assert normalize_atc_candidate(": M03AA01.") == "M03AA01"
|
||||
result = extract_atc_codes(": M03AA01.")
|
||||
assert result.codes == ["M03AA01"]
|
||||
|
||||
|
||||
def test_name_prefixed_code_stripped_confirmed_real_arginin_case():
|
||||
# real field text for ARGININ (physical page 204): two salt forms, each
|
||||
# its own "Name: CODE" line, not a bare code — see atc.py module
|
||||
# docstring, defect 6.
|
||||
assert normalize_atc_candidate("Arginin glutamat: A05BA01") == "A05BA01"
|
||||
result = extract_atc_codes("Arginin glutamat: A05BA01\nArginin hydroclorid: B05XB01")
|
||||
assert result.codes == ["A05BA01", "B05XB01"]
|
||||
|
||||
|
||||
def test_plain_code_with_no_colon_still_normalizes():
|
||||
assert normalize_atc_candidate("N03AX12") == "N03AX12"
|
||||
|
||||
|
||||
def test_reversed_code_first_shape_confirmed_real_hmg_coa_case():
|
||||
# real field text for CÁC CHẤT ỨC CHẾ HMG-CoA REDUCTASE (physical page
|
||||
# 284): each statin is "CODE: Name", the opposite order from the
|
||||
# "Name: CODE" shape above — see atc.py module docstring, defect 7.
|
||||
# "C10A A01" also has the already-fixed stray-whitespace split.
|
||||
assert normalize_atc_candidate("C10A A01: Simvastatin") == "C10AA01"
|
||||
result = extract_atc_codes("C10A A01: Simvastatin\nC10A A02: Lovastatin")
|
||||
assert result.codes == ["C10AA01", "C10AA02"]
|
||||
|
||||
|
||||
def test_annotation_containing_a_comma_does_not_break_the_split_confirmed_vaccine_case():
|
||||
# real field text for VẮC XIN SỞI (physical page 1437): the English
|
||||
# annotation "(Measles, live attenuated)" contains its own comma. An
|
||||
# earlier version split on "," *before* stripping the annotation,
|
||||
# breaking "J07BD01 (Measles, live attenuated)." into two unrecoverable
|
||||
# fragments and silently returning zero codes — see atc.py module
|
||||
# docstring, defect 4.
|
||||
result = extract_atc_codes("J07BD01 (Measles, live attenuated).")
|
||||
assert result.codes == ["J07BD01"]
|
||||
|
||||
|
||||
def test_extract_all_20_insulin_codes_from_real_field_text():
|
||||
# exact real field text for INSULIN (physical page 809) — see atc.py
|
||||
# module docstring; confirms the fix recovers all 20, not just 2.
|
||||
field_text = (
|
||||
"A10AB01 (người); A10AB02 (bò); A10AB03 (lợn);\n"
|
||||
"A10AB04 (lispro); A10AB05 (aspart); A10AB06 (glulisin);\n"
|
||||
"A10AC01 (người); A10AC02 (bò); A10AC03 (lợn); A10AC04\n"
|
||||
"(lispro); A10AD01 (người), A10AD02 (bò), A10AD03 (lợn),\n"
|
||||
"A10AD04 (lispro), A10AE01 (người); A10AE02 (bò); A10AE03\n"
|
||||
"(lợn); A10AE04 (glargin); A10AE05 (detemir), A10AF01 (người)."
|
||||
)
|
||||
result = extract_atc_codes(field_text)
|
||||
assert len(result.codes) == 20
|
||||
assert "A10AB01" in result.codes
|
||||
assert "A10AF01" in result.codes
|
||||
Reference in New Issue
Block a user