move a bunch of things to celery
[wolnelektury.git] / apps / waiter / models.py
1 from os.path import join, isfile
2 from django.core.urlresolvers import reverse
3 from django.db import models
4 from djcelery.models import TaskMeta
5 from waiter.settings import WAITER_URL
6 from waiter.utils import check_abspath
7 from picklefield import PickledObjectField
8
9
10 class WaitedFile(models.Model):
11     path = models.CharField(max_length=255, unique=True, db_index=True)
12     task = PickledObjectField(null=True, editable=False)
13     description = models.CharField(max_length=255, null=True, blank=True)
14
15     @classmethod
16     def exists(cls, path):
17         """Returns opened file or None.
18         
19         `path` is relative to WAITER_ROOT.
20         Won't open a path leading outside of WAITER_ROOT.
21         """
22         abs_path = check_abspath(path)
23         # Pre-fetch objects for deletion to avoid minor race condition
24         relevant = [o.id for o in cls.objects.filter(path=path)]
25         if isfile(abs_path):
26             cls.objects.filter(id__in=relevant).delete()
27             return True
28         else:
29             return False
30
31     def is_stale(self):
32         if self.task is None:
33             # Race; just let the other task roll. 
34             return False
35         if self.task.status not in (u'PENDING', u'STARTED', u'SUCCESS', u'RETRY'):
36             return True
37         return False
38
39     @classmethod
40     def order(cls, path, task_creator, description=None):
41         """
42         Returns an URL for the user to follow.
43         If the file is ready, returns download URL.
44         If not, starts preparing it and returns waiting URL.
45
46         task_creator: function taking a path and generating the file;
47         description: a string or string proxy with a description for user;
48         """
49         already = cls.exists(path)
50         if not already:
51             waited, created = cls.objects.get_or_create(path=path)
52             if created or waited.is_stale():
53                 waited.task = task_creator(check_abspath(path))
54                 waited.description = description
55                 waited.save()
56             return reverse("waiter", args=[path])
57         return join(WAITER_URL, path)