Completed
Push — master ( 8081d9...05c9ef )
by Bart
23:30 queued 23:30
created

validate_organisation_image()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 2
rs 10
cc 1
1
"""
2
Models that extend mezzanine Pages and add JD specific data.
3
"""
4
5
from datetime import datetime
6
import os
7
from string import punctuation
8
import logging
9
10
from PIL import Image
11
12
from django.core.exceptions import ValidationError
13
from django.db import models
14
from django.db.models import Q
15
from django.utils import timezone
16
from django.conf import settings
17
18
from mezzanine.blog.models import BlogCategory, BlogPost
19
from mezzanine.core.fields import FileField
20
from mezzanine.core.fields import RichTextField
21
from mezzanine.core.models import Orderable, RichText, SiteRelated
22
from mezzanine.core.models import CONTENT_STATUS_PUBLISHED
23
from mezzanine.pages.models import Page
24
25
logger = logging.getLogger(__name__)
26
27
28
class FooterLinks(SiteRelated):
29
    title = models.CharField(max_length=100, blank=True, default="")
30
31
    def __str__(self):
32
        return self.title
33
34
35
class FooterLink(SiteRelated, Orderable):
36
    title = models.CharField(max_length=100, blank=True, default="")
37
    url = models.CharField(max_length=500, blank=True, default="")
38
    footer_links = models.ForeignKey(FooterLinks, blank=True, null=True)
39
40
    def __str__(self):
41
        return self.title
42
43
44
class FooterInfo(SiteRelated):
45
    title = models.CharField(max_length=100, blank=True, default="")
46
    content = RichTextField()
47
48
    def __str__(self):
49
        return self.title
50
51
52
class Footer(SiteRelated):
53
    links_left = models.OneToOneField(FooterLinks, auto_created=True, related_name="links_left")
54
    links_middle = models.OneToOneField(FooterLinks, auto_created=True, related_name="links_right")
55
    info_right = models.OneToOneField(FooterInfo, auto_created=True)
56
57
    class Meta:
58
        verbose_name = "Footer"
59
        verbose_name_plural = "Footer"
60
61
62
def validate_header_image(imagepath):
63
    """ Validates the resolution of a header image. """
64
    absolute_imagepath = os.path.join(settings.MEDIA_ROOT, str(imagepath))
65
    if not os.path.exists(absolute_imagepath):
66
        raise ValidationError(
67
            'The file for this header does not exist anymore. Please remove or replace this header before saving the page.')
68
    im = Image.open(absolute_imagepath)
69
    width, height = im.size
70
    aspect_ratio = width/height
71
    if aspect_ratio < 2.0:
72
        raise ValidationError('Image aspect ratio should be at least 2 (for example 2000x1000px). The selected image is %i x %i. Please resize the image.' % (width, height))
73
    if width < 1000:
74
        raise ValidationError('Image resolution is too low. It should be at least 1000px wide. The selected image is %i x %i. Please find a larger image.' % (width, height))
75
76
77
class PageHeaderImage(SiteRelated):
78
    """ Page header image. """
79
    name = models.CharField(max_length=1000, blank=True, null=False, default="")
80
    page = models.ForeignKey(Page, blank=False, null=True)
81
    image = FileField(max_length=200, format="Image", validators=[validate_header_image])
82
83
84
class PageItem(SiteRelated):
85
    page = models.ForeignKey(Page, blank=False, null=True)
86
    visible = models.BooleanField(default=True)
87
88
    class Meta:
89
        abstract = True
90
91
92
class SidebarAgenda(PageItem):
93
    SITE = 'SI'
94
    ALL = 'AL'
95
    MAIN = 'MA'
96
    MAIN_AND_SITE = 'SM'
97
98
    EVENT_CHOICES = (
99
        (SITE, 'Site'),
100
        (ALL, 'All'),
101
        (MAIN, 'Main site'),
102
        (MAIN_AND_SITE, 'Main and site'),
103
    )
104
105
    type = models.CharField(max_length=2, choices=EVENT_CHOICES)
106
107
    class Meta:
108
        verbose_name = "Sidebar Agenda Item"
109
110
    def get_name(self):
111
        if self.type == self.MAIN_AND_SITE:
112
            return 'Events for current and main site'
113
        elif self.type == self.ALL:
114
            return 'Events for all sites'
115
        elif self.type == self.SITE:
116
            return 'Events for current site'
117
        elif self.type == self.MAIN:
118
            return 'Events for main site'
119
        assert False
120
121
    def __str__(self):
122
        return self.get_name()
123
124
125
class SidebarTwitter(PageItem):
126
127
    class Meta:
128
        verbose_name = "Sidebar Twitter Item"
129
130
131
class SidebarSocial(PageItem):
132
133
    @property
134
    def urls(self):
135
        smulrs = SocialMediaUrls.objects.all()
136
        if smulrs.exists():
137
            return smulrs[0]
138
        return None
139
140
    class Meta:
141
        verbose_name = "Sidebar Social Media Item"
142
143
144
class SidebarRichText(PageItem):
145
    title = models.CharField(max_length=100, blank=True, default="")
146
    content = RichTextField()
147
148
    class Meta:
149
        verbose_name = "Sidebar RichText Item"
150
151
    def __str__(self):
152
        return self.title
153
154
155
class SidebarLink(PageItem, Orderable):
156
    title = models.CharField(max_length=100, blank=True, default="")
157
    url = models.CharField(max_length=500, blank=True, default="")
158
159
160
class ActionBanner(PageItem):
161
    title = models.CharField(max_length=500, blank=True, default="")
162
    content = RichTextField()
163
    image = FileField(max_length=300, format="Image")
164
    button_title = models.CharField(max_length=500, blank=True, default="")
165
    button_url = models.CharField(max_length=500, blank=True, default="")
166
167
    def __str__(self):
168
        return self.title
169
170
171
def validate_images_aspect_ratio(imagepath, required_aspect_ratio, max_difference):
172
    """ Validates the aspect ratio of an image. """
173
    absolute_imagepath = os.path.join(settings.MEDIA_ROOT, str(imagepath))
174
    im = Image.open(absolute_imagepath)
175
    width, height = im.size
176
    aspect_ratio = width/height
177
    if abs(aspect_ratio - required_aspect_ratio) > max_difference:
178
        raise ValidationError('Image aspect ratio should be %i, selected image is %i x %i. Please resize the image.' % (required_aspect_ratio, width, height))
179
180
181
def validate_vision_image(imagepath):
182
    validate_images_aspect_ratio(imagepath, required_aspect_ratio=1.5, max_difference=0.1)
183
184
185
class VisionPage(Page, RichText):
186
    """
187
    """
188
    image = FileField(max_length=300, format="Image", blank=True, default="", validators=[validate_vision_image])
189
190
    class Meta:
191
        verbose_name = 'Standpunt pagina'
192
        verbose_name_plural = "Standpunt paginas"
193
194
195
class VisionsPage(Page, RichText):
196
    """
197
    """
198
    vision_pages = models.ManyToManyField(VisionPage, blank=True, verbose_name="Standpunt pagina's")
199
200
    class Meta:
201
        verbose_name = 'Standpunten pagina'
202
        verbose_name_plural = "Standpunten paginas"
203
204
205
def validate_organisation_image(imagepath):
206
    validate_images_aspect_ratio(imagepath, required_aspect_ratio=1.5, max_difference=0.1)
207
208
209
class OrganisationPartPage(Page, RichText):
210
    """
211
    """
212
    image = FileField(max_length=300, format="Image", blank=True, default="", validators=[validate_organisation_image])
213
214
    class Meta:
215
        verbose_name = 'Organisatie-onderdeel pagina'
216
        verbose_name_plural = "Organisatie-onderdeel paginas"
217
218
219
class OrganisationPage(Page, RichText):
220
    """
221
    """
222
    organisation_part_pages = models.ManyToManyField(OrganisationPartPage, blank=True, verbose_name="Organisatie onderdelen")
223
224
    class Meta:
225
        verbose_name = 'Organisatie pagina'
226
        verbose_name_plural = "Organisatie paginas"
227
228
229
class OrganisationMember(SiteRelated):
230
    """
231
    """
232
    name = models.CharField(max_length=200, blank=False, default="")
233
    content = RichTextField()
234
    image = FileField(max_length=300, format="Image", blank=True, default="")
235
    email = models.EmailField(blank=True, default="")
236
    facebook_url = models.URLField(blank=True, default="")
237
    twitter_url = models.URLField(blank=True, default="")
238
239
    class Meta:
240
        verbose_name = 'Organisatie lid'
241
        verbose_name_plural = "Organisatie leden"
242
243
    def __str__(self):
244
        return self.name
245
246
247
class OrganisationPartMember(SiteRelated):
248
    member = models.ForeignKey(OrganisationMember)
249
    organisation_part = models.ForeignKey(OrganisationPartPage, null=True, blank=True)
250
    role = models.CharField(max_length=200, blank=False, default="")
251
252
    class Meta:
253
        verbose_name = 'Organisatie functie'
254
        verbose_name_plural = "Organisatie functies"
255
256
    def __str__(self):
257
        return self.role + ' - ' + self.member.name
258
259
260
class HomePage(Page, RichText):
261
    """
262
    Page model for the site homepage.
263
    Only works properly when url points to the homepage '/' as url.
264
    """
265
    header_title = models.CharField(max_length=300, blank=True, default="")
266
    header_subtitle = models.CharField(max_length=500, blank=True, default="")
267
    news_category = models.ForeignKey(BlogCategory, null=True, blank=True)
268
    vision_pages = models.ManyToManyField(VisionPage, blank=True, verbose_name="Standpunt pagina's")
269
270
    @property
271
    def blog_posts(self):
272
        return get_public_blogposts(self.news_category)
273
274
    class Meta:
275
        verbose_name = 'Home pagina'
276
        verbose_name_plural = "Home paginas"
277
278
279
class BlogCategoryPage(Page, RichText):
280
    """
281
    Model for a page that displays a list of posts in a single blog category.
282
    """
283
284
    blog_category = models.ForeignKey(BlogCategory, null=False, blank=False)
285
    show_excerpt = models.BooleanField(default=False, null=False, blank=False,
286
                                       help_text='Show only the first paragraph of a blog post.')
287
288
    class Meta:
289
        verbose_name = "Blog categorie pagina"
290
        verbose_name_plural = "Blog categorie paginas"
291
292
293
def get_public_blogposts(blog_category):
294
    """ Returns all blogposts for a given category that are published and not expired. """
295
    blog_posts = BlogPost.objects.all().filter(categories=blog_category).filter(status=CONTENT_STATUS_PUBLISHED)
296
    return blog_posts.filter(publish_date__lte=timezone.now()).filter(Q(expiry_date__isnull=True)
297
                                                                      | Q(expiry_date__gte=timezone.now()))
298
299
300
class SocialMediaUrls(SiteRelated):
301
    facebook_url = models.URLField(max_length=300, blank=True, default="")
302
    twitter_url = models.URLField(max_length=300, blank=True, default="")
303
    youtube_url = models.URLField(max_length=300, blank=True, default="")
304
    linkedin_url = models.URLField(max_length=300, blank=True, default="")
305
    instagram_url = models.URLField(max_length=300, blank=True, default="")
306
307
    class Meta:
308
        verbose_name = "Social media urls"
309
        verbose_name_plural = "Social media urls"
310
311
312
class WordLidPage(Page, RichText):
313
    """
314
    """
315
    
316
    class Meta:
317
        verbose_name = 'WordLidPage'
318
319
320
class ThatsWhyItem(PageItem, Orderable):
321
    title = models.CharField(max_length=100, blank=True, default="")
322
323
    class Meta:
324
        verbose_name = "That's why item"
325
        verbose_name_plural = "That's why items"
326
327
    def __str__(self):
328
        return self.title
329
330