Report how many files a rename run renamed 🟥

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Roth 2026-07-29 08:42:33 +00:00
parent 82e1dd555a
commit 5dc524be8f
2 changed files with 61 additions and 1 deletions

View file

@ -1,5 +1,6 @@
import csv
import os
from dataclasses import dataclass, field
from typing import Optional
from domain.pashub_fetcher.sharepoint_subfolders import SharepointSubfolders
@ -16,6 +17,34 @@ BATCH_FOLDER_PREFIX = "_Sero Batch"
logger = setup_logger()
@dataclass
class RenameFailure:
"""One file SharePoint refused to rename, and why."""
uprn: str
original_name: str
new_name: str
error: str
@dataclass
class RenameSummary:
"""What a run did, threaded back up through the traversal.
Counts are per-**file**; ``missing_folders`` is per-**property** (a UPRN with
no assessment folder under any root, so no file was even looked at)."""
renamed: int = 0
would_rename: int = 0
skipped_already_canonical: int = 0
skipped_images: int = 0
missing_folders: list[str] = field(default_factory=list)
failed: list[RenameFailure] = field(default_factory=list)
def merge_in(self, other: "RenameSummary") -> None:
raise NotImplementedError
def build_canonical_filename(
uprn: str, address: str, postcode: str, original_name: str
) -> Optional[str]:
@ -76,7 +105,8 @@ class SharepointRenamerOrchestrator:
)
return [BASE_PATH, *batch_roots]
def run(self) -> None:
def run(self) -> RenameSummary:
summary = RenameSummary()
roots = self._discover_roots()
with open(self._csv_path, newline="", encoding="utf-8-sig") as f:
reader = csv.DictReader(f)
@ -93,6 +123,7 @@ class SharepointRenamerOrchestrator:
address=row["Address"].strip(),
postcode=row["Postcode"].strip(),
)
return summary
def _process_row(
self, roots: list[str], uprn: str, address: str, postcode: str

View file

@ -8,6 +8,7 @@ from domain.pashub_fetcher.sharepoint_subfolders import SharepointSubfolders
from orchestration.sharepoint_renamer_orchestrator import (
ASSESSMENT_SUBFOLDER,
BASE_PATH,
RenameSummary,
SharepointRenamerOrchestrator,
build_canonical_filename,
)
@ -124,6 +125,34 @@ def test_run_renames_files_for_property_under_batch_root(tmp_path: Path) -> None
sp.rename_file.assert_called_once_with("id-1", "100_1 High St AB1 2CD_Survey.pdf")
def test_run_summary_counts_each_renamed_file(tmp_path: Path) -> None:
# Arrange
csv_path = _write_csv(tmp_path, [("100", "1 High St", "AB1 2CD")])
assessment = _assessment_path(BASE_PATH, "1 High St", "AB1 2CD")
sp = MagicMock()
def fake_get(path: str) -> dict[str, Any]:
if path == BASE_PATH:
return {"value": []}
if path == assessment:
return {
"value": [
_make_file("Survey.pdf", "id-1"),
_make_file("Report.docx", "id-2"),
]
}
raise ValueError("not found")
sp.get_folders_in_path.side_effect = fake_get
# Act
summary = SharepointRenamerOrchestrator(sp, csv_path).run()
# Assert
assert summary.renamed == 2
def test_run_uprn_prefixed_address_yields_single_uprn_filename(
tmp_path: Path,
) -> None: