Rozdział 23 · Część IV · Wdrażamy SafeSort
Od analizy do planu działania
Plan ruchu to po prostu dane: można go wyświetlać, sprawdzać lub odrzucać bez dotykania pojedynczego pliku.
SafeSort · Część 4 z 6Wdrożenie
Add safe organization plan
Area: SafetyPriority: High
git switch -c feat/plan-and-collisions
SafeSort ma lista znalezionych plików oraz regułę klasyfikacji — ale to jest Jeszcze nie mam planu działania. Plan jest lista ruchów konkretnych: skąd i gdzie każdy plik przesuwa się, jeśli użytkownik potwierdzi wykonanie.
Tylko czytać
scan / plan / duplicates tylko czytać
Zmieniają pliki
apply rodzaju; undo przywraca
src/safesort/models.py
@dataclass(frozen=True)
class MoveOperation:
source: Path
destination: Path
@dataclass(frozen=True)
class SortPlan:
root: Path
operations: tuple[MoveOperation, ...]
Plan to dane, a nie działanie
SortPlan nie zawiera wywołań przenoszących pliki, tylko opis tego, co może być do zrobienia. Ta idea jest kluczowa dla całej architektury SafeSort: dopóki obiekt pozostaje danymi, może być wyświetlany, testowany, zapisywany lub po prostu odrzucany bez ryzyka użycia pojedynczego pliku użytkownika.Funkcja build_plan() tworzy plan na podstawie listy plików z
skaner bez dotykania dysku ani razu, chyba read-only sprawdzeniu, czy plik już istnieje
z tą nazwą na miejscu docelowym. Co robić, jeśli jesteś, to temat kolejnego etapu.
src/safesort/planner.py
def build_plan(files: list[FileInfo], root: Path, config: Config) -> SortPlan:
dest_root = Path(root) / config.destination
operations = []
for file in files:
category = classify(file.extension, config.extensions)
dest_dir = dest_root / category
candidate = dest_dir / file.path.name
destination = _resolve_collision(candidate, reserved)
operations.append(MoveOperation(source=file.path, destination=destination))
return SortPlan(root=root, operations=tuple(operations))
Punkt kontrolny testu: Czyste planowanie
tests/test_planner.py
def test_build_plan_describes_move_without_executing_it(tmp_path):
source = tmp_path / "report.pdf"
source.write_bytes(b"report")
file = FileInfo(source, source.stat().st_size, ".pdf")
plan = build_plan([file], tmp_path, Config())
assert plan.operations[0].destination == tmp_path / "Sorted/documents/report.pdf"
assert source.exists()
assert not plan.operations[0].destination.exists()
Praktyka: Zbuduj plan na podstawie listy plików
interaktywny laptop bezpośrednio w przeglądarce – Python 3.14 przez Pyodide, bez instalacji
Checkpoint · Issue #3
git commit -m "feat: add safe organization plan with collision handling"
Zlał się jak Pull Request #17. Nagłówek PR nazywa od razu obie rzeczy: ten sam PR zamknął też Issue #6 (obsługa konfliktu nazw), pokazany na osobnej stronie 23-14, ponieważ jako lekcja to dwa różne, samodzielne pojęcia, nawet jeśli w repozytorium trafiły do tej samej gałęzi.
Status prawdziwego Project: Done