fix: preflight member records before rules

This commit is contained in:
Marcel Peterkau
2026-06-21 17:54:09 +02:00
parent 55bc3b666e
commit 7596e47981
10 changed files with 163 additions and 44 deletions
+42 -8
View File
@@ -77,13 +77,12 @@ class MemberRepository:
seen_numbers: dict[str, str] = {}
for member_dir in self._member_directories():
try:
member = self.get_member(member_dir.name)
member, _contributions = self.preflight_member_record(member_dir.name)
validate_member_dates(
birth_date=member.birth_date,
accepted_at=member.accepted_at,
membership_started_at=member.membership_started_at,
)
self.get_contributions(member.member_id)
if member.member_id != member_dir.name:
errors.append(f"{member_dir.name}/member.json: member_id stimmt nicht mit Ordner überein")
normalized_number = member.member_number.casefold().strip()
@@ -108,18 +107,45 @@ class MemberRepository:
def list_members(self) -> list[Member]:
members: list[Member] = []
for directory in self._member_directories():
for member_id in self.list_member_ids():
try:
members.append(self.get_member(directory.name))
except (OSError, ValueError, TypeError, KeyError, json.JSONDecodeError):
members.append(self.get_member(member_id))
except RepositoryError:
continue
return sorted(members, key=lambda item: (item.last_name.casefold(), item.first_name.casefold()))
def list_member_ids(self) -> list[str]:
return sorted(directory.name for directory in self._member_directories())
def get_member(self, member_id: str) -> Member:
path = self._member_path(member_id) / "member.json"
if not path.is_file():
raise RepositoryError(f"Mitglied nicht gefunden: {member_id}")
return Member.from_dict(read_json(path))
try:
raw = read_json(path)
if not isinstance(raw, dict):
raise TypeError("Wurzelelement muss ein JSON-Objekt sein")
for section_name in ("person", "membership", "contribution_profile"):
if section_name in raw and not isinstance(raw[section_name], dict):
raise TypeError(f"{section_name} muss ein JSON-Objekt sein")
return Member.from_dict(raw)
except (OSError, ValueError, TypeError, KeyError, json.JSONDecodeError) as exc:
raise RepositoryError(f"{member_id}/member.json konnte nicht gelesen werden: {exc}") from exc
def preflight_member_record(self, member_id: str) -> tuple[Member, ContributionData]:
member = self.get_member(member_id)
if member.member_id != member_id:
raise RepositoryError(f"{member_id}/member.json: member_id stimmt nicht mit Ordner überein")
if member.schema_version != 1:
raise RepositoryError(
f"{member_id}/member.json: nicht unterstützte schema_version {member.schema_version}"
)
contributions = self.get_contributions(member_id)
try:
self.get_events(member_id)
except (OSError, UnicodeError) as exc:
raise RepositoryError(f"{member_id}/events.jsonl konnte nicht gelesen werden: {exc}") from exc
return member, contributions
def create_member(
self,
@@ -203,6 +229,11 @@ class MemberRepository:
raw = read_json(path)
if not isinstance(raw, dict):
raise TypeError("Wurzelelement muss ein JSON-Objekt sein")
for field_name in ("claims", "payments", "allocations"):
if field_name in raw and not isinstance(raw[field_name], list):
raise TypeError(f"{field_name} muss eine JSON-Liste sein")
if field_name in raw and any(not isinstance(item, dict) for item in raw[field_name]):
raise TypeError(f"Alle Einträge in {field_name} müssen JSON-Objekte sein")
return ContributionData.from_dict(raw)
except (OSError, ValueError, TypeError, json.JSONDecodeError) as exc:
raise RepositoryError(
@@ -255,8 +286,11 @@ class MemberRepository:
if not line.strip():
continue
try:
events.append(Event.from_dict(json.loads(line)))
except (ValueError, TypeError, KeyError, json.JSONDecodeError) as exc:
raw = json.loads(line)
if not isinstance(raw, dict):
raise TypeError("Event muss ein JSON-Objekt sein")
events.append(Event.from_dict(raw))
except (AttributeError, ValueError, TypeError, KeyError, json.JSONDecodeError) as exc:
raise RepositoryError(f"Ungültiges Event in Zeile {line_number}: {exc}") from exc
return events