custom pdf in dirs
[wolnelektury.git] / apps / waiter / models.py
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
6
7
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)
12
13     @staticmethod
14     def abspath(path):
15         abs_path = abspath(join(WAITER_ROOT, path))
16         if not abs_path.startswith(WAITER_ROOT):
17             raise ValueError('Path not inside WAITER_ROOT.')
18         return abs_path
19
20     @classmethod
21     def exists(cls, path):
22         """Returns opened file or None.
23         
24         `path` is relative to WAITER_ROOT.
25         Won't open a path leading outside of WAITER_ROOT.
26         """
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)]
30         if exists(abs_path):
31             cls.objects.filter(id__in=relevant).delete()
32             return True
33         else:
34             return False
35
36     def is_stale(self):
37         if self.task is None:
38             # Race; just let the other task roll. 
39             return False
40         try:
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.
45             pass
46         except AssertionError:
47             return True
48         return False
49
50     @classmethod
51     def order(cls, path, task_creator, description=None):
52         """
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.
56
57         task_creator: function taking a path and generating the file;
58         description: a string or string proxy with a description for user;
59         """
60         already = cls.exists(path)
61         if not already:
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
66                 waited.save()
67             return reverse("waiter", args=[path])
68         return join(WAITER_URL, path)