annotate gpp/potd/models.py @ 505:a5d11471d031

Refactor the logic in the rate limiter decorator. Check to see if the request was ajax, as the ajax view always returns 200. Have to decode the JSON response to see if an error occurred or not.
author Brian Neal <bgneal@gmail.com>
date Sat, 03 Dec 2011 19:13:38 +0000
parents 7dbdbb08e68c
children ae89ba801e8b
rev   line source
gremmie@1 1 """
gremmie@1 2 Models for the Photo Of The Day (POTD) application.
gremmie@1 3 """
gremmie@1 4 import os
gremmie@1 5 from PIL import ImageFile
gremmie@1 6 from PIL import Image
gremmie@1 7 try:
gremmie@1 8 from cStringIO import StringIO
gremmie@1 9 except:
gremmie@1 10 from StringIO import StringIO
gremmie@1 11
gremmie@1 12 from django.db import models
gremmie@1 13 from django.contrib.auth.models import User
gremmie@1 14 from django.core.files.base import ContentFile
gremmie@1 15
gremmie@1 16 POTD_THUMB_WIDTH = 120
gremmie@1 17
gremmie@1 18 def scale_image(image):
gremmie@1 19 (w, h) = image.size
gremmie@1 20 if w <= POTD_THUMB_WIDTH:
gremmie@1 21 return image
gremmie@1 22 scale_factor = float(POTD_THUMB_WIDTH) / w
gremmie@1 23 new_height = int(scale_factor * h)
gremmie@1 24 return image.resize((POTD_THUMB_WIDTH, new_height), Image.ANTIALIAS)
gremmie@1 25
gremmie@1 26
gremmie@1 27 class Photo(models.Model):
gremmie@1 28 """Model to represent a POTD"""
gremmie@1 29 photo = models.ImageField(upload_to='potd/%Y/%m/%d')
gremmie@1 30 thumb = models.ImageField(upload_to='potd/%Y/%m/%d/thumbs', blank=True, null=True)
gremmie@1 31 caption = models.CharField(max_length=128)
gremmie@1 32 description = models.TextField()
gremmie@1 33 user = models.ForeignKey(User)
gremmie@1 34 date_added = models.DateField(auto_now_add=True)
gremmie@1 35 potd_count = models.IntegerField(default=0)
gremmie@1 36
bgneal@14 37 class Meta:
bgneal@14 38 ordering = ('-date_added', '-caption')
bgneal@14 39
gremmie@1 40 def __unicode__(self):
gremmie@1 41 return u'%s (%s)' % (self.caption, self.pk)
gremmie@1 42
bgneal@14 43 @models.permalink
bgneal@14 44 def get_absolute_url(self):
bgneal@14 45 return ('potd-archive', [str(self.id)])
gremmie@1 46
bgneal@182 47 def save(self, *args, **kwargs):
bgneal@330 48 if not self.pk:
bgneal@330 49 self.generate_thumb()
gremmie@1 50
bgneal@330 51 super(Photo, self).save(*args, **kwargs)
bgneal@330 52 Sequence.objects.insert_photo(self.pk)
bgneal@330 53
bgneal@330 54 def delete(self):
bgneal@330 55 Sequence.objects.remove_photo(self.pk)
bgneal@330 56 super(Photo, self).delete()
bgneal@330 57
bgneal@330 58 def can_comment_on(self):
bgneal@330 59 return Current.objects.get_current_id() == self.id
bgneal@330 60
bgneal@330 61 def generate_thumb(self):
gremmie@1 62 if self.thumb:
gremmie@1 63 self.thumb.delete(save=False)
gremmie@1 64
gremmie@1 65 parser = ImageFile.Parser()
gremmie@1 66 for chunk in self.photo.chunks():
gremmie@1 67 parser.feed(chunk)
gremmie@1 68 image = parser.close()
gremmie@1 69 format = image.format
gremmie@1 70 image = scale_image(image)
gremmie@1 71 s = StringIO()
gremmie@1 72 image.save(s, format)
gremmie@1 73 thumb_name = os.path.basename(self.photo.path)
gremmie@1 74 self.thumb.save(thumb_name, ContentFile(s.getvalue()), save=False)
gremmie@1 75
bgneal@491 76 def ogp_tags(self):
bgneal@491 77 """
bgneal@491 78 Returns a dict of Open Graph Protocol meta tags.
bgneal@491 79
bgneal@491 80 """
bgneal@491 81 desc = "Photo of the day: %s." % self.caption
bgneal@491 82 return {
bgneal@491 83 'og:title': self.caption,
bgneal@491 84 'og:type': 'article',
bgneal@491 85 'og:url': self.get_absolute_url(),
bgneal@491 86 'og:image': self.photo.url,
bgneal@491 87 'og:description': desc,
bgneal@491 88 }
bgneal@491 89
gremmie@1 90
gremmie@1 91 class CurrentManager(models.Manager):
gremmie@1 92 def get_current_photo(self):
gremmie@1 93 try:
gremmie@1 94 c = self.get(pk=1)
gremmie@1 95 return c.potd
gremmie@1 96 except Current.DoesNotExist:
gremmie@1 97 return None
gremmie@1 98
gremmie@1 99 def get_current_id(self):
gremmie@1 100 potd = self.get_current_photo()
gremmie@1 101 if potd is not None:
gremmie@1 102 return potd.pk
gremmie@1 103 return None
gremmie@1 104
gremmie@1 105
gremmie@1 106 class Current(models.Model):
gremmie@1 107 """This model simply stores the current POTD."""
gremmie@1 108 potd = models.ForeignKey(Photo)
gremmie@1 109
gremmie@1 110 objects = CurrentManager()
gremmie@1 111
gremmie@1 112 def __unicode__(self):
gremmie@1 113 return self.potd.__unicode__()
gremmie@1 114
gremmie@1 115 class Meta:
gremmie@1 116 verbose_name_plural = 'Current'
gremmie@1 117
gremmie@1 118
gremmie@1 119 class SequenceManager(models.Manager):
gremmie@1 120 def insert_photo(self, photo_id):
gremmie@1 121 current = Current.objects.get_current_id()
gremmie@1 122 if current is not None:
bgneal@330 123 s = self.get(pk=1)
bgneal@330 124 seq = [int(x) for x in s.seq.split(',')]
bgneal@330 125 if photo_id not in seq:
bgneal@330 126 i = seq.index(current)
bgneal@330 127 seq.insert(i + 1, photo_id)
bgneal@330 128 s.seq = ','.join([str(x) for x in seq])
bgneal@330 129 s.save()
gremmie@1 130
gremmie@1 131 def remove_photo(self, photo_id):
gremmie@1 132 try:
gremmie@1 133 s = self.get(pk=1)
bgneal@330 134 except Sequence.DoesNotExist:
bgneal@330 135 pass
bgneal@330 136 else:
gremmie@1 137 seq = [int(x) for x in s.seq.split(',')]
gremmie@1 138 if photo_id in seq:
gremmie@1 139 seq.remove(photo_id)
gremmie@1 140 s.seq = ','.join([str(x) for x in seq])
gremmie@1 141 s.save()
gremmie@1 142
gremmie@1 143
gremmie@1 144 class Sequence(models.Model):
gremmie@1 145 """This model stores the sequence of photos for the POTD."""
gremmie@1 146 seq = models.CommaSeparatedIntegerField(max_length=4096)
gremmie@1 147
gremmie@1 148 objects = SequenceManager()
gremmie@1 149
gremmie@1 150 def __unicode__(self):
gremmie@1 151 return self.seq
gremmie@1 152
gremmie@1 153 class Meta:
gremmie@1 154 verbose_name_plural = 'Sequence'
gremmie@1 155