gremmie@1
|
1 """
|
gremmie@1
|
2 Models for the Photo Of The Day (POTD) application.
|
bgneal@515
|
3
|
gremmie@1
|
4 """
|
bgneal@535
|
5 import datetime
|
gremmie@1
|
6 import os
|
gremmie@1
|
7 from PIL import ImageFile
|
gremmie@1
|
8 from PIL import Image
|
gremmie@1
|
9 try:
|
gremmie@1
|
10 from cStringIO import StringIO
|
gremmie@1
|
11 except:
|
gremmie@1
|
12 from StringIO import StringIO
|
gremmie@1
|
13
|
gremmie@1
|
14 from django.db import models
|
gremmie@1
|
15 from django.contrib.auth.models import User
|
gremmie@1
|
16 from django.core.files.base import ContentFile
|
bgneal@1035
|
17 from django.core.urlresolvers import reverse
|
gremmie@1
|
18
|
gremmie@1
|
19 POTD_THUMB_WIDTH = 120
|
gremmie@1
|
20
|
gremmie@1
|
21 def scale_image(image):
|
gremmie@1
|
22 (w, h) = image.size
|
gremmie@1
|
23 if w <= POTD_THUMB_WIDTH:
|
gremmie@1
|
24 return image
|
gremmie@1
|
25 scale_factor = float(POTD_THUMB_WIDTH) / w
|
gremmie@1
|
26 new_height = int(scale_factor * h)
|
gremmie@1
|
27 return image.resize((POTD_THUMB_WIDTH, new_height), Image.ANTIALIAS)
|
gremmie@1
|
28
|
gremmie@1
|
29
|
gremmie@1
|
30 class Photo(models.Model):
|
gremmie@1
|
31 """Model to represent a POTD"""
|
gremmie@1
|
32 photo = models.ImageField(upload_to='potd/%Y/%m/%d')
|
gremmie@1
|
33 thumb = models.ImageField(upload_to='potd/%Y/%m/%d/thumbs', blank=True, null=True)
|
gremmie@1
|
34 caption = models.CharField(max_length=128)
|
gremmie@1
|
35 description = models.TextField()
|
bgneal@1206
|
36 user = models.ForeignKey(User, related_name='potd_set',
|
bgneal@1206
|
37 on_delete=models.CASCADE)
|
bgneal@535
|
38 date_added = models.DateField()
|
gremmie@1
|
39 potd_count = models.IntegerField(default=0)
|
gremmie@1
|
40
|
bgneal@14
|
41 class Meta:
|
bgneal@14
|
42 ordering = ('-date_added', '-caption')
|
bgneal@14
|
43
|
gremmie@1
|
44 def __unicode__(self):
|
gremmie@1
|
45 return u'%s (%s)' % (self.caption, self.pk)
|
gremmie@1
|
46
|
bgneal@14
|
47 def get_absolute_url(self):
|
bgneal@1035
|
48 return reverse('potd-archive', args=[str(self.id)])
|
gremmie@1
|
49
|
bgneal@182
|
50 def save(self, *args, **kwargs):
|
bgneal@330
|
51 if not self.pk:
|
bgneal@330
|
52 self.generate_thumb()
|
bgneal@535
|
53 self.date_added = datetime.datetime.now()
|
gremmie@1
|
54
|
bgneal@330
|
55 super(Photo, self).save(*args, **kwargs)
|
bgneal@330
|
56
|
bgneal@330
|
57 def can_comment_on(self):
|
bgneal@330
|
58 return Current.objects.get_current_id() == self.id
|
bgneal@330
|
59
|
bgneal@330
|
60 def generate_thumb(self):
|
gremmie@1
|
61 if self.thumb:
|
gremmie@1
|
62 self.thumb.delete(save=False)
|
gremmie@1
|
63
|
gremmie@1
|
64 parser = ImageFile.Parser()
|
gremmie@1
|
65 for chunk in self.photo.chunks():
|
gremmie@1
|
66 parser.feed(chunk)
|
gremmie@1
|
67 image = parser.close()
|
gremmie@1
|
68 format = image.format
|
gremmie@1
|
69 image = scale_image(image)
|
gremmie@1
|
70 s = StringIO()
|
gremmie@1
|
71 image.save(s, format)
|
gremmie@1
|
72 thumb_name = os.path.basename(self.photo.path)
|
gremmie@1
|
73 self.thumb.save(thumb_name, ContentFile(s.getvalue()), save=False)
|
gremmie@1
|
74
|
bgneal@491
|
75 def ogp_tags(self):
|
bgneal@491
|
76 """
|
bgneal@491
|
77 Returns a dict of Open Graph Protocol meta tags.
|
bgneal@491
|
78
|
bgneal@491
|
79 """
|
bgneal@491
|
80 desc = "Photo of the day: %s." % self.caption
|
bgneal@491
|
81 return {
|
bgneal@491
|
82 'og:title': self.caption,
|
bgneal@491
|
83 'og:type': 'article',
|
bgneal@491
|
84 'og:url': self.get_absolute_url(),
|
bgneal@491
|
85 'og:image': self.photo.url,
|
bgneal@491
|
86 'og:description': desc,
|
bgneal@491
|
87 }
|
bgneal@491
|
88
|
gremmie@1
|
89
|
gremmie@1
|
90 class CurrentManager(models.Manager):
|
bgneal@515
|
91 """
|
bgneal@515
|
92 Manager for the Current model.
|
bgneal@515
|
93
|
bgneal@515
|
94 """
|
gremmie@1
|
95 def get_current_photo(self):
|
bgneal@515
|
96 """
|
bgneal@515
|
97 Retrieves the current photo object from the current record.
|
bgneal@515
|
98
|
bgneal@515
|
99 """
|
gremmie@1
|
100 try:
|
gremmie@1
|
101 c = self.get(pk=1)
|
gremmie@1
|
102 return c.potd
|
gremmie@1
|
103 except Current.DoesNotExist:
|
gremmie@1
|
104 return None
|
gremmie@1
|
105
|
gremmie@1
|
106 def get_current_id(self):
|
bgneal@515
|
107 """
|
bgneal@515
|
108 Returns the ID of the current POTD from the current record.
|
bgneal@515
|
109
|
bgneal@515
|
110 """
|
gremmie@1
|
111 potd = self.get_current_photo()
|
gremmie@1
|
112 if potd is not None:
|
gremmie@1
|
113 return potd.pk
|
gremmie@1
|
114 return None
|
gremmie@1
|
115
|
gremmie@1
|
116
|
gremmie@1
|
117 class Current(models.Model):
|
gremmie@1
|
118 """This model simply stores the current POTD."""
|
bgneal@1206
|
119 potd = models.ForeignKey(Photo, on_delete=models.CASCADE)
|
gremmie@1
|
120
|
gremmie@1
|
121 objects = CurrentManager()
|
gremmie@1
|
122
|
gremmie@1
|
123 def __unicode__(self):
|
gremmie@1
|
124 return self.potd.__unicode__()
|
gremmie@1
|
125
|
gremmie@1
|
126 class Meta:
|
gremmie@1
|
127 verbose_name_plural = 'Current'
|
gremmie@1
|
128
|
gremmie@1
|
129
|
gremmie@1
|
130 class SequenceManager(models.Manager):
|
bgneal@515
|
131 """
|
bgneal@515
|
132 Manager for the Sequence model.
|
bgneal@515
|
133
|
bgneal@515
|
134 """
|
gremmie@1
|
135 def insert_photo(self, photo_id):
|
bgneal@515
|
136 """
|
bgneal@515
|
137 Inserts the given photo_id just after the current photo so it
|
bgneal@515
|
138 will appear as tomorrow's POTD.
|
bgneal@515
|
139
|
bgneal@515
|
140 """
|
gremmie@1
|
141 current = Current.objects.get_current_id()
|
gremmie@1
|
142 if current is not None:
|
bgneal@330
|
143 s = self.get(pk=1)
|
bgneal@330
|
144 seq = [int(x) for x in s.seq.split(',')]
|
bgneal@330
|
145 if photo_id not in seq:
|
bgneal@330
|
146 i = seq.index(current)
|
bgneal@330
|
147 seq.insert(i + 1, photo_id)
|
bgneal@330
|
148 s.seq = ','.join([str(x) for x in seq])
|
bgneal@330
|
149 s.save()
|
gremmie@1
|
150
|
gremmie@1
|
151 def remove_photo(self, photo_id):
|
bgneal@515
|
152 """
|
bgneal@515
|
153 Removes a given photo id from the sequence of photos.
|
bgneal@515
|
154
|
bgneal@515
|
155 """
|
gremmie@1
|
156 try:
|
gremmie@1
|
157 s = self.get(pk=1)
|
bgneal@330
|
158 except Sequence.DoesNotExist:
|
bgneal@330
|
159 pass
|
bgneal@330
|
160 else:
|
gremmie@1
|
161 seq = [int(x) for x in s.seq.split(',')]
|
gremmie@1
|
162 if photo_id in seq:
|
gremmie@1
|
163 seq.remove(photo_id)
|
gremmie@1
|
164 s.seq = ','.join([str(x) for x in seq])
|
gremmie@1
|
165 s.save()
|
gremmie@1
|
166
|
gremmie@1
|
167
|
gremmie@1
|
168 class Sequence(models.Model):
|
gremmie@1
|
169 """This model stores the sequence of photos for the POTD."""
|
gremmie@1
|
170 seq = models.CommaSeparatedIntegerField(max_length=4096)
|
gremmie@1
|
171
|
gremmie@1
|
172 objects = SequenceManager()
|
gremmie@1
|
173
|
gremmie@1
|
174 def __unicode__(self):
|
bgneal@535
|
175 return u'POTD Sequence %d' % self.id
|
gremmie@1
|
176
|
gremmie@1
|
177 class Meta:
|
gremmie@1
|
178 verbose_name_plural = 'Sequence'
|