1 from os.path import join, abspath, exists
2 from django.core.urlresolvers import reverse
3 from django.db import models
4 from waiter.settings import WAITER_ROOT, WAITER_URL
5 from djcelery.models import TaskMeta
8 class WaitedFile(models.Model):
9 path = models.CharField(max_length=255, unique=True, db_index=True)
10 task = models.CharField(max_length=64, null=True, editable=False)
11 description = models.CharField(max_length=255, null=True, blank=True)
15 abs_path = abspath(join(WAITER_ROOT, path))
16 if not abs_path.startswith(WAITER_ROOT):
17 raise ValueError('Path not inside WAITER_ROOT.')
21 def exists(cls, path):
22 """Returns opened file or None.
24 `path` is relative to WAITER_ROOT.
25 Won't open a path leading outside of WAITER_ROOT.
27 abs_path = cls.abspath(path)
28 # Pre-fetch objects for deletion to avoid minor race condition
29 relevant = [o.id for o in cls.objects.filter(path=path)]
31 cls.objects.filter(id__in=relevant).delete()
38 # Race; just let the other task roll.
41 meta = TaskMeta.objects.get(task_id=self.task)
42 assert meta.status in (u'PENDING', u'STARTED', u'SUCCESS', u'RETRY')
43 except TaskMeta.DoesNotExist:
44 # Might happen it's not yet there.
46 except AssertionError:
51 def order(cls, path, task_creator, description=None):
53 Returns an URL for the user to follow.
54 If the file is ready, returns download URL.
55 If not, starts preparing it and returns waiting URL.
57 task_creator: function taking a path and generating the file;
58 description: a string or string proxy with a description for user;
60 already = cls.exists(path)
62 waited, created = cls.objects.get_or_create(path=path)
63 if created or waited.is_stale():
64 waited.task = task_creator(cls.abspath(path))
65 waited.description = description
67 return reverse("waiter", args=[path])
68 return join(WAITER_URL, path)