Skip to content

v4.1.1 — Fix BUG-001 (db_loader crash)

Choose a tag to compare

@jbiter jbiter released this 10 Jul 01:00

v4.1.1 — Bug Fix Release

🐛 Fix: BUG-001 (Critical)

load_records_from_db() no longer crashes when records have NULL year or citation_count.

Root cause: SQLite stores nullable INTEGER columns as empty strings "" when not set. The loader passed these "" values directly to Pydantic's Record constructor, which expects int | None and rejected empty strings with int_parsing validation errors.

Impact: After importing any bibliographic file with incomplete metadata (missing year or citation count), all subsequent analysis commands (stats, text, network, trend) crashed.

Fix: Added _opt_int() helper in src/citationer/utils/db_loader.py:103-111 that converts empty strings, None, or whitespace-only strings to None before passing to Pydantic.

def _opt_int(value):
    if value is None:
        return None
    if isinstance(value, int):
        return value
    if value == "" or str(value).strip() == "":
        return None
    return value

✅ Regression Tests

3 new tests in tests/test_database.py::TestBug001Regression:

  • test_load_with_null_year — Pydantic Record accepts NULL year
  • test_load_with_empty_string_year — Manual SQL UPDATE sets int columns to NULL → load succeeds
  • test_round_trip_orphan_record — Full round-trip with all year/citation_count fields missing

📊 Status

  • Tests: 562 passing (was 559)
  • Coverage: 80.49% (CI gate 80% met)
  • Lint: ruff clean

🐞 Remaining Defects (not fixed in 4.1.1)

  • #16 BUG-002: Institution.name_en not serialized
  • #17 BUG-003: en/zh keywords not split on load
  • #19 BUG-004: trend baseline median polluted by burst
  • #20 BUG-005: trend strategy recomputes in second pass

Closes #15