fix banner sizing
[wolnelektury.git] / src / annoy / models.py
1 import hashlib
2 import json
3 from django.apps import apps
4 from django.conf import settings
5 from django.db import models
6 from django.template import Context, Template
7 from django.utils.timezone import now
8 from .places import PLACES, PLACE_CHOICES, STYLES
9
10
11 class Campaign(models.Model):
12     name = models.CharField(max_length=255, help_text='Dla zespołu')
13     image = models.FileField('obraz', upload_to='annoy/banners/', blank=True)
14
15     def __str__(self):
16         return self.name
17
18
19 class Banner(models.Model):
20     place = models.SlugField('miejsce', choices=PLACE_CHOICES)
21     campaign = models.ForeignKey(Campaign, models.PROTECT, null=True, blank=True)
22
23     style = models.CharField(
24         'styl', max_length=255, blank=True,
25         choices=STYLES,
26     )
27     smallfont = models.BooleanField('mały font', default=False)
28     text_color = models.CharField(max_length=10, blank=True)
29     background_color = models.CharField(max_length=10, blank=True)
30     action_label = models.CharField(
31         'etykieta akcji',
32         max_length=255, blank=True,
33         help_text='Jeśli pusta, cały banner będzie służył jako link.'
34     )
35     open_label = models.CharField('etykieta otwierania', max_length=255, blank=True)
36     close_label = models.CharField('etykieta zamykania', max_length=255, blank=True)
37     text = models.TextField('tekst')
38     image = models.FileField('obraz', upload_to='annoy/banners/', blank=True)
39     url = models.CharField('URL', max_length=1024)
40     priority = models.PositiveSmallIntegerField(
41         'priorytet', default=0,
42         help_text='Bannery z wyższym priorytetem mają pierwszeństwo.')
43     since = models.DateTimeField('od', null=True, blank=True)
44     until = models.DateTimeField('do', null=True, blank=True)
45     books = models.ManyToManyField('catalogue.Book', blank=True)
46
47     target = models.IntegerField('cel', null=True, blank=True)
48     progress = models.IntegerField('postęp', null=True, blank=True)
49     show_members = models.BooleanField('widoczny dla członków klubu', default=False)
50     staff_preview = models.BooleanField('podgląd tylko dla zespołu', default=False)
51     only_authenticated = models.BooleanField('tylko dla zalogowanych', default=False)
52
53     class Meta:
54         verbose_name = 'banner'
55         verbose_name_plural = 'bannery'
56         ordering = ('place', '-priority',)
57
58     def __str__(self):
59         return self.text
60
61     def get_text(self):
62         return Template(self.text).render(Context())
63
64     def get_image(self):
65         if self.campaign and self.campaign.image:
66             return self.campaign.image
67         else:
68             return self.image
69     
70     @classmethod
71     def choice(cls, place, request, exemptions=True, book=None):
72         Membership = apps.get_model('club', 'Membership')
73
74         if exemptions and hasattr(request, 'annoy_banner_exempt'):
75             return cls.objects.none()
76
77         if settings.DEBUG:
78             assert place in PLACES, f"Banner place `{place}` must be defined in annoy.places."
79
80         n = now()
81         banners = cls.objects.filter(
82             place=place
83         ).exclude(
84             since__gt=n
85         ).exclude(
86             until__lt=n
87         ).order_by('-priority', '?')
88
89         if book is None:
90             banners = banners.filter(books=None)
91         else:
92             banners = banners.filter(models.Q(books=None) | models.Q(books=book))
93             
94         
95         if not request.user.is_authenticated:
96             banners = banners.filter(only_authenticated=False)
97
98         if not request.user.is_staff:
99             banners = banners.filter(staff_preview=False)
100
101         if Membership.is_active_for(request.user):
102             banners = banners.filter(show_members=True)
103
104         return banners
105
106     @property
107     def progress_percent(self):
108         if not self.target:
109             return 0
110         return (self.progress or 0) / self.target * 100
111
112     @property
113     def progress_percent_pretty(self):
114         return int(self.progress_percent)
115
116     def update_progress(self):
117         # Total of new payments during the action.
118         # This definition will need to change for longer timespans.
119         if not self.since or not self.until or not self.target:
120             return
121         Schedule = apps.get_model('club', 'Schedule')
122         self.progress = Schedule.objects.filter(
123             payed_at__gte=self.since,
124             payed_at__lte=self.until,
125         ).aggregate(c=models.Sum('amount'))['c']
126         self.save(update_fields=['progress'])
127
128     @classmethod
129     def update_all_progress(cls):
130         for obj in cls.objects.exclude(target=None):
131             obj.update_progress()
132
133
134 class DynamicTextInsert(models.Model):
135     paragraphs = models.IntegerField('akapity')
136     url = models.CharField(max_length=1024)
137
138     class Meta:
139         verbose_name = 'dynamiczna wstawka'
140         verbose_name_plural = 'dynamiczne wstawki'
141         ordering = ('paragraphs', )
142
143     def __str__(self):
144         return str(self.paragraphs)
145
146     @classmethod
147     def get_all(cls, request):
148         Membership = apps.get_model('club', 'Membership')
149         if Membership.is_active_for(request.user) and not request.user.is_staff:
150             return cls.objects.none()
151         return cls.objects.all()
152
153
154     def choose(self):
155         return self.dynamictextinserttext_set.order_by('?').first()
156
157
158 class DynamicTextInsertText(models.Model):
159     insert = models.ForeignKey(DynamicTextInsert, models.CASCADE)
160     own_colors = models.BooleanField(default=False)
161     background_color = models.CharField(max_length=10, blank=True)
162     text_color = models.CharField(max_length=10, blank=True)
163     text = models.TextField('tekst')
164     image = models.FileField(blank=True, upload_to='annoy/inserts/')
165
166
167 class MediaInsertSet(models.Model):
168     file_format = models.CharField(max_length=8, choices=[
169         ('epub', 'epub'),
170         ('mobi', 'mobi'),
171         ('pdf', 'pdf'),
172         ])
173     etag = models.CharField(max_length=64, blank=True)
174
175     def update_etag(self):
176         self.etag = hashlib.sha1(json.dumps(self.get_texts()).encode('utf-8')).hexdigest()
177         self.save(update_fields=['etag'])
178
179     def get_texts(self):
180         return [t.text for t in self.mediainserttext_set.all()]
181
182     @classmethod
183     def get_for_format(cls, file_format):
184         return cls.objects.filter(file_format=file_format).first()
185
186     @classmethod
187     def get_texts_for(cls, file_format):
188         self = cls.get_for_format(file_format)
189         if self is None:
190             return []
191         return self.get_texts()
192
193
194 class MediaInsertText(models.Model):
195     media_insert_set = models.ForeignKey(MediaInsertSet, models.CASCADE)
196     ordering = models.IntegerField()
197     text = models.TextField()
198
199     class Meta:
200         ordering = ('ordering',)