mirror of
https://github.com/encounter/decomp.me.git
synced 2026-03-30 11:06:27 -07:00
07eea75181
* Oops * undo * fixup down.html behaviour * text/html is in gzip_types by defaut, so remove this to prevent log warning * Apply down.html behaviour to dev docker setup * ... and one more thing
93 lines
2.5 KiB
Python
Executable File
93 lines
2.5 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import argparse
|
|
import os
|
|
import datetime
|
|
from typing import Type
|
|
|
|
import django
|
|
from django.apps import apps
|
|
from django.db.models import Exists, OuterRef, Model, QuerySet
|
|
|
|
|
|
def get_model(model_name: str) -> Type[Model]:
|
|
return apps.get_model("coreapp", model_name.capitalize())
|
|
|
|
|
|
def perform_delete(qs: QuerySet[Model], dry_run: bool = False) -> int:
|
|
count = qs.count()
|
|
if dry_run:
|
|
return count
|
|
|
|
deleted, _ = qs.delete()
|
|
return deleted
|
|
|
|
|
|
def remove_ownerless_scratches(
|
|
cutoff_datetime: datetime.datetime, dry_run: bool = False
|
|
) -> int:
|
|
Scratch = get_model("Scratch")
|
|
|
|
to_delete = Scratch.objects.filter( # type: ignore[attr-defined]
|
|
owner__isnull=True, creation_time__lt=cutoff_datetime
|
|
)
|
|
return perform_delete(to_delete, dry_run=dry_run)
|
|
|
|
|
|
def remove_anonymous_profiles(
|
|
cutoff_datetime: datetime.datetime, dry_run: bool = False
|
|
) -> int:
|
|
Profile = get_model("Profile")
|
|
Scratch = get_model("Scratch")
|
|
|
|
to_delete = Profile.objects.annotate( # type: ignore[attr-defined]
|
|
has_scratch=Exists(Scratch.objects.filter(owner=OuterRef("pk"))) # type: ignore[attr-defined]
|
|
).filter(user__isnull=True, creation_date__lt=cutoff_datetime, has_scratch=False)
|
|
return perform_delete(to_delete, dry_run=dry_run)
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(
|
|
description="Decomp.me database housekeeping script"
|
|
)
|
|
parser.add_argument(
|
|
"--dry-run",
|
|
action="store_true",
|
|
help="Simulate deletions without applying them",
|
|
)
|
|
parser.add_argument(
|
|
"--cutoff-days",
|
|
type=int,
|
|
default=1,
|
|
help="Threshold in days; only delete items older than this",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "decompme.settings")
|
|
django.setup()
|
|
|
|
dry_run: bool = args.dry_run
|
|
cutoff_days: int = args.cutoff_days
|
|
assert cutoff_days >= 1, "--cutoff-days must be >= 1"
|
|
|
|
cutoff_datetime = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(
|
|
days=cutoff_days
|
|
)
|
|
|
|
prefix = "[DRYRUN] " if dry_run else ""
|
|
|
|
for text, func in [
|
|
("Owner-less Scratches", remove_ownerless_scratches),
|
|
("Scratch-less Profiles", remove_anonymous_profiles),
|
|
]:
|
|
print(f"{prefix}Cleaning up {text}... ", end="")
|
|
deleted = func(cutoff_datetime, dry_run=dry_run)
|
|
if deleted:
|
|
print(f"{deleted:,} entry(s) removed")
|
|
else:
|
|
print("No entries found!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|