From 10118935435e8d26aa1b48075360e5a7730cce09 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Sat, 26 Mar 2016 19:10:31 +0100 Subject: [PATCH] Fixed #107 and fixed #125 -- Periodic cleanup tasks --- doc/admin/installation.rst | 11 +++++++++- src/pretix/base/__init__.py | 2 +- src/pretix/base/management/__init__.py | 0 .../base/management/commands/__init__.py | 0 .../base/management/commands/runperiodic.py | 12 +++++++++++ src/pretix/base/models/orders.py | 5 +++++ src/pretix/base/services/cleanup.py | 21 +++++++++++++++++++ src/pretix/base/signals.py | 10 +++++++++ 8 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/pretix/base/management/__init__.py create mode 100644 src/pretix/base/management/commands/__init__.py create mode 100644 src/pretix/base/management/commands/runperiodic.py create mode 100644 src/pretix/base/services/cleanup.py diff --git a/doc/admin/installation.rst b/doc/admin/installation.rst index f5d45bedff..a47b3907e0 100644 --- a/doc/admin/installation.rst +++ b/doc/admin/installation.rst @@ -9,6 +9,7 @@ To use pretix, the most minimal setup consists of: * **pretix** and the python packages it depends on * An **WSGI application server** (we recommend gunicorn) +* A periodic task runner, e.g. ``cron`` You get those two bundled in the ``pretix/standalone`` docker image. @@ -39,4 +40,12 @@ If you want to use one of the payment providers shipping with pretix, you should ``pip install -r requirements/.txt`` where ```` is one of ``banktransfer``, ``paypal`` or ``stripe``. We will provide a step-by-step tutorial with the first stable release, but all configuration -already :ref:`is documented `. \ No newline at end of file +already :ref:`is documented `. + +Set up a cronjob +---------------- + +You need to set up a cronjob that runs the management command ``runperiodic``. The exact interval is not important +but should be something between every minute and every hour. You could for example configure cron like this:: + + 15,45 * * * * python3 /path/to/pretix/manage.py runperiodic \ No newline at end of file diff --git a/src/pretix/base/__init__.py b/src/pretix/base/__init__.py index 13d6de6b37..3017e2b18f 100644 --- a/src/pretix/base/__init__.py +++ b/src/pretix/base/__init__.py @@ -8,7 +8,7 @@ class PretixBaseConfig(AppConfig): def ready(self): from . import exporter # NOQA from . import payment # NOQA - from .services import export, mail, tickets, cart, orders # NOQA + from .services import export, mail, tickets, cart, orders, cleanup # NOQA try: from .celery import app as celery_app # NOQA diff --git a/src/pretix/base/management/__init__.py b/src/pretix/base/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/pretix/base/management/commands/__init__.py b/src/pretix/base/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/pretix/base/management/commands/runperiodic.py b/src/pretix/base/management/commands/runperiodic.py new file mode 100644 index 0000000000..9da53f359b --- /dev/null +++ b/src/pretix/base/management/commands/runperiodic.py @@ -0,0 +1,12 @@ +from django.core.management import call_command +from django.core.management.base import BaseCommand + +from ...signals import periodic_task + + +class Command(BaseCommand): + help = "Run periodic tasks" + + def handle(self, *args, **options): + periodic_task.send(self) + call_command('clearsessions') diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 5f41bf2aca..611d489e01 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -299,6 +299,11 @@ class QuestionAnswer(models.Model): else: return self.answer + def save(self, *args, **kwargs): + if self.orderposition and self.cartposition: + raise ValueError('QuestionAnswer cannot be linked to an order and a cart position at the same time.') + super().save(*args, **kwargs) + class AbstractPosition(models.Model): """ diff --git a/src/pretix/base/services/cleanup.py b/src/pretix/base/services/cleanup.py new file mode 100644 index 0000000000..235970c868 --- /dev/null +++ b/src/pretix/base/services/cleanup.py @@ -0,0 +1,21 @@ +from datetime import timedelta + +from django.dispatch import receiver +from django.utils.timezone import now + +from ..models import CachedFile, CartPosition, InvoiceAddress +from ..signals import periodic_task + + +@receiver(signal=periodic_task) +def clean_cart_positions(sender, **kwargs): + for cp in CartPosition.objects.filter(expires__lt=now() - timedelta(days=14)): + cp.delete() + for ia in InvoiceAddress.objects.filter(order__isnull=True, last_modified__lt=now() - timedelta(days=14)): + ia.delete() + + +@receiver(signal=periodic_task) +def clean_cached_files(sender, **kwargs): + for cf in CachedFile.objects.filter(expires__isnull=False, expires__lt=now()): + cf.delete() diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py index 1b422ce5c4..5d982f121d 100644 --- a/src/pretix/base/signals.py +++ b/src/pretix/base/signals.py @@ -95,3 +95,13 @@ don't know how to turn it into human-readable text. logentry_display = EventPluginSignal( providing_args=["logentry"] ) + + +""" +This is a regular django signal (no pretix event signal) that we send out every +time the periodic task cronjob runs. This interval is not sharply defined, it can +be everything between a minute and a day. The actions you perform should be +idempotent, i.e. it should not make a difference if this is send out more often +than expected. +""" +periodic_task = django.dispatch.Signal()