mirror of
https://github.com/narrrl/holy-flavor-rater.git
synced 2026-11-03 16:41:27 +01:00
fixes
This commit is contained in:
1 parent
2b2b4cf48f
commit
22fe0674dd
22 files changed
+620
-286
No files matched your search
@@ -0,0 +1,94 @@
|
||||
"""Garbage collect orphan flavor image files.
|
||||
|
||||
Walks MEDIA_ROOT/flavors/ and removes directories whose paths are not referenced
|
||||
by any Flavor.local_image_paths or Flavor.main_image_path. Use --dry-run to
|
||||
preview without deleting.
|
||||
|
||||
Files referenced by the legacy Flavor.image ImageField are also preserved (for
|
||||
backwards compatibility) until that field is dropped.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from api.models import Flavor
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Remove orphan flavor image files / directories not referenced by any Flavor."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Print what would be deleted but don't touch the filesystem.",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dry_run: bool = options["dry_run"]
|
||||
flavors_root = Path(settings.MEDIA_ROOT) / "flavors"
|
||||
if not flavors_root.exists():
|
||||
self.stdout.write(self.style.WARNING(f"No flavors dir at {flavors_root}"))
|
||||
return
|
||||
|
||||
# Collect every relative path/directory that should be kept.
|
||||
referenced_files: set[str] = set()
|
||||
referenced_dirs: set[str] = set()
|
||||
legacy_image_files: set[str] = set()
|
||||
|
||||
for flavor in Flavor.objects.all().only(
|
||||
"local_image_paths", "main_image_path", "image"
|
||||
):
|
||||
paths = list(flavor.local_image_paths or [])
|
||||
if flavor.main_image_path:
|
||||
paths.append(flavor.main_image_path)
|
||||
for rel in paths:
|
||||
referenced_files.add(rel)
|
||||
referenced_dirs.add(os.path.dirname(rel))
|
||||
if flavor.image:
|
||||
legacy_image_files.add(flavor.image.name)
|
||||
|
||||
# Walk the flavors tree and decide what's unreferenced.
|
||||
deleted_dirs: list[str] = []
|
||||
deleted_files: list[str] = []
|
||||
kept = 0
|
||||
|
||||
for entry in sorted(flavors_root.iterdir()):
|
||||
rel_dir = f"flavors/{entry.name}"
|
||||
if entry.is_dir():
|
||||
if rel_dir in referenced_dirs:
|
||||
# Drop only orphan files inside the referenced dir.
|
||||
for f in entry.iterdir():
|
||||
rel_file = f"{rel_dir}/{f.name}"
|
||||
if f.is_file() and rel_file not in referenced_files:
|
||||
deleted_files.append(rel_file)
|
||||
if not dry_run:
|
||||
f.unlink()
|
||||
else:
|
||||
kept += 1
|
||||
else:
|
||||
deleted_dirs.append(rel_dir)
|
||||
if not dry_run:
|
||||
shutil.rmtree(entry)
|
||||
elif entry.is_file():
|
||||
rel_file = f"flavors/{entry.name}"
|
||||
if rel_file in legacy_image_files or rel_file in referenced_files:
|
||||
kept += 1
|
||||
else:
|
||||
deleted_files.append(rel_file)
|
||||
if not dry_run:
|
||||
entry.unlink()
|
||||
|
||||
verb = "Would delete" if dry_run else "Deleted"
|
||||
self.stdout.write(self.style.SUCCESS(f"{verb}: {len(deleted_dirs)} dirs, {len(deleted_files)} files"))
|
||||
for path in deleted_dirs:
|
||||
self.stdout.write(f" dir {path}")
|
||||
for path in deleted_files:
|
||||
self.stdout.write(f" file {path}")
|
||||
self.stdout.write(f"Kept: {kept} entries")
|
||||
Reference in new issue
Block a user