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: 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) is_public = models.BooleanField(default=True) sizes = models.ManyToManyField(LuxImageSize, blank=True) 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_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 = "" 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) 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': s = LuxImageSize.objects.get(name=size) if s not in self.sizes.all(): 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())