diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/photos/admin.py | 28 | ||||
-rw-r--r-- | app/photos/forms.py | 55 | ||||
-rw-r--r-- | app/photos/migrations/0004_luximage_alt.py | 20 | ||||
-rw-r--r-- | app/photos/migrations/0005_auto_20160318_1244.py | 25 | ||||
-rw-r--r-- | app/photos/migrations/0006_auto_20160318_2047.py | 30 | ||||
-rw-r--r-- | app/photos/migrations/0007_auto_20160320_0802.py | 20 | ||||
-rw-r--r-- | app/photos/migrations/0008_luximagesize_quality.py | 21 | ||||
-rw-r--r-- | app/photos/migrations/0009_auto_20160320_0907.py | 20 | ||||
-rw-r--r-- | app/photos/models.py | 78 | ||||
-rw-r--r-- | app/photos/readexif.py | 59 | ||||
-rw-r--r-- | app/photos/templatetags/get_image_by_size.py (renamed from app/photos/templatetags/get_image_size.py) | 4 | ||||
-rw-r--r-- | app/photos/utils.py | 4 |
12 files changed, 308 insertions, 56 deletions
diff --git a/app/photos/admin.py b/app/photos/admin.py index 659dcb6..9453734 100644 --- a/app/photos/admin.py +++ b/app/photos/admin.py @@ -4,7 +4,7 @@ from django.contrib.gis.admin import OSMGeoAdmin from django.conf.urls import patterns from django.conf.urls import url from django.utils.translation import ungettext, ugettext_lazy as _ -from photos.models import Photo, PhotoGallery, LuxImage, LuxGallery +from photos.models import Photo, PhotoGallery, LuxImage, LuxGallery, LuxImageSize from django.shortcuts import render from django.contrib.admin import helpers from django.http import HttpResponseRedirect @@ -13,10 +13,17 @@ from django.http import HttpResponseRedirect from .forms import UploadZipForm, GalleryForm +class LuxImageSizeAdmin(OSMGeoAdmin): + pass + + +admin.site.register(LuxImageSize, LuxImageSizeAdmin) + + class LuxImageAdmin(OSMGeoAdmin): - list_display = ('pk', 'admin_thumbnail', 'pub_date',) - list_filter = ('pub_date',) + list_display = ('pk', 'admin_thumbnail', 'pub_date', 'location') + list_filter = ('pub_date', 'location') search_fields = ['title', 'caption'] # Options for OSM map Using custom ESRI topo map default_lon = -9285175 @@ -29,6 +36,21 @@ class LuxImageAdmin(OSMGeoAdmin): map_template = 'gis/admin/osm.html' openlayers_url = '/static/admin/js/OpenLayers.js' + fieldsets = ( + (None, { + 'fields': (('image', 'pub_date'), 'sizes', ('title', 'alt'), 'caption', 'point', ('is_public', 'is_video'), ('photo_credit_source','photo_credit_url')) + }), + ('Exif Data', { + 'classes': ('collapse',), + 'fields': ('exif_raw', 'exif_aperture', 'exif_make', 'exif_model', 'exif_exposure', 'exif_iso', 'exif_focal_length', 'exif_lens', 'exif_date', 'height', 'width'), + }), + ) + + + + + + admin.site.register(LuxImage, LuxImageAdmin) diff --git a/app/photos/forms.py b/app/photos/forms.py index 1f80496..8ad89f2 100644 --- a/app/photos/forms.py +++ b/app/photos/forms.py @@ -2,10 +2,8 @@ import zipfile from zipfile import BadZipFile import logging import datetime -import time import os from io import BytesIO -from fractions import Fraction try: import Image except ImportError: @@ -17,21 +15,18 @@ from django.utils.safestring import mark_safe from django.contrib import messages from django.core.files.base import ContentFile from django.contrib.admin import widgets -from django.contrib.gis.geos import Point from django.conf import settings -import exiftool -from photos.models import LuxImage, LuxGallery -from locations.models import Location +from photos.models import LuxImage, LuxGallery, LuxImageSize from .utils import resize_image +from .readexif import readexif logger = logging.getLogger('photos.forms') class GalleryForm(forms.ModelForm): class Meta: - model = LuxGallery fields = '__all__' widgets = { 'images': forms.SelectMultiple, @@ -152,56 +147,24 @@ class UploadZipForm(forms.Form): image.image.save(filename, contentfile) image.save() gallery.images.add(image) - - with exiftool.ExifTool() as et: - meta = et.get_metadata(image.image.path) - et.terminate() - image.exif_raw = meta - try: - image.title = meta["EXIF:ImageDescription"] - except: - pass - try: - image.caption = meta["EXIF:UserComment"] - except: - pass - try: - image.exif_lens = meta["MakerNotes:LensType"] - except: - try: - image.exif_lens = meta["XMP:Lens"] - except: - pass - try: - image.point = Point(meta["XMP:GPSLongitude"], meta["XMP:GPSLatitude"], srid=4326) - try: - image.location = Location.objects.filter(geometry__contains=image.point).get() - except Location.DoesNotExist: - pass - except KeyError: - pass - image.exif_aperture = meta["EXIF:FNumber"] - image.exif_make = meta["EXIF:Make"] - image.exif_model = meta["EXIF:Model"] - image.exif_exposure = str(Fraction(float(meta["EXIF:ExposureTime"])).limit_denominator()) - image.exif_iso = meta["EXIF:ISO"] - image.exif_focal_length = meta["EXIF:FocalLength"] - fmt_date = time.strptime(meta["EXIF:DateTimeOriginal"], "%Y:%m:%d %H:%M:%S") - image.exif_date = time.strftime("%Y-%m-%d %H:%M:%S", fmt_date) - image.height = meta["File:ImageHeight"] - image.width = meta["File:ImageWidth"] - image.save() + readexif(image) count += 1 img = Image.open(image.image.path) base_path = "%s/galleries/" % settings.IMAGES_ROOT if img.size[0] > img.size[1]: resize_image(img, 2280, None, 65, base_path+'large/', image.get_image_name()) + image.sizes.add(LuxImageSize.objects.get(size=2280)) resize_image(img, 1140, None, 72, base_path+'medium/', image.get_image_name()) + image.sizes.add(LuxImageSize.objects.get(size=1140)) resize_image(img, 720, None, 68, base_path+'small/', image.get_image_name()) + image.sizes.add(LuxImageSize.objects.get(size=720)) if img.size[1] > img.size[0]: resize_image(img, None, 1600, 65, base_path+'large/', image.get_image_name()) + image.sizes.add(LuxImageSize.objects.get(size=1600)) resize_image(img, None, 800, 72, base_path+'medium/', image.get_image_name()) + image.sizes.add(LuxImageSize.objects.get(size=800)) resize_image(img, None, 460, 60, base_path+'small/', image.get_image_name()) + image.sizes.add(LuxImageSize.objects.get(size=460)) resize_image(img, 160, None, 68, base_path+'thumb/', image.get_image_name()) zipper.close() diff --git a/app/photos/migrations/0004_luximage_alt.py b/app/photos/migrations/0004_luximage_alt.py new file mode 100644 index 0000000..ecbaf56 --- /dev/null +++ b/app/photos/migrations/0004_luximage_alt.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-16 22:45 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0003_luxgallery_caption_style'), + ] + + operations = [ + migrations.AddField( + model_name='luximage', + name='alt', + field=models.CharField(blank=True, max_length=300, null=True), + ), + ] diff --git a/app/photos/migrations/0005_auto_20160318_1244.py b/app/photos/migrations/0005_auto_20160318_1244.py new file mode 100644 index 0000000..584714e --- /dev/null +++ b/app/photos/migrations/0005_auto_20160318_1244.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-18 12:44 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0004_luximage_alt'), + ] + + operations = [ + migrations.AddField( + model_name='luximage', + name='photo_credit_source', + field=models.CharField(blank=True, max_length=300, null=True), + ), + migrations.AddField( + model_name='luximage', + name='photo_credit_url', + field=models.CharField(blank=True, max_length=300, null=True), + ), + ] diff --git a/app/photos/migrations/0006_auto_20160318_2047.py b/app/photos/migrations/0006_auto_20160318_2047.py new file mode 100644 index 0000000..7cf0a4f --- /dev/null +++ b/app/photos/migrations/0006_auto_20160318_2047.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-18 20:47 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0005_auto_20160318_1244'), + ] + + operations = [ + migrations.CreateModel( + name='LuxImageSize', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('size', models.IntegerField(max_length=5)), + ], + options={ + 'verbose_name_plural': 'Image Sizes', + }, + ), + migrations.AddField( + model_name='luximage', + name='sizes', + field=models.ManyToManyField(to='photos.LuxImageSize'), + ), + ] diff --git a/app/photos/migrations/0007_auto_20160320_0802.py b/app/photos/migrations/0007_auto_20160320_0802.py new file mode 100644 index 0000000..474bd42 --- /dev/null +++ b/app/photos/migrations/0007_auto_20160320_0802.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-20 08:02 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0006_auto_20160318_2047'), + ] + + operations = [ + migrations.AlterField( + model_name='luximagesize', + name='size', + field=models.IntegerField(), + ), + ] diff --git a/app/photos/migrations/0008_luximagesize_quality.py b/app/photos/migrations/0008_luximagesize_quality.py new file mode 100644 index 0000000..06dc0cc --- /dev/null +++ b/app/photos/migrations/0008_luximagesize_quality.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-20 08:47 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0007_auto_20160320_0802'), + ] + + operations = [ + migrations.AddField( + model_name='luximagesize', + name='quality', + field=models.IntegerField(default=65), + preserve_default=False, + ), + ] diff --git a/app/photos/migrations/0009_auto_20160320_0907.py b/app/photos/migrations/0009_auto_20160320_0907.py new file mode 100644 index 0000000..ccfebf5 --- /dev/null +++ b/app/photos/migrations/0009_auto_20160320_0907.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-20 09:07 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0008_luximagesize_quality'), + ] + + operations = [ + migrations.RenameField( + model_name='luximagesize', + old_name='size', + new_name='width', + ), + ] diff --git a/app/photos/models.py b/app/photos/models.py index 50ae43b..46c659e 100644 --- a/app/photos/models.py +++ b/app/photos/models.py @@ -10,14 +10,34 @@ from django.conf import settings from taggit.managers import TaggableManager from locations.models import Location, Region +from .utils import resize_image +from .readexif import readexif +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.db.models.signals import m2m_changed + def get_upload_path(self, filename): - return "images/galleries/original/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + return "images/original/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +class LuxImageSize(models.Model): + width = models.IntegerField() + quality = models.IntegerField() + + class Meta: + verbose_name_plural = 'Image Sizes' + + def __str__(self): + return str(self.width) class LuxImage(models.Model): image = models.FileField(blank=True, null=True, upload_to=get_upload_path) title = models.CharField(null=True, blank=True, max_length=300) + alt = models.CharField(null=True, blank=True, max_length=300) + photo_credit_source = models.CharField(null=True, blank=True, max_length=300) + photo_credit_url = models.CharField(null=True, blank=True, max_length=300) caption = models.TextField(blank=True, null=True) pub_date = models.DateTimeField() exif_raw = models.TextField(blank=True, null=True) @@ -35,6 +55,7 @@ class LuxImage(models.Model): location = models.ForeignKey(Location, null=True, blank=True) is_public = models.BooleanField(default=True) is_video = models.BooleanField(default=False) + sizes = models.ManyToManyField(LuxImageSize) flickr_id = models.CharField(null=True, blank=True, max_length=80) class Meta: @@ -48,15 +69,27 @@ class LuxImage(models.Model): else: return "%s" % self.pk + def get_admin_image(self): + for size in self.sizes.all(): + if size.width <= 800: + return self.get_image_by_size(size) + + def get_image_name(self): - return self.image.url.split("galleries/original/")[1] + return self.image.url.split("original/")[1][5:-4] - def get_image_size(self, size="original"): + def get_image_ext(self): + return self.image.url[-3:] + + def get_image_by_size(self, size="original"): base = self.get_image_name() - return "%sgalleries/%s/%s" % (settings.IMAGES_URL, size, base) + if size == "original": + return "%soriginal/%s/%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), base, self.get_image_ext()) + else: + return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), base, size, self.get_image_ext()) def admin_thumbnail(self): - return force_text('<a href="%s"><img src="%s"></a>' % (self.get_image_size(), self.get_image_size("thumb"))) + return force_text('<a href="%s"><img src="%s"></a>' % (self.get_image_by_size(), self.get_image_by_size("tn"))) admin_thumbnail.allow_tags = True admin_thumbnail.short_description = 'Thumbnail' @@ -79,6 +112,25 @@ class LuxImage(models.Model): return "/admin/photos/luximage/%s/change/" % n.pk +@receiver(post_save, sender=LuxImage) +def post_save_events(sender, update_fields, created, instance, **kwargs): + instance = readexif(instance) + post_save.disconnect(post_save_events, sender=LuxImage) + instance.save() + post_save.connect(post_save_events, sender=LuxImage) + + +@receiver(m2m_changed, sender=LuxImage.sizes.through) +def update_photo_sizes(sender, instance, **kwargs): + print("hellow world") + base_path = "%s/%s/" % (settings.IMAGES_ROOT, instance.pub_date.strftime("%Y")) + img = Image.open(instance.image.path) + resize_image(img, 160, None, 65, base_path, "%s_tn.%s" % (instance.get_image_name(), instance.get_image_ext())) + for size in instance.sizes.all(): + print(size.width) + resize_image(img, size.width, None, size.quality, base_path, "%s_%s.%s" % (instance.get_image_name(), size.width, instance.get_image_ext())) + + class LuxGallery(models.Model): title = models.CharField(blank=True, max_length=300) description = models.TextField(blank=True, null=True) @@ -317,3 +369,19 @@ class PhotoGallerySitemap(Sitemap): def lastmod(self, obj): return obj.pub_date + + +def resize_luximage(self, image): + image.save() + img = Image.open(image.image.path) + base_path = "%s/galleries/" % settings.IMAGES_ROOT + if img.size[0] > img.size[1]: + resize_image(img, 2280, None, 65, base_path+'large/', image.get_image_name()) + resize_image(img, 1140, None, 72, base_path+'medium/', image.get_image_name()) + resize_image(img, 720, None, 68, base_path+'small/', image.get_image_name()) + if img.size[1] > img.size[0]: + resize_image(img, None, 1600, 65, base_path+'large/', image.get_image_name()) + resize_image(img, None, 800, 72, base_path+'medium/', image.get_image_name()) + resize_image(img, None, 460, 60, base_path+'small/', image.get_image_name()) + + resize_image(img, 160, None, 68, base_path+'thumb/', image.get_image_name()) diff --git a/app/photos/readexif.py b/app/photos/readexif.py new file mode 100644 index 0000000..0b3f7f7 --- /dev/null +++ b/app/photos/readexif.py @@ -0,0 +1,59 @@ +import time +from fractions import Fraction + +from django.contrib.gis.geos import Point + +import exiftool + +from locations.models import Location + + +def readexif(image): + """ + takes an image and fills in all the exif data tracked in the image model + + """ + with exiftool.ExifTool() as et: + meta = et.get_metadata(image.image.path) + et.terminate() + image.exif_raw = meta + try: + image.title = meta["EXIF:ImageDescription"] + except: + try: + image.title = meta["XMP:Title"] + except: + pass + try: + image.caption = meta["EXIF:UserComment"] + except: + pass + try: + image.exif_lens = meta["MakerNotes:LensType"] + except: + try: + image.exif_lens = meta["XMP:Lens"] + except: + pass + try: + image.point = Point(meta["XMP:GPSLongitude"], meta["XMP:GPSLatitude"], srid=4326) + try: + image.location = Location.objects.filter(geometry__contains=image.point).get() + except Location.DoesNotExist: + pass + except KeyError: + pass + try: + image.exif_aperture = meta["EXIF:FNumber"] + except: + pass + image.exif_make = meta["EXIF:Make"] + image.exif_model = meta["EXIF:Model"] + image.exif_exposure = str(Fraction(float(meta["EXIF:ExposureTime"])).limit_denominator()) + image.exif_iso = meta["EXIF:ISO"] + image.exif_focal_length = meta["EXIF:FocalLength"] + fmt_date = time.strptime(meta["EXIF:DateTimeOriginal"], "%Y:%m:%d %H:%M:%S") + image.exif_date = time.strftime("%Y-%m-%d %H:%M:%S", fmt_date) + image.height = meta["File:ImageHeight"] + image.width = meta["File:ImageWidth"] + return image diff --git a/app/photos/templatetags/get_image_size.py b/app/photos/templatetags/get_image_by_size.py index 4ca6c66..c56c44e 100644 --- a/app/photos/templatetags/get_image_size.py +++ b/app/photos/templatetags/get_image_by_size.py @@ -3,6 +3,6 @@ from django import template register = template.Library() @register.simple_tag -def get_image_size(obj, *args): - method = getattr(obj, "get_image_size") +def get_image_by_size(obj, *args): + method = getattr(obj, "get_image_by_size") return method(*args) diff --git a/app/photos/utils.py b/app/photos/utils.py index 6cc64a3..e880277 100644 --- a/app/photos/utils.py +++ b/app/photos/utils.py @@ -1,4 +1,7 @@ import os +import subprocess + +# pip install python-resize-image from resizeimage import resizeimage @@ -13,3 +16,4 @@ def resize_image(img, width=None, height=None, quality=72, base_path="", filenam os.makedirs(base_path) path = "%s%s" % (base_path, filename) newimg.save(path, newimg.format, quality=quality) + subprocess.call(["jpegoptim", "%s" % path]) |