diff --git a/hc/api/admin.py b/hc/api/admin.py index 10579deb..37b9e7e1 100644 --- a/hc/api/admin.py +++ b/hc/api/admin.py @@ -29,7 +29,7 @@ class ChecksAdmin(admin.ModelAdmin): search_fields = ["name", "user__email"] list_display = ("id", "name_tags", "created", "code", "status", "email", - "last_ping") + "last_ping", "n_pings") list_select_related = ("user", ) list_filter = ("status", OwnershipListFilter, "last_ping") actions = ["send_alert"] diff --git a/hc/api/management/commands/prunechecks.py b/hc/api/management/commands/prunechecks.py new file mode 100644 index 00000000..96936ff7 --- /dev/null +++ b/hc/api/management/commands/prunechecks.py @@ -0,0 +1,13 @@ +from datetime import timedelta + +from django.core.management.base import BaseCommand +from django.utils import timezone +from hc.api.models import Check + + +class Command(BaseCommand): + help = 'Prune anonymous checks older than 2 hours' + + def handle(self, *args, **options): + cutoff = timezone.now() - timedelta(hours=2) + Check.objects.filter(user=None, created__lt=cutoff).delete() diff --git a/hc/api/management/commands/prunepings.py b/hc/api/management/commands/prunepings.py new file mode 100644 index 00000000..31edf77f --- /dev/null +++ b/hc/api/management/commands/prunepings.py @@ -0,0 +1,29 @@ +from django.db.models import F +from django.contrib.auth.models import User +from django.core.management.base import BaseCommand +from hc.accounts.models import Profile +from hc.api.models import Check + + +class Command(BaseCommand): + help = 'Prune pings based on limits in user profiles' + + def handle(self, *args, **options): + + # Create any missing user profiles + for user in User.objects.filter(profile=None): + Profile.objects.for_user(user) + + # Select checks having n_ping greater than the limit in user profile + checks = Check.objects + checks = checks.annotate(limit=F("user__profile__ping_log_limit")) + checks = checks.filter(n_pings__gt=F("limit")) + + for check in checks: + n = check.prune_pings(check.limit) + print("---") + print("User: %s" % check.user.email) + print("Check: %s" % check.name) + print("Pruned: %d" % n) + + print("Done.") diff --git a/hc/api/models.py b/hc/api/models.py index c510e601..287bb9ba 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -94,6 +94,33 @@ class Check(models.Model): def tags_list(self): return self.tags.split(" ") + def prune_pings(self, keep_limit): + """ Prune pings for this check. + + If the check has more than `keep_limit` ping objects, prune the + oldest ones. Return the number of pruned pings. + + `keep_limit` specifies how many ping objects to keep. + + """ + + pings = Ping.objects.filter(owner=self).order_by("-created") + cutoff = pings[keep_limit:keep_limit+1] + + # If cutoff is empty slice then the check has less than `keep_limit` + # pings and there's nothing to prune yet. + if len(cutoff) == 0: + return 0 + + cutoff_date = cutoff[0].created + q = Ping.objects.filter(owner=self, created__lte=cutoff_date) + n_pruned, _ = q.delete() + + self.n_pings = keep_limit + self.save(update_fields=("n_pings", )) + + return n_pruned + class Ping(models.Model): owner = models.ForeignKey(Check) diff --git a/requirements.txt b/requirements.txt index e9271027..d2fd8cff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ django-appconf==1.0.1 django-ses-backend==0.1.1 Django==1.9 -django_compressor==1.5 +django_compressor==1.6 djmail==0.11.0 futures==3.0.3 premailer==2.9.6