Add missing constraint.
[wolnelektury.git] / src / waiter / models.py
1 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
3 #
4 from os.path import join, isfile
5 from django.urls import reverse
6 from django.db import models
7 from waiter.settings import WAITER_URL, WAITER_MAX_QUEUE
8 from waiter.utils import check_abspath
9
10
11 class WaitedFile(models.Model):
12     path = models.CharField(max_length=255, unique=True, db_index=True)
13     task_id = models.CharField(max_length=128, db_index=True, null=True, blank=True)
14     description = models.CharField(max_length=255, null=True, blank=True)
15
16     @classmethod
17     def exists(cls, path):
18         """Returns opened file or None.
19
20         `path` is relative to WAITER_ROOT.
21         Won't open a path leading outside of WAITER_ROOT.
22         """
23         abs_path = check_abspath(path)
24         # Pre-fetch objects for deletion to avoid minor race condition
25         relevant = [o.id for o in cls.objects.filter(path=path)]
26         if isfile(abs_path):
27             cls.objects.filter(id__in=relevant).delete()
28             return True
29         else:
30             return False
31
32     @classmethod
33     def can_order(cls, path):
34         return (cls.objects.filter(path=path).exists() or
35                 cls.exists(path) or
36                 cls.objects.count() < WAITER_MAX_QUEUE
37                 )
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:
53                 task = task_creator(check_abspath(path), waited.pk)
54                 waited.task_id = task.task_id
55                 waited.description = description
56                 waited.save()
57             return reverse("waiter", args=[path])
58         return join(WAITER_URL, path)