1 from os.path import join, isfile
2 from django.core.urlresolvers import reverse
3 from django.db import models
4 from waiter.settings import WAITER_URL, WAITER_MAX_QUEUE
5 from waiter.utils import check_abspath
6 from picklefield import PickledObjectField
9 class WaitedFile(models.Model):
10 path = models.CharField(max_length=255, unique=True, db_index=True)
11 task_id = models.CharField(max_length=128, db_index=True, null=True, blank=True)
12 task = PickledObjectField(null=True, editable=False)
13 description = models.CharField(max_length=255, null=True, blank=True)
16 def exists(cls, path):
17 """Returns opened file or None.
19 `path` is relative to WAITER_ROOT.
20 Won't open a path leading outside of WAITER_ROOT.
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)]
26 cls.objects.filter(id__in=relevant).delete()
32 def can_order(cls, path):
33 return (cls.objects.filter(path=path).exists() or
35 cls.objects.count() < WAITER_MAX_QUEUE
40 # Race; just let the other task roll.
42 if self.task.status not in (u'PENDING', u'STARTED', u'SUCCESS', u'RETRY'):
47 def order(cls, path, task_creator, description=None):
49 Returns an URL for the user to follow.
50 If the file is ready, returns download URL.
51 If not, starts preparing it and returns waiting URL.
53 task_creator: function taking a path and generating the file;
54 description: a string or string proxy with a description for user;
56 already = cls.exists(path)
58 waited, created = cls.objects.get_or_create(path=path)
59 if created or waited.is_stale():
60 waited.task = task_creator(check_abspath(path))
61 waited.task_id = waited.task.task_id
62 waited.description = description
64 return reverse("waiter", args=[path])
65 return join(WAITER_URL, path)