""" Management-Command für den Retention-Cleanup. Nutzbar für: - Dry-Run-Audits ("welche Jobs würden gelöscht werden?") - Ad-hoc-Ausführung außerhalb des Beat-Schedules (z.B. nach DSGVO-Anfrage) - Smoke-Test in CI Beispiele: docker compose exec web python manage.py cleanup_jobs --dry-run docker compose exec web python manage.py cleanup_jobs --days 7 docker compose exec web python manage.py cleanup_jobs """ from __future__ import annotations from django.core.management.base import BaseCommand, CommandError from mailmerge.services.retention import cleanup_expired_jobs class Command(BaseCommand): help = "Löscht abgelaufene MailMergeJobs (Status DONE/FAILED, älter als Retention-Frist)." def add_arguments(self, parser): parser.add_argument( "--days", type=int, default=None, help="Überschreibt JOB_RETENTION_DAYS aus den Settings.", ) parser.add_argument( "--dry-run", action="store_true", help="Nur Kandidaten zählen, nichts löschen.", ) def handle(self, *args, **options): days = options["days"] dry_run = options["dry_run"] try: result = cleanup_expired_jobs(retention_days=days, dry_run=dry_run) except ValueError as exc: raise CommandError(str(exc)) from exc self.stdout.write(f"Stichtag : {result.cutoff.isoformat()}") self.stdout.write(f"Dry-Run : {result.dry_run}") self.stdout.write(f"Kandidaten : {result.candidates}") self.stdout.write(f"Gelöschte Jobs : {result.deleted_jobs}") self.stdout.write(f"Gelöschte Files: {result.deleted_files}") if result.errors: self.stdout.write(self.style.WARNING(f"Fehler ({len(result.errors)}):")) for err in result.errors: self.stdout.write(self.style.WARNING(f" - {err}")) elif not dry_run: self.stdout.write(self.style.SUCCESS("Cleanup erfolgreich."))