api: cw, audiences, elevenreader
[wolnelektury.git] / src / api / models.py
1 # This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
3 #
4 from datetime import timedelta
5 import uuid
6 from django.conf import settings
7 from django.contrib.auth.models import User
8 from django.contrib.contenttypes.models import ContentType
9 from django.db import models
10 from django.db.models.signals import pre_delete
11 from django.utils import timezone
12 from catalogue.models import Book, Tag
13
14
15 class Deleted(models.Model):
16     object_id = models.IntegerField()
17     slug = models.SlugField('slug', max_length=120, blank=True, db_index=True)
18     content_type = models.ForeignKey(ContentType, models.CASCADE)
19     category = models.CharField(max_length=64, null=True, blank=True, db_index=True)
20     created_at = models.DateTimeField(editable=False, db_index=True)
21     deleted_at = models.DateTimeField(auto_now_add=True, db_index=True)
22
23     class Meta:
24         unique_together = (('content_type', 'object_id'),)
25
26
27 def _pre_delete_handler(sender, instance, **kwargs):
28     """ save deleted objects for change history purposes """
29
30     if sender in (Book, Tag):
31         if sender == Tag:
32             if instance.category in ('book', 'set'):
33                 return
34             category = instance.category
35         else:
36             category = None
37         content_type = ContentType.objects.get_for_model(sender)
38         Deleted.objects.create(
39             content_type=content_type,
40             object_id=instance.id,
41             created_at=instance.created_at,
42             category=category,
43             slug=instance.slug
44         )
45 pre_delete.connect(_pre_delete_handler)
46
47
48 class BookUserData(models.Model):
49     book = models.ForeignKey(Book, models.CASCADE)
50     user = models.ForeignKey(User, models.CASCADE)
51     complete = models.BooleanField(default=False)
52     last_changed = models.DateTimeField(auto_now=True)
53
54     class Meta:
55         unique_together = [('user', 'book')]
56
57     @property
58     def state(self):
59         return 'complete' if self.complete else 'reading'
60
61     @classmethod
62     def update(cls, book, user, state):
63         instance, created = cls.objects.get_or_create(book=book, user=user)
64         instance.complete = state == 'complete'
65         instance.save()
66         return instance
67
68
69 KEY_SIZE = 18
70 SECRET_SIZE = 32
71
72 CONSUMER_STATES = (
73     ('pending', 'Pending approval'),
74     ('accepted', 'Accepted'),
75     ('canceled', 'Canceled'),
76 )
77
78
79 class Nonce(models.Model):
80     token_key = models.CharField(max_length=KEY_SIZE)
81     consumer_key = models.CharField(max_length=KEY_SIZE)
82     key = models.CharField(max_length=255)
83
84     def __str__(self):
85         return "Nonce %s for %s" % (self.key, self.consumer_key)
86
87
88 class Consumer(models.Model):
89     name = models.CharField(max_length=255)
90     description = models.TextField()
91     key = models.CharField(max_length=KEY_SIZE)
92     secret = models.CharField(max_length=SECRET_SIZE)
93     status = models.CharField(max_length=16, choices=CONSUMER_STATES, default='pending')
94     user = models.ForeignKey(
95         settings.AUTH_USER_MODEL, models.CASCADE,
96         null=True, blank=True, related_name='consumers'
97     )
98
99     def __str__(self):
100         return "Consumer %s with key %s" % (self.name, self.key)
101
102
103 class Token(models.Model):
104     REQUEST = 1
105     ACCESS = 2
106     REFRESH = 3
107     TOKEN_TYPES = (
108         (REQUEST, 'Request'),
109         (ACCESS, 'Access'),
110         (REFRESH, 'Refresh')
111     )
112
113     key = models.CharField(max_length=KEY_SIZE)
114     secret = models.CharField(max_length=SECRET_SIZE)
115     token_type = models.IntegerField(choices=TOKEN_TYPES)
116     timestamp = models.IntegerField()
117     is_approved = models.BooleanField(default=False)
118     user = models.ForeignKey(
119         settings.AUTH_USER_MODEL, models.CASCADE,
120         null=True, blank=True, related_name='tokens'
121     )
122     consumer = models.ForeignKey(Consumer, models.CASCADE, null=True, blank=True)
123
124     def __str__(self):
125         return "%s Token %s for %s" % (self.get_token_type_display(), self.key, self.consumer)
126
127
128 class SessionTransferToken(models.Model):
129     token = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
130     user = models.ForeignKey(
131         settings.AUTH_USER_MODEL, models.CASCADE)
132     created_at = models.DateTimeField(auto_now_add=True)
133     expires_at = models.DateTimeField()
134     used = models.BooleanField(default=False)
135
136     @classmethod
137     def create_for_user(cls, user, lifetime_seconds=30):
138         return cls.objects.create(
139             user=user,
140             expires_at=timezone.now() + timedelta(seconds=lifetime_seconds)
141         )
142
143     def is_valid(self):
144         if self.used:
145             return False
146         if timezone.now() > self.expires_at:
147             return False
148         return True