perf(campaign): share linear collision-safe attachment naming

Release v0.1.29. Coordinated integrity review: GovOPlaN/govoplan-core#298.
This commit is contained in:
2026-09-08 12:19:37 +02:00
parent c51fc180fb
commit 8bca4fc728
9 changed files with 130 additions and 40 deletions
@@ -1,5 +1,7 @@
from __future__ import annotations
from govoplan_campaign.backend.services.filenames import FilenameAllocator
import mimetypes
import re
import tempfile
@@ -303,15 +305,8 @@ def _archive_filename(archive: ZipArchiveConfig, values: dict[str, Any], entry_i
return filename if filename.lower().endswith(".zip") else f"{filename}.zip"
def _unique_attachment_filename(filename: str, used: set[str]) -> str:
candidate = filename
path = Path(filename)
counter = 2
while candidate.casefold() in used:
candidate = f"{path.stem} ({counter}){path.suffix}"
counter += 1
used.add(candidate.casefold())
return candidate
def _unique_attachment_filename(filename: str, used: FilenameAllocator) -> str:
return used.allocate(filename)
def _deduplicated_archive_members(members: list[tuple[Path, str]]) -> list[tuple[Path, str]]:
@@ -381,8 +376,8 @@ def _attach_files(
evidence: list[dict[str, object]] = []
archive_members: dict[str, list[tuple[Path, str]]] = {}
archive_attachments: dict[str, list[ResolvedAttachment]] = {}
used_message_filenames: set[str] = set()
used_zip_member_filenames: dict[str, set[str]] = {}
used_message_filenames = FilenameAllocator()
used_zip_member_filenames: dict[str, FilenameAllocator] = {}
for attachment in resolution.attachments:
attachment.message_filenames = []
@@ -394,7 +389,7 @@ def _attach_files(
continue
match_paths = [Path(match) for match in attachment.matches]
if attachment.zip_enabled and attachment.zip_archive_id:
used_archive_names = used_zip_member_filenames.setdefault(attachment.zip_archive_id, set())
used_archive_names = used_zip_member_filenames.setdefault(attachment.zip_archive_id, FilenameAllocator())
for position, path in enumerate(match_paths, start=1):
requested = _render_attachment_filename(
template=attachment.zip_entry_name_template,