annotate potd/models.py @ 887:9a15f7c27526

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