GCMD_Keyword_Classifier_MVP / tests /test_redundancy.py
igerasimov's picture
MVP Milestone 9
57b6fe2
Raw
History Blame Contribute Delete
7.58 kB
from __future__ import annotations
from pathlib import Path
from gcmd_classifier.classification import (
ClassificationCandidate,
remove_redundant_classifications,
validate_candidate,
)
from gcmd_classifier.models import ClassificationRecord, SupportType
from gcmd_classifier.vocabulary import load_vocabulary
FIXTURE_PATH = Path("tests/fixtures/gcmd_hierarchy_small.json")
def _index():
return load_vocabulary(FIXTURE_PATH)
def _candidate(uuid: str, *, branch_id: str | None) -> ClassificationCandidate:
index = _index()
record = index.get(uuid)
topic = record if record.level == "Topic" else index.get(index.ancestors_of(uuid)[-1])
term = None
if record.level == "Term":
term = record
elif record.level.startswith("Variable_Level_"):
term = next(
index.get(ancestor)
for ancestor in index.ancestors_of(uuid)
if index.get(ancestor).level == "Term"
)
return ClassificationCandidate(
final_uuid=record.UUID,
final_name=record.name,
final_level=record.level,
final_canonical_path=record.canonical_path,
path_components=record.path_components,
topic_uuid=topic.UUID,
topic_name=topic.name,
term_uuid=None if term is None else term.UUID,
term_name=None if term is None else term.name,
branch_id=branch_id,
evidence=f"Evidence for {record.name}.",
support_type=SupportType.EXPLICIT,
confidence=0.7,
)
def _record(uuid: str, *, branch_id: str | None) -> ClassificationRecord:
result = validate_candidate(_candidate(uuid, branch_id=branch_id), _index())
assert result.classification is not None
return result.classification
def _warning_codes(result) -> list[str]:
return [warning.code for warning in result.warnings]
def test_exact_duplicate_uuid_removed() -> None:
first = _record("vl2-carbon-dioxide", branch_id="topic:t/term:x/variable:a")
duplicate = _record("vl2-carbon-dioxide", branch_id="topic:t/term:x/variable:a-copy")
result = remove_redundant_classifications([first, duplicate], _index())
assert [record.UUID for record in result.classifications] == ["vl2-carbon-dioxide"]
assert [record.UUID for record in result.removed] == ["vl2-carbon-dioxide"]
assert "DUPLICATE_UUID_REMOVED" in _warning_codes(result)
def test_exact_duplicate_canonical_path_removed() -> None:
first = _record("vl2-carbon-dioxide", branch_id="topic:t/term:x/variable:a")
duplicate_path = _record("vl2-methane", branch_id="topic:t/term:x/variable:b").model_copy(
update={"canonical_path": first.canonical_path}
)
result = remove_redundant_classifications([first, duplicate_path], _index())
assert [record.UUID for record in result.classifications] == ["vl2-carbon-dioxide"]
assert [record.UUID for record in result.removed] == ["vl2-methane"]
assert "DUPLICATE_CANONICAL_PATH_REMOVED" in _warning_codes(result)
def test_ancestor_removed_when_descendant_exists_in_same_branch_lineage() -> None:
ancestor = _record("term-atmospheric-chemistry", branch_id="topic:t/term:x")
descendant = _record("vl2-carbon-dioxide", branch_id="topic:t/term:x/variable:a")
result = remove_redundant_classifications([ancestor, descendant], _index())
assert [record.UUID for record in result.classifications] == ["vl2-carbon-dioxide"]
assert [record.UUID for record in result.removed] == ["term-atmospheric-chemistry"]
assert "ANCESTOR_REMOVED_SAME_BRANCH" in _warning_codes(result)
def test_deepest_descendant_retained() -> None:
term = _record("term-atmospheric-chemistry", branch_id="topic:t/term:x")
variable_level_1 = _record("vl1-atmosphere-carbon", branch_id="topic:t/term:x/variable:a")
variable_level_3 = _record(
"vl3-carbon-dioxide-profiles",
branch_id="topic:t/term:x/variable:a/variable:b/variable:c",
)
result = remove_redundant_classifications([term, variable_level_1, variable_level_3], _index())
assert [record.UUID for record in result.classifications] == ["vl3-carbon-dioxide-profiles"]
assert {record.UUID for record in result.removed} == {
"term-atmospheric-chemistry",
"vl1-atmosphere-carbon",
}
def test_ancestor_preserved_when_independent_branch() -> None:
ancestor = _record("term-atmospheric-chemistry", branch_id="topic:t/term:independent")
descendant = _record("vl2-carbon-dioxide", branch_id="topic:t/term:x/variable:a")
result = remove_redundant_classifications([ancestor, descendant], _index())
assert [record.UUID for record in result.classifications] == [
"term-atmospheric-chemistry",
"vl2-carbon-dioxide",
]
assert result.removed == ()
assert "ANCESTOR_PRESERVED_INDEPENDENT_BRANCH" in _warning_codes(result)
def test_sibling_classifications_preserved() -> None:
dioxide = _record("vl2-carbon-dioxide", branch_id="topic:t/term:x/variable:a")
methane = _record("vl2-methane", branch_id="topic:t/term:x/variable:b")
result = remove_redundant_classifications([dioxide, methane], _index())
assert [record.UUID for record in result.classifications] == [
"vl2-carbon-dioxide",
"vl2-methane",
]
assert result.removed == ()
def test_same_concept_name_in_different_branches_is_preserved_when_uuid_and_path_differ() -> None:
atmosphere_carbon = _record("vl1-atmosphere-carbon", branch_id="topic:a/term:a/variable:a")
ocean_carbon = _record("vl1-ocean-carbon", branch_id="topic:o/term:o/variable:o")
result = remove_redundant_classifications([atmosphere_carbon, ocean_carbon], _index())
assert [record.name for record in result.classifications] == ["CARBON", "CARBON"]
assert [record.UUID for record in result.classifications] == [
"vl1-atmosphere-carbon",
"vl1-ocean-carbon",
]
assert result.removed == ()
def test_redundancy_uses_uuid_ancestry_not_string_prefix_matching() -> None:
atmosphere_carbon = _record("vl1-atmosphere-carbon", branch_id="topic:a/term:a/variable:a")
ocean_carbon = _record("vl1-ocean-carbon", branch_id="topic:o/term:o/variable:o")
assert atmosphere_carbon.name == ocean_carbon.name
result = remove_redundant_classifications([atmosphere_carbon, ocean_carbon], _index())
assert result.retained_count == 2
assert result.removed_count == 0
def test_insufficient_branch_provenance_preserves_ancestor_with_warning() -> None:
ancestor = _record("term-atmospheric-chemistry", branch_id=None)
descendant = _record("vl2-carbon-dioxide", branch_id="topic:t/term:x/variable:a")
result = remove_redundant_classifications([ancestor, descendant], _index())
assert [record.UUID for record in result.classifications] == [
"term-atmospheric-chemistry",
"vl2-carbon-dioxide",
]
assert "ANCESTOR_PRESERVED_INSUFFICIENT_PROVENANCE" in _warning_codes(result)
def test_removal_and_preservation_diagnostics_include_branch_provenance() -> None:
ancestor = _record("term-atmospheric-chemistry", branch_id="topic:t/term:x")
descendant = _record("vl2-carbon-dioxide", branch_id="topic:t/term:x/variable:a")
result = remove_redundant_classifications([ancestor, descendant], _index())
removal_warning = next(
warning for warning in result.warnings if warning.code == "ANCESTOR_REMOVED_SAME_BRANCH"
)
assert removal_warning.details["branch_id"] == "topic:t/term:x"
assert removal_warning.details["descendant_branch_id"] == "topic:t/term:x/variable:a"