erster versuch der KI mit Plan
This commit is contained in:
1 parent
c697c64f3a
commit
2faa1689dd
20 files changed
+1749
No files matched your search
@@ -0,0 +1,172 @@
|
||||
"""Tests for src/pdf_filler.py — field dict building and pauschale/plan logic."""
|
||||
|
||||
from datetime import date
|
||||
|
||||
import pytest
|
||||
|
||||
from src.models import AttendanceDate, MonthlyAttendance, Person
|
||||
from src.pdf_filler import build_field_dict
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@pytest.fixture
|
||||
def cfg():
|
||||
return {
|
||||
"antragsnummer": "SAB-12345",
|
||||
"ort": "Leipzig",
|
||||
"template_pdf": "template/sab.pdf",
|
||||
"output_dir": "output",
|
||||
"plan_hours_value": "6",
|
||||
"pauschale_value": "8,60",
|
||||
"excel_year_pattern": "(20\\d{2})",
|
||||
"excel_name_header": "Name, Vorname",
|
||||
"excel_name_column": 1,
|
||||
"excel_seq_column": 0,
|
||||
"excel_day_start_col": 9,
|
||||
"excel_day_end_col": 39,
|
||||
"month_mapping": {"Feb": "02"},
|
||||
"valid_status_codes": ["K", "KK", "E", "F", "P", "U"],
|
||||
}
|
||||
|
||||
|
||||
def _make_attendance(*entries) -> MonthlyAttendance:
|
||||
person = Person(seq_num=1, name="Teilnehmer, Test")
|
||||
return MonthlyAttendance(
|
||||
person=person,
|
||||
year=2026,
|
||||
month=2,
|
||||
month_name="Feb",
|
||||
entries=list(entries),
|
||||
)
|
||||
|
||||
|
||||
def _present(day: int, hours: float) -> AttendanceDate:
|
||||
return AttendanceDate(date=date(2026, 2, day), status="X", hours=hours)
|
||||
|
||||
|
||||
def _absent(day: int, status: str) -> AttendanceDate:
|
||||
return AttendanceDate(date=date(2026, 2, day), status=status, hours=None)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Header fields
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestHeaderFields:
|
||||
def test_person_name(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(), cfg, date(2026, 2, 1))
|
||||
assert fields["person.name"] == "Teilnehmer, Test"
|
||||
|
||||
def test_monat_jahr(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(), cfg, date(2026, 2, 1))
|
||||
assert fields["monat_jahr"] == "02/2026"
|
||||
|
||||
def test_antragsnummer(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(), cfg, date(2026, 2, 1))
|
||||
assert fields["antragsnummer"] == "SAB-12345"
|
||||
|
||||
def test_ort(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(), cfg, date(2026, 2, 1))
|
||||
assert fields["ort"] == "Leipzig"
|
||||
|
||||
def test_erstellungsdatum(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(), cfg, date(2026, 3, 27))
|
||||
assert fields["erstellungsdatum"] == "27.03.2026"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Present day (status X)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestPresentDay:
|
||||
def test_datum_field(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(_present(3, 6.0)), cfg, date(2026, 2, 1))
|
||||
assert fields["datum.0"] == "03.02.2026"
|
||||
|
||||
def test_anwesend_field(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(_present(3, 6.0)), cfg, date(2026, 2, 1))
|
||||
assert fields["anwesend.0"] == "X"
|
||||
|
||||
def test_ist_field_whole_hours(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(_present(3, 6.0)), cfg, date(2026, 2, 1))
|
||||
assert fields["ist.0"] == "6"
|
||||
|
||||
def test_ist_field_decimal_hours(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(_present(3, 6.5)), cfg, date(2026, 2, 1))
|
||||
assert fields["ist.0"] == "6,5"
|
||||
|
||||
def test_plan_always_6(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(_present(3, 3.0)), cfg, date(2026, 2, 1))
|
||||
assert fields["plan.0"] == "6"
|
||||
|
||||
def test_pauschale_set(self, cfg):
|
||||
fields = build_field_dict(_make_attendance(_present(3, 6.0)), cfg, date(2026, 2, 1))
|
||||
assert fields["pauschale.0"] == "8,60"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Absent days (K, KK, E, F, P, U)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestAbsentDay:
|
||||
@pytest.mark.parametrize("status", ["K", "KK", "E", "F", "P", "U"])
|
||||
def test_anwesend_field(self, cfg, status):
|
||||
fields = build_field_dict(_make_attendance(_absent(4, status)), cfg, date(2026, 2, 1))
|
||||
assert fields["anwesend.0"] == status
|
||||
|
||||
@pytest.mark.parametrize("status", ["K", "KK", "E", "F", "P", "U"])
|
||||
def test_ist_empty(self, cfg, status):
|
||||
fields = build_field_dict(_make_attendance(_absent(4, status)), cfg, date(2026, 2, 1))
|
||||
assert fields["ist.0"] == ""
|
||||
|
||||
@pytest.mark.parametrize("status", ["K", "KK", "E", "F", "P", "U"])
|
||||
def test_plan_empty(self, cfg, status):
|
||||
fields = build_field_dict(_make_attendance(_absent(4, status)), cfg, date(2026, 2, 1))
|
||||
assert fields["plan.0"] == ""
|
||||
|
||||
@pytest.mark.parametrize("status", ["K", "KK", "E", "F", "P", "U"])
|
||||
def test_pauschale_empty(self, cfg, status):
|
||||
fields = build_field_dict(_make_attendance(_absent(4, status)), cfg, date(2026, 2, 1))
|
||||
assert fields["pauschale.0"] == ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Multi-row indexing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestMultiRow:
|
||||
def test_second_entry_indexed_as_1(self, cfg):
|
||||
entries = [_present(1, 6.0), _absent(2, "K")]
|
||||
fields = build_field_dict(_make_attendance(*entries), cfg, date(2026, 2, 1))
|
||||
assert fields["datum.1"] == "02.02.2026"
|
||||
assert fields["anwesend.1"] == "K"
|
||||
|
||||
def test_23_entries_produce_index_0_to_22(self, cfg):
|
||||
entries = [_present(i, 6.0) for i in range(1, 24)] # 23 entries max
|
||||
fields = build_field_dict(_make_attendance(*entries), cfg, date(2026, 2, 1))
|
||||
# Index 22 should exist
|
||||
assert "datum.22" in fields
|
||||
assert "anwesend.22" in fields
|
||||
# Index 23 must NOT exist
|
||||
assert "datum.23" not in fields
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hours formatting
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestHoursFormatting:
|
||||
def test_whole_number_no_decimal(self):
|
||||
entry = AttendanceDate(date=date(2026, 2, 1), status="X", hours=6.0)
|
||||
assert entry.hours_german() == "6"
|
||||
|
||||
def test_decimal_comma_separator(self):
|
||||
entry = AttendanceDate(date=date(2026, 2, 1), status="X", hours=6.5)
|
||||
assert entry.hours_german() == "6,5"
|
||||
|
||||
def test_none_returns_empty(self):
|
||||
entry = AttendanceDate(date=date(2026, 2, 1), status="K", hours=None)
|
||||
assert entry.hours_german() == ""
|
||||
Reference in new issue
Block a user