diff --git a/citeforge/merge_utils.py b/citeforge/merge_utils.py index 89ca2c0d..594bc894 100644 --- a/citeforge/merge_utils.py +++ b/citeforge/merge_utils.py @@ -1400,8 +1400,18 @@ def _replace_existing() -> None: if prefer_entry: pf_fields = sum(1 for v in prefer_entry.get("fields", {}).values() if v) nf_fields = sum(1 for v in entry.get("fields", {}).values() if v) - pf_doi = (prefer_entry.get("fields", {}).get("doi") or "").strip() - if pf_fields > nf_fields or (pf_fields == nf_fields and pf_doi): + pf_doi = _norm_doi(prefer_entry.get("fields", {}).get("doi")) or "" + same_doi_work = bool( + pf_doi + and new_doi + and pf_doi == new_doi + and evaluate_identity( + prefer_entry, + entry, + context=IdentityContext.DISK_SURVIVOR, + ).verdict + ) + if not same_doi_work and (pf_fields > nf_fields or (pf_fields == nf_fields and pf_doi)): logger.debug( f"FILE_CLEANUP_BLOCKED | prefer={os.path.basename(prefer_path)} " f"({pf_fields} fields, doi) > new ({nf_fields} fields) | keeping enriched", diff --git a/citeforge/text_utils.py b/citeforge/text_utils.py index 804f220e..67a01b4f 100644 --- a/citeforge/text_utils.py +++ b/citeforge/text_utils.py @@ -256,7 +256,7 @@ def normalize_person_name(n: Any | None) -> str: if not n: return "" n_str = to_text(n) - n2 = strip_accents(n_str).lower() + n2 = latex_to_ascii(n_str, math_mode="remove").lower() n2 = n2.replace("'", "").replace("\u2019", "").replace("\u02bc", "") n2 = _PERSON_PUNCT_RE.sub(" ", n2) return " ".join(n2.split()) diff --git a/tests/test_save_entry.py b/tests/test_save_entry.py index d1a4701f..6cef4137 100644 --- a/tests/test_save_entry.py +++ b/tests/test_save_entry.py @@ -405,3 +405,51 @@ def test_year_change_renames_the_file_to_match_the_new_year(tmp_path: Path) -> N assert len(survivors) == 1, f"the superseded name must not linger: {survivors}" assert "2023" in survivors[0], f"filename must carry the new year: {survivors[0]}" assert "2020" not in survivors[0] + + +def test_validated_same_doi_metadata_replaces_equal_field_baseline(tmp_path: Path) -> None: + """A DOI-bearing baseline cannot block corrected metadata solely because field counts tie.""" + author_dir = tmp_path / format_author_dirname("Haque, Israat", "x1") + author_dir.mkdir(parents=True) + doi = "10.1109/tmlcn.2025.3575368" + old_path = factories.write_bib( + author_dir, + factories.article( + key="Hasan2024", + title="A Generalized Transformer-based Radio Link Failure Prediction Framework in 5G RANs", + author="Kazi Hasan and Thomas Trappenberg and Israat Haque", + year="2024", + journal="IEEE Transactions on Machine Learning in Communications and Networking", + doi=doi, + url=f"https://doi.org/{doi}", + volume="3", + pages="1-12", + ), + "Hasan2024-GeneralizedTransformer.bib", + ) + incoming = factories.article( + key="Hasan2025", + title="A Generalized GNN-Transformer-Based Radio Link Failure Prediction Framework in 5G RAN", + author="Kazi Hasan and Khaleda Papry and Thomas Trappenberg and Israat Haque", + year="2025", + journal="IEEE Transactions on Machine Learning in Communications and Networking", + doi=doi, + url=f"https://doi.org/{doi}", + volume="3", + pages="1-12", + ) + + path, written = save_entry_to_file( + str(tmp_path), + "x1", + incoming, + prefer_path=str(old_path), + author_name="Haque, Israat", + ) + + assert written is True + assert not old_path.exists() + assert "2025" in Path(path).name + saved = _read(author_dir, Path(path).name) + assert "Khaleda Papry" in saved + assert "year = {2025}" in saved diff --git a/tests/test_text_utils.py b/tests/test_text_utils.py index 9138d2c7..2f9a82d0 100644 --- a/tests/test_text_utils.py +++ b/tests/test_text_utils.py @@ -142,6 +142,10 @@ def test_author_overlap_preserves_apostrophes_inside_surnames() -> None: assert author_overlap_ratio("Sageev Oore and Jason D'eon", "Oore, Sageev and D'eon, Jason") == 1.0 +def test_author_overlap_decodes_bibtex_accent_macros() -> None: + assert author_overlap_ratio(r"Anne Bergh{\"o}fer", "Anne Berghofer") == 1.0 + + def _preprint_side() -> dict[str, str]: return {"title": "T", "author": "Smith, John", "year": "2021", "journal": "arXiv"}