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)