changeset 696:b2a8fde3173a

Got the image resizing and uploading working. It needs a lot of work though. This commit is just to capture something that works.
author Brian Neal <bgneal@gmail.com>
date Sun, 08 Sep 2013 19:06:54 -0500
parents 2d35e5f97a99
children 67f8d49a9377
files sg101/settings/base.py sg101/templates/user_photos/photo_detail.html user_photos/forms.py user_photos/images.py user_photos/models.py user_photos/urls.py user_photos/views.py
diffstat 7 files changed, 145 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/sg101/settings/base.py	Sat Sep 07 20:50:46 2013 -0500
+++ b/sg101/settings/base.py	Sun Sep 08 19:06:54 2013 -0500
@@ -292,9 +292,13 @@
 WIKI_REDIS_SET = 'wiki_cookie_keys'
 
 # User photo upload settings
-USER_PHOTO_ENABLED = True
-USER_PHOTO_ACCESS_KEY = SECRETS['AWS_ACCESS_KEY']
-USER_PHOTO_SECRET_KEY = SECRETS['AWS_SECRET_KEY']
+USER_PHOTOS_ENABLED = True
+USER_PHOTOS_ACCESS_KEY = SECRETS['AWS_ACCESS_KEY']
+USER_PHOTOS_SECRET_KEY = SECRETS['AWS_SECRET_KEY']
+USER_PHOTOS_BUCKET = 'sg101.user.photos'
+USER_PHOTOS_BASE_URL = 'https://s3-us-west-1.amazonaws.com'
+USER_PHOTOS_MAX_SIZE = (660, 720)
+USER_PHOTOS_THUMB_SIZE = (120, 120)
 
 #######################################################################
 # Asynchronous settings (queues, queued_search, redis, celery, etc)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sg101/templates/user_photos/photo_detail.html	Sun Sep 08 19:06:54 2013 -0500
@@ -0,0 +1,18 @@
+{% extends 'base.html' %}
+{% block title %}User Photo Details{% endblock %}
+{% block content %}
+<h2>User Photo Details</h2>
+
+<ul>
+   <li>Uploader: {{ object.user.username }}</li>
+   <li>Upload date: {{ object.upload_date|date }}</li>
+</li>
+
+<div>
+   <img src="{{ object.thumb_url }}" alt="thumbnail" />
+</div>
+
+<div>
+   <img src="{{ object.url }}" alt="photo" />
+</div>
+{% endblock %}
--- a/user_photos/forms.py	Sat Sep 07 20:50:46 2013 -0500
+++ b/user_photos/forms.py	Sun Sep 08 19:06:54 2013 -0500
@@ -1,6 +1,25 @@
 """Forms for the user_photos application."""
 from django import forms
 
+from user_photos.models import Photo
+from user_photos.images import process_file
+
 
 class UploadForm(forms.Form):
     image_file = forms.ImageField()
+
+    def __init__(self, *args, **kwargs):
+        self.user = kwargs.pop('user')
+        super(UploadForm, self).__init__(*args, **kwargs)
+
+    def save(self):
+        """Processes the image and creates a new Photo object, which is saved to
+        the database. The new Photo instance is returned.
+
+        This function should only be called if is_valid() returns True.
+
+        """
+        url, thumb_url = process_file(self.cleaned_data['image_file'], self.user)
+        photo = Photo(user=self.user, url=url, thumb_url=thumb_url)
+        photo.save()
+        return photo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/user_photos/images.py	Sun Sep 08 19:06:54 2013 -0500
@@ -0,0 +1,86 @@
+"""images.py
+
+This module contains image processing routines for the user_photos application.
+
+"""
+import datetime
+import logging
+from io import BytesIO
+import os.path
+import uuid
+
+from django.conf import settings
+from PIL import Image
+from boto.s3.connection import S3Connection
+from boto.s3.key import Key
+
+
+logger = logging.getLogger(__name__)
+
+
+def process_file(f, user):
+    """Perform processing on the given uploaded image file:
+
+    * The image is resized if necessary
+    * A thumbnail version is created
+    * The image and thumbnail are uploaded to an S3 bucket
+    * The image and thumbnail URLs are returned as a tuple
+
+    """
+    logger.info('Processing image file for {}: {}'.format(user.username, f.name))
+
+    unique_key = uuid.uuid4().hex
+    ext = os.path.splitext(f.name)[1]
+    filename = '/tmp/' + unique_key + ext
+    with open(filename, 'wb') as fp:
+        for chunk in f.chunks():
+            fp.write(chunk)
+
+    # Resize image if necessary
+    image = Image.open(filename)
+    if image.size > settings.USER_PHOTOS_MAX_SIZE:
+        logger.debug('Resizing from {} to {}'.format(image.size, settings.USER_PHOTOS_MAX_SIZE))
+        image.thumbnail(settings.USER_PHOTOS_MAX_SIZE, Image.ANTIALIAS)
+        image.save(filename)
+
+    # Create thumbnail
+    logger.debug('Creating thumbnail')
+    image = Image.open(filename)
+    image.thumbnail(settings.USER_PHOTOS_THUMB_SIZE, Image.ANTIALIAS)
+    thumb = BytesIO()
+    image.save(thumb, format=image.format)
+
+    # Upload both images to S3
+    logger.debug('Getting connection / bucket')
+    conn = S3Connection(settings.USER_PHOTOS_ACCESS_KEY,
+                        settings.USER_PHOTOS_SECRET_KEY)
+    bucket = conn.get_bucket(settings.USER_PHOTOS_BUCKET, validate=False)
+
+    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+
+    logging.debug('Uploading image')
+    k1 = Key(bucket)
+    k1.key = unique_key + ext
+    k1.set_metadata('user', user.username)
+    k1.set_metadata('date', now)
+    k1.set_contents_from_filename(filename)
+
+    logging.debug('Uploading thumbnail')
+    k2 = Key(bucket)
+    k2.key = '{}t{}'.format(unique_key, ext)
+    k2.set_metadata('user', user.username)
+    k2.set_metadata('date', now)
+    k2.set_contents_from_string(thumb.getvalue())
+
+    logging.debug('Making public')
+    k1.make_public()
+    k2.make_public()
+
+    os.remove(filename)
+
+    logger.info('Completed processing image file for {}: {}'.format(user.username, f.name))
+
+    url_base = '{}/{}/'.format(settings.USER_PHOTOS_BASE_URL,
+                               settings.USER_PHOTOS_BUCKET)
+
+    return (url_base + k1.key, url_base + k2.key)
--- a/user_photos/models.py	Sat Sep 07 20:50:46 2013 -0500
+++ b/user_photos/models.py	Sun Sep 08 19:06:54 2013 -0500
@@ -4,6 +4,7 @@
 
 from django.db import models
 from django.conf import settings
+from django.core.urlresolvers import reverse
 
 
 class Photo(models.Model):
@@ -19,7 +20,7 @@
                                self.upload_date.strftime('%Y-%m-%d %H:%M:%S'))
 
     def get_absolute_url(self):
-        return self.url
+        return reverse('user_photos-detail', kwargs={'pk': self.pk})
 
     def save(self, *args, **kwargs):
         if not self.pk and not self.upload_date:
--- a/user_photos/urls.py	Sat Sep 07 20:50:46 2013 -0500
+++ b/user_photos/urls.py	Sun Sep 08 19:06:54 2013 -0500
@@ -1,6 +1,13 @@
 """URLs for the user_photos application."""
 from django.conf.urls import patterns, url
+from django.views.generic import DetailView
+
+from user_photos.models import Photo
+
 
 urlpatterns = patterns('user_photos.views',
     url(r'^upload/$', 'upload', name='user_photos-upload'),
+    url(r'^photo/(?P<pk>\d+)/$',
+        DetailView.as_view(model=Photo),
+        name='user_photos-detail')
 )
--- a/user_photos/views.py	Sat Sep 07 20:50:46 2013 -0500
+++ b/user_photos/views.py	Sun Sep 08 19:06:54 2013 -0500
@@ -1,7 +1,7 @@
 """Views for the user_photos application."""
 from django.conf import settings
 from django.contrib.auth.decorators import login_required
-from django.shortcuts import render
+from django.shortcuts import render, redirect
 
 from user_photos.forms import UploadForm
 
@@ -19,17 +19,16 @@
 
     """
     form = None
-    uploads_enabled = settings.USER_PHOTO_ENABLED
+    uploads_enabled = settings.USER_PHOTOS_ENABLED
 
     if uploads_enabled:
         if request.method == 'POST':
-            form = UploadForm(request.POST, request.FILES)
+            form = UploadForm(request.POST, request.FILES, user=request.user)
             if form.is_valid():
-                #TODO
-                print "**************", request.FILES['image_file']
-                pass
+                photo = form.save()
+                return redirect(photo)
         else:
-            form = UploadForm()
+            form = UploadForm(user=request.user)
 
     return render(request, 'user_photos/upload_form.html', {
         'enabled': uploads_enabled,