import os.path
import io
import datetime
from PIL import Image
from django.core.exceptions import ValidationError
from django.contrib.gis.db import models
from django.contrib.sitemaps import Sitemap
from django.utils.encoding import force_text
from django.utils.functional import cached_property
from django.urls import reverse
from django.apps import apps
from django.utils.html import format_html
from django.utils.text import slugify
from django.conf import settings
from django import forms
from taggit.managers import TaggableManager
from resizeimage.imageexceptions import ImageSizeError
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/original/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename)
def get_vid_upload_path(self, filename):
return "images/videos/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename)
class LuxImageSize(models.Model):
name = models.CharField(null=True, blank=True, max_length=30)
width = models.IntegerField(null=True, blank=True)
height = models.IntegerField(null=True, blank=True)
quality = models.IntegerField()
class Meta:
ordering = ('-name', 'id')
verbose_name_plural = 'Image Sizes'
def __str__(self):
if self.width:
size = self.width
if self.height:
size = self.height
return "%s - %s" %(self.name, str(size))
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(default=datetime.datetime.now)
exif_raw = models.TextField(blank=True, null=True)
exif_aperture = models.CharField(max_length=50, blank=True, null=True)
exif_make = models.CharField(max_length=50, blank=True, null=True)
exif_model = models.CharField(max_length=50, blank=True, null=True)
exif_exposure = models.CharField(max_length=50, blank=True, null=True)
exif_iso = models.CharField(max_length=50, blank=True, null=True)
exif_focal_length = models.CharField(max_length=50, blank=True, null=True)
exif_lens = models.CharField(max_length=50, blank=True, null=True)
exif_date = models.DateTimeField(blank=True, null=True)
height = models.CharField(max_length=6, blank=True, null=True)
width = models.CharField(max_length=6, blank=True, null=True)
point = models.PointField(null=True, blank=True)
location = models.ForeignKey("locations.Location", on_delete=models.CASCADE, null=True, blank=True, related_name="+")
is_public = models.BooleanField(default=True)
sizes = models.ManyToManyField(LuxImageSize, blank=True, related_name="+")
flickr_id = models.CharField(null=True, blank=True, max_length=80)
twitter_link = models.CharField(null=True, blank=True, max_length=300)
facebook_link = models.CharField(null=True, blank=True, max_length=300)
class Meta:
ordering = ('-pub_date', 'id')
verbose_name_plural = 'Images'
get_latest_by = 'pub_date'
def __str__(self):
if self.title:
return "%s" % self.title
else:
return "%s" % self.pk
def get_type(self):
return str(self.__class__.__name__)
def get_admin_image(self):
for size in self.sizes.all():
if size.width and size.width <= 820 or size.height and size.height <= 800:
return self.get_image_by_size(size.name)
def get_admin_insert(self):
return "/media/images/%s/%s_tn.%s" % (self.pub_date.strftime("%Y"), self.get_image_name(), self.get_image_ext())
def get_largest_image(self):
t = []
for size in self.sizes.all():
t.append(size.width)
t.sort(key=float)
t.reverse()
return self.get_image_path_by_size(t[0])
def get_image_name(self):
return self.image.url.split("original/")[1][5:-4]
def get_image_name_new(self):
return os.path.basename(self.image.path)[:-4]
def get_image_ext(self):
return self.image.url[-3:]
@cached_property
def get_featured_jrnl(self):
''' cached version of getting the primary image for archive page'''
return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), 'featured_jrnl', self.get_image_ext())
@cached_property
def get_picwide_sm(self):
''' cached version of getting the second image for archive page'''
return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), 'picwide-sm', self.get_image_ext())
@cached_property
def get_srcset(self):
srcset = ""
length = len(self.sizes.all())
print(length)
loopnum = 1
for size in self.sizes.all():
srcset += "%s%s/%s_%s.%s %sw" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), size.name, self.get_image_ext(), size.width)
if loopnum < length:
srcset += ", "
loopnum = loopnum+1
return srcset
@cached_property
def get_src(self):
src = ""
if self.sizes.all().count() > 1:
src += "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), 'picwide-med', self.get_image_ext())
else:
src += "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), [size.name for size in self.sizes.all()], self.get_image_ext())
return src
def get_image_by_size(self, size="original"):
base = self.get_image_name()
if size == "admin_insert":
return "images/%s/%s.%s" % (self.pub_date.strftime("%Y"), base, self.get_image_ext())
if size == "original":
return "%soriginal/%s/%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), base, self.get_image_ext())
else:
if size != 'tn':
try:
self.sizes.filter(name=size)
except DoesNotExist:
print("new size is "+s.name)
self.sizes.add(s)
return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), base, size, self.get_image_ext())
def get_image_path_by_size(self, size="original"):
base = self.get_image_name()
if size == "original":
return "%s/original/%s/%s.%s" % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), base, self.get_image_ext())
else:
return "%s/%s/%s_%s.%s" % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), base, size, self.get_image_ext())
def get_thumbnail_url(self):
return self.get_image_by_size("tn")
def admin_thumbnail(self):
return format_html('' % (self.get_image_by_size(), self.get_image_by_size("tn")))
admin_thumbnail.short_description = 'Thumbnail'
def get_sizes(self):
return self.sizes.all()
@property
def latitude(self):
return self.point.y
@property
def longitude(self):
return self.point.x
@property
def get_previous_published(self):
return self.get_previous_by_pub_date()
@property
def get_next_published(self):
return self.get_next_by_pub_date()
@property
def get_previous_admin_url(self):
n = self.get_previous_by_pub_date()
return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] )
@property
def get_next_admin_url(self):
model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name)
try:
return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] )
except model.DoesNotExist:
return ''
@property
def is_portait(self):
if int(self.height) > int(self.width):
return True
else:
return False
def save(self, *args, **kwargs):
if not self.point:
self.point = LuxImage.objects.latest().point
try:
self.location = apps.get_model('locations', 'Location').objects.filter(
geometry__contains=self.point
).get()
except apps.get_model('locations', 'Location').DoesNotExist:
raise forms.ValidationError("There is no location associated with that point, add it: %sadmin/locations/location/add/" % (settings.BASE_URL))
super(LuxImage, self).save()
@receiver(post_save, sender=LuxImage)
def post_save_events(sender, update_fields, created, instance, **kwargs):
if instance.exif_raw == '':
filename, file_extension = os.path.splitext(instance.image.path)
if file_extension != ".mp4":
img = Image.open(instance.image.path)
instance.height = img.height
instance.width = img.width
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):
base_path = "%s/%s/" % (settings.IMAGES_ROOT, instance.pub_date.strftime("%Y"))
filename, file_extension = os.path.splitext(instance.image.path)
if file_extension != ".mp4":
img = Image.open(instance.image.path)
resize_image(img, 160, None, 78, base_path, "%s_tn.%s" % (instance.get_image_name(), instance.get_image_ext()))
for size in instance.sizes.all():
if size.width:
print("Image width is:"+str(img.width))
try:
if size.width <= img.width:
resize_image(img, size.width, None, size.quality, base_path, "%s_%s.%s" % (instance.get_image_name(), slugify(size.name), instance.get_image_ext()))
else:
raise ValidationError({'items': ["Size is larger than source image"]})
except ImageSizeError:
m2m_changed.disconnect(update_photo_sizes, sender=LuxImage.sizes.through)
instance.sizes.remove(size)
m2m_changed.connect(update_photo_sizes, sender=LuxImage.sizes.through)
if size.height:
try:
if size.height <= img.height:
resize_image(img, None, size.height, size.quality, base_path, "%s_%s.%s" % (instance.get_image_name(), slugify(size.name), instance.get_image_ext()))
else:
pass
except ImageSizeError:
m2m_changed.disconnect(update_photo_sizes, sender=LuxImage.sizes.through)
instance.sizes.remove(size)
m2m_changed.connect(update_photo_sizes, sender=LuxImage.sizes.through)
class LuxGallery(models.Model):
title = models.CharField(blank=True, max_length=300)
description = models.TextField(blank=True, null=True)
slug = models.CharField(blank=True, max_length=300)
thumb = models.ForeignKey(LuxImage, on_delete=models.CASCADE, related_name="gallery_thumb", null=True, blank=True)
images = models.ManyToManyField(LuxImage)
pub_date = models.DateTimeField(null=True)
point = models.PointField(null=True, blank=True)
location = models.ForeignKey("locations.Location", on_delete=models.CASCADE, null=True, blank=True)
is_public = models.BooleanField(default=True)
caption_style = models.CharField(blank=True, null=True, max_length=400)
class Meta:
ordering = ('-pub_date', 'id')
verbose_name_plural = 'Galleries'
get_latest_by = 'pub_date'
def __str__(self):
return self.title
def get_main_image(self):
return "%sgallery_thumbs/%s.jpg" % (settings.IMAGES_URL, self.id)
def get_absolute_url(self):
if self.is_public:
return "/photos/galleries/%s" % (self.slug)
else:
return "/photos/galleries/private/%s" % (self.slug)
def latitude(self):
if self.point:
return self.point.y
def longitude(self):
if self.point:
return self.point.x
def thumbs(self):
lst = [x.image.name for x in self.images.all()]
lst = ["%s" % (x, x.split('/')[-1]) for x in lst]
return ', '.join(item for item in lst if item)
thumbs.allow_tags = True
class LuxVideo(models.Model):
video_mp4 = models.FileField(blank=True, null=True, upload_to=get_vid_upload_path)
video_webm = models.FileField(blank=True, null=True, upload_to=get_vid_upload_path)
video_poster = models.FileField(blank=True, null=True, upload_to=get_vid_upload_path)
title = models.CharField(null=True, blank=True, max_length=300)
pub_date = models.DateTimeField(default=datetime.datetime.now)
youtube_url = models.CharField(null=True, blank=True, max_length=80)
vimeo_url = models.CharField(null=True, blank=True, max_length=300)
def __str__(self):
if self.title:
return self.title
else:
return str(self.pk)
def get_type(self):
return str(self.__class__.__name__)
class Meta:
ordering = ('-pub_date', 'id')
verbose_name_plural = 'Videos'
get_latest_by = 'pub_date'
class Photo(models.Model):
description = models.TextField(blank=True, null=True)
title = models.CharField(blank=True, max_length=300)
pub_date = models.DateTimeField()
tags = TaggableManager(blank=True)
exif_aperture = models.CharField(max_length=50, blank=True, null=True)
exif_make = models.CharField(max_length=50, blank=True, null=True)
exif_model = models.CharField(max_length=50, blank=True, null=True)
exif_exposure = models.CharField(max_length=50, blank=True, null=True)
exif_iso = models.CharField(max_length=50, blank=True, null=True)
exif_focal_length = models.CharField(max_length=50, blank=True, null=True)
exif_lens = models.CharField(max_length=50, blank=True, null=True)
exif_date = models.DateTimeField()
"""Flickr Specific Stuff"""
# Vlickr id is varchar since Flickr ids are larger than than integerfield can handle and BigIntegerField gets weird in Postgres.
flickr_id = models.CharField(max_length=300)
flickr_owner = models.CharField(max_length=20)
flickr_server = models.IntegerField()
flickr_farm = models.IntegerField()
flickr_secret = models.CharField(max_length=50)
flickr_originalsecret = models.CharField(max_length=50)
lon = models.FloatField('Longitude', help_text="Longitude of centerpoint", null=True)
lat = models.FloatField('Latitude', help_text="Latitude of centerpoint", null=True)
location = models.ForeignKey("locations.Location", on_delete=models.CASCADE, null=True)
region = models.ForeignKey("locations.Region", on_delete=models.CASCADE, null=True)
slideshowimage_width = models.CharField(max_length=4, blank=True, null=True)
slideshowimage_height = models.CharField(max_length=4, blank=True, null=True)
slideshowimage_margintop = models.CharField(max_length=4, blank=True, null=True)
slideshowimage_marginleft = models.CharField(max_length=4, blank=True, null=True)
is_public = models.BooleanField(default=True)
class Meta:
ordering = ('-pub_date',)
def admin_thumbnail(self):
return force_text('' % (self.get_absolute_url(), self.get_small_square_url()))
admin_thumbnail.allow_tags = True
admin_thumbnail.short_description = 'Thumbnail'
def get_local_medium_url(self):
return '%sflickr/med/%s/%s.jpg' % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.flickr_id)
def get_local_orig_url(self):
return '%s/flickr/full/%s/%s.jpg' % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), self.flickr_id)
def get_local_slideshow_url(self):
return '%sslideshow/%s/%s.jpg' % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.flickr_id)
def __str__(self):
return self.title
def get_absolute_url(self):
return "/photo/%s/" % (self.id)
def get_model_name(self):
return 'photo'
def get_small_square_url(self):
return self.get_pic_url(size="small_square")
def get_large_url(self):
return self.get_pic_url(size="large")
def get_small_url(self):
return self.get_pic_url(size="small")
def get_medium_url(self):
return self.get_pic_url(size="medium")
def get_original_url(self):
# return self.get_pic_url(size="original")
return "http://farm%s.static.flickr.com/%s/%s_%s_o.jpg" % (self.flickr_farm, self.flickr_server, self.flickr_id, self.flickr_originalsecret)
def get_retina_slideshow_url(self):
return '%sslideshow/%s/%sx2.jpg' % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.flickr_id)
def has_retina(self):
return os.path.isfile('%s/slideshow/%s/%sx2.jpg' % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), self.flickr_id))
@property
def get_height(self):
im = Image.open('%s/slideshow/%s/%s.jpg' % (settings.IMAGES_ROOT,
self.pub_date.strftime("%Y"), self.flickr_id))
xsize, ysize = im.size
return ysize
@property
def get_width(self):
im = Image.open('%s/slideshow/%s/%s.jpg' % (settings.IMAGES_ROOT,
self.pub_date.strftime("%Y"), self.flickr_id))
xsize, ysize = im.size
return xsize
@property
def get_margin_top(self):
im = Image.open('%s/slideshow/%s/%s.jpg' % (settings.IMAGES_ROOT,
self.pub_date.strftime("%Y"), self.flickr_id))
xsize, ysize = im.size
mtop = 340 - (ysize / 2)
return mtop
@property
def get_margin_left(self):
im = Image.open('%s/slideshow/%s/%s.jpg' % (settings.IMAGES_ROOT,
self.pub_date.strftime("%Y"), self.flickr_id))
xsize, ysize = im.size
mtop = 500 - (xsize / 2)
return mtop
@property
def flickr_link(self):
return '%s%s/' % ('http://www.flickr.com/photos/luxagraf/', self.flickr_id)
@property
def is_portait(self):
if int(self.slideshowimage_height) > int(self.slideshowimage_width):
return True
else:
return False
def get_pic_url(self, size='small'):
# small_square=75x75
# thumb=100 on longest side
# small=240 on longest side
# medium=500 on longest side
# large=1024 on longest side
# original=duh
base_url = "http://static.flickr.com"
size_char = 's' # default to small_square
if size == 'small_square':
size_char = '_s'
elif size == 'thumb':
size_char = '_t'
elif size == 'small':
size_char = '_m'
elif size == 'medium':
size_char = ''
elif size == 'large':
size_char = '_b'
elif size == 'original':
size_char = '_o'
return "http://farm%s.static.flickr.com/%s/%s_%s%s.jpg" % (self.flickr_farm, self.flickr_server, self.flickr_id, self.flickr_secret, size_char)
def get_tumble_image(self):
return "%s/crops/%s/%s.jpg" % (settings.IMAGES_URL, self.pub_date.strftime("%Y/%b").lower(), self.id)
def get_previous_published(self):
return self.get_previous_by_pub_date()
def get_next_published(self):
return self.get_next_by_pub_date()
def comment_period_open(self):
return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date
def save(self, *args, **kwargs):
super(Photo, self).save()
class PhotoGallery(models.Model):
set_id = models.CharField(blank=True, max_length=300)
set_title = models.CharField(blank=True, max_length=300)
set_desc = models.TextField(blank=True, null=True)
set_slug = models.CharField(blank=True, max_length=300)
primary = models.CharField(blank=True, max_length=300)
photos = models.ManyToManyField(Photo)
location = models.ForeignKey("locations.Location", on_delete=models.CASCADE, null=True)
region = models.ForeignKey("locations.Region", on_delete=models.CASCADE, null=True)
pub_date = models.DateTimeField(null=True)
class Meta:
ordering = ('-pub_date', 'id')
verbose_name_plural = 'Photo Galleries'
get_latest_by = 'pub_date'
def __str__(self):
return self.set_title
def get_main_image(self):
return "%sgallery_thumbs/%s.jpg" % (settings.IMAGES_URL, self.id)
def get_absolute_url(self):
return "/photos/galleries/%s/" % (self.set_slug)
class PhotoGallerySitemap(Sitemap):
changefreq = "never"
priority = 0.7
protocol = "https"
def items(self):
return PhotoGallery.objects.all()
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())