view gpp/potd/models.py @ 265:1ba2c6bf6eb7

Closing #98. Animated GIFs were losing their transparency and animated properties when saved as avatars. Reworked the avatar save process to only run the avatar through PIL if it is too big. This preserves the original uploaded file if it is within the desired size settings. This may still mangle big animated gifs. If this becomes a problem, then maybe look into calling the PIL Image.resize() method directly. Moved the PIL image specific functions from bio.forms to a new module: core.image for better reusability in the future.
author Brian Neal <bgneal@gmail.com>
date Fri, 24 Sep 2010 02:12:09 +0000
parents 5c889b587416
children 3c951521e0ec
line wrap: on
line source
"""
Models for the Photo Of The Day (POTD) application.
"""
import os
from PIL import ImageFile
from PIL import Image
try:
   from cStringIO import StringIO
except:
   from StringIO import StringIO

from django.db import models
from django.contrib.auth.models import User
from django.core.files.base import ContentFile

POTD_THUMB_WIDTH = 120

def scale_image(image):
    (w, h) = image.size
    if w <= POTD_THUMB_WIDTH:
        return image
    scale_factor = float(POTD_THUMB_WIDTH) / w
    new_height = int(scale_factor * h)
    return image.resize((POTD_THUMB_WIDTH, new_height), Image.ANTIALIAS)


class Photo(models.Model):
    """Model to represent a POTD"""
    photo = models.ImageField(upload_to='potd/%Y/%m/%d')
    thumb = models.ImageField(upload_to='potd/%Y/%m/%d/thumbs', blank=True, null=True)
    caption = models.CharField(max_length=128)
    description = models.TextField()
    user = models.ForeignKey(User)
    date_added = models.DateField(auto_now_add=True)
    potd_count = models.IntegerField(default=0)

    class Meta:
        ordering = ('-date_added', '-caption')

    def __unicode__(self):
        return u'%s (%s)' % (self.caption, self.pk)

    @models.permalink
    def get_absolute_url(self):
        return ('potd-archive', [str(self.id)])

    def save(self, *args, **kwargs):

        if self.thumb:
            self.thumb.delete(save=False)

        parser = ImageFile.Parser()
        for chunk in self.photo.chunks():
            parser.feed(chunk)
        image = parser.close()
        format = image.format
        image = scale_image(image)
        s = StringIO()
        image.save(s, format)
        thumb_name = os.path.basename(self.photo.path)
        self.thumb.save(thumb_name, ContentFile(s.getvalue()), save=False)
        
        super(Photo, self).save(*args, **kwargs)
        Sequence.objects.insert_photo(self.pk)

    def delete(self):
        Sequence.objects.remove_photo(self.pk)
        super(Photo, self).delete()

    def can_comment_on(self):
        return Current.objects.get_current_id() == self.id


class CurrentManager(models.Manager):
    def get_current_photo(self):
        try:
            c = self.get(pk=1)
            return c.potd
        except Current.DoesNotExist:
            return None

    def get_current_id(self):
        potd = self.get_current_photo()
        if potd is not None:
            return potd.pk
        return None


class Current(models.Model):
    """This model simply stores the current POTD."""
    potd = models.ForeignKey(Photo)

    objects = CurrentManager()

    def __unicode__(self):
        return self.potd.__unicode__()

    class Meta:
        verbose_name_plural = 'Current'


class SequenceManager(models.Manager):
    def insert_photo(self, photo_id):
        current = Current.objects.get_current_id()
        if current is not None:
            try:
                s = self.get(pk=1)
                seq = [int(x) for x in s.seq.split(',')]
                if photo_id not in seq:
                    i = seq.index(current)
                    seq.insert(i + 1, photo_id)
                    s.seq = ','.join([str(x) for x in seq])
                    s.save()
            except:
                pass

    def remove_photo(self, photo_id):
        try:
            s = self.get(pk=1)
            seq = [int(x) for x in s.seq.split(',')]
            if photo_id in seq:
                seq.remove(photo_id)
                s.seq = ','.join([str(x) for x in seq])
                s.save()
        except:
            pass


class Sequence(models.Model):
    """This model stores the sequence of photos for the POTD."""
    seq = models.CommaSeparatedIntegerField(max_length=4096)

    objects = SequenceManager()

    def __unicode__(self):
        return self.seq

    class Meta:
        verbose_name_plural = 'Sequence'