summaryrefslogtreecommitdiff
path: root/app/media
diff options
context:
space:
mode:
Diffstat (limited to 'app/media')
-rw-r--r--app/media/__init__.py0
-rw-r--r--app/media/admin.py37
-rw-r--r--app/media/build.py48
-rw-r--r--app/media/migrations/0001_initial.py111
-rw-r--r--app/media/migrations/__init__.py0
-rw-r--r--app/media/models.py359
-rw-r--r--app/media/photos.js71
-rw-r--r--app/media/resize.py53
-rw-r--r--app/media/retriever.py323
-rw-r--r--app/media/retriever.py.bak314
-rw-r--r--app/media/static/image-preview.js42
-rw-r--r--app/media/static/my_styles.css40
-rw-r--r--app/media/templatetags/__init__.py0
-rw-r--r--app/media/templatetags/get_image_by_size.py8
-rw-r--r--app/media/templatetags/get_image_width.py9
-rw-r--r--app/media/templatetags/get_size_by_name.py8
-rw-r--r--app/media/urls.py74
-rw-r--r--app/media/utils.py28
-rw-r--r--app/media/views.py130
19 files changed, 1655 insertions, 0 deletions
diff --git a/app/media/__init__.py b/app/media/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/app/media/__init__.py
diff --git a/app/media/admin.py b/app/media/admin.py
new file mode 100644
index 0000000..12d0509
--- /dev/null
+++ b/app/media/admin.py
@@ -0,0 +1,37 @@
+from django.contrib import admin
+from django.contrib.gis.admin import OSMGeoAdmin
+
+from .models import LuxImage, LuxGallery, LuxImageSize, LuxVideo, LuxAudio
+
+
+@admin.register(LuxImageSize)
+class LuxImageSizeAdmin(OSMGeoAdmin):
+ list_display = ('name', 'width', 'height', 'quality')
+
+
+@admin.register(LuxVideo)
+class LuxVideoAdmin(OSMGeoAdmin):
+ pass
+
+
+@admin.register(LuxImage)
+class LuxImageAdmin(OSMGeoAdmin):
+ list_display = ('pk', 'admin_thumbnail', 'pub_date', 'caption')
+ list_filter = ('pub_date',)
+ search_fields = ['title', 'caption']
+ # Options for OSM map Using custom ESRI topo map
+
+ fieldsets = (
+ (None, {
+ 'fields': ('title', ('image'), 'pub_date', 'sizes', 'alt', 'caption', ('is_public'), ('photo_credit_source', 'photo_credit_url'))
+ }),
+ )
+
+ class Media:
+ js = ('image-preview.js', 'next-prev-links.js')
+
+
+@admin.register(LuxAudio)
+class LuxAudioAdmin(OSMGeoAdmin):
+ list_display = ('pk', 'title', 'pub_date')
+ list_filter = ('pub_date',)
diff --git a/app/media/build.py b/app/media/build.py
new file mode 100644
index 0000000..e95cbfc
--- /dev/null
+++ b/app/media/build.py
@@ -0,0 +1,48 @@
+import os
+from django.urls import reverse
+from builder.base import BuildNew
+
+from .models import LuxImage
+
+
+class BuildLuxPhotos(BuildNew):
+
+ def build(self):
+ self.build_detail_view()
+ self.build_daily_photo()
+
+ def get_model_queryset(self):
+ return self.model.objects.all()
+
+ def build_daily_photo(self):
+ '''
+ build out images that I post daily, found by title prefix daily_
+ '''
+ self.build_list_view(
+ base_path=reverse("photos:daily_photo_list"),
+ qs=LuxImage.objects.filter(is_public=True, title__startswith="daily_"),
+ paginate_by=10
+ )
+
+ def build_detail_view(self):
+ '''
+ write out all the expenses for each trip
+ '''
+ for obj in self.get_model_queryset():
+ url = obj.get_absolute_url()
+ path, slug = os.path.split(url)
+ path = '%s/' % path
+ # write html
+ response = self.client.get(url)
+ print(path, slug)
+ self.write_file(path, response.content, filename=slug)
+
+
+def dailybuilder():
+ j = BuildLuxPhotos("photos", "LuxImage")
+ j.build_daily_photo()
+
+
+def builder():
+ j = BuildLuxPhotos("photos", "LuxGallery")
+ j.build()
diff --git a/app/media/migrations/0001_initial.py b/app/media/migrations/0001_initial.py
new file mode 100644
index 0000000..623e685
--- /dev/null
+++ b/app/media/migrations/0001_initial.py
@@ -0,0 +1,111 @@
+# Generated by Django 3.2.8 on 2021-10-06 20:30
+
+import datetime
+from django.db import migrations, models
+import django.db.models.deletion
+import media.models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='LuxAudio',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('title', models.CharField(max_length=200)),
+ ('subtitle', models.CharField(blank=True, max_length=200)),
+ ('slug', models.SlugField(blank=True, unique_for_date='pub_date')),
+ ('body_html', models.TextField(blank=True)),
+ ('body_markdown', models.TextField(blank=True)),
+ ('pub_date', models.DateTimeField(default=datetime.datetime.now)),
+ ('mp3', models.FileField(blank=True, null=True, upload_to=media.models.get_audio_upload_path)),
+ ('ogg', models.FileField(blank=True, null=True, upload_to=media.models.get_audio_upload_path)),
+ ],
+ options={
+ 'verbose_name': 'Audio',
+ 'verbose_name_plural': 'Audio',
+ 'ordering': ('-pub_date',),
+ 'get_latest_by': 'pub_date',
+ },
+ ),
+ migrations.CreateModel(
+ name='LuxImageSize',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(blank=True, max_length=30, null=True)),
+ ('width', models.IntegerField(blank=True, null=True)),
+ ('height', models.IntegerField(blank=True, null=True)),
+ ('quality', models.IntegerField()),
+ ],
+ options={
+ 'verbose_name_plural': 'Image Sizes',
+ 'ordering': ('-name', 'id'),
+ },
+ ),
+ migrations.CreateModel(
+ name='LuxVideo',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('video_mp4', models.FileField(blank=True, null=True, upload_to=media.models.get_vid_upload_path)),
+ ('video_webm', models.FileField(blank=True, null=True, upload_to=media.models.get_vid_upload_path)),
+ ('video_poster', models.FileField(blank=True, null=True, upload_to=media.models.get_vid_upload_path)),
+ ('title', models.CharField(blank=True, max_length=300, null=True)),
+ ('pub_date', models.DateTimeField(default=datetime.datetime.now)),
+ ('youtube_url', models.CharField(blank=True, max_length=80, null=True)),
+ ('vimeo_url', models.CharField(blank=True, max_length=300, null=True)),
+ ],
+ options={
+ 'verbose_name': 'Video',
+ 'verbose_name_plural': 'Videos',
+ 'ordering': ('-pub_date', 'id'),
+ 'get_latest_by': 'pub_date',
+ },
+ ),
+ migrations.CreateModel(
+ name='LuxImage',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('image', models.FileField(blank=True, null=True, upload_to=media.models.get_upload_path)),
+ ('title', models.CharField(blank=True, max_length=300, null=True)),
+ ('alt', models.CharField(blank=True, max_length=300, null=True)),
+ ('photo_credit_source', models.CharField(blank=True, max_length=300, null=True)),
+ ('photo_credit_url', models.CharField(blank=True, max_length=300, null=True)),
+ ('caption', models.TextField(blank=True, null=True)),
+ ('pub_date', models.DateTimeField(default=datetime.datetime.now)),
+ ('height', models.CharField(blank=True, max_length=6, null=True)),
+ ('width', models.CharField(blank=True, max_length=6, null=True)),
+ ('is_public', models.BooleanField(default=True)),
+ ('sizes', models.ManyToManyField(blank=True, to='media.LuxImageSize')),
+ ],
+ options={
+ 'verbose_name_plural': 'Images',
+ 'ordering': ('-pub_date', 'id'),
+ 'get_latest_by': 'pub_date',
+ },
+ ),
+ migrations.CreateModel(
+ name='LuxGallery',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('title', models.CharField(blank=True, max_length=300)),
+ ('description', models.TextField(blank=True, null=True)),
+ ('slug', models.CharField(blank=True, max_length=300)),
+ ('pub_date', models.DateTimeField(null=True)),
+ ('is_public', models.BooleanField(default=True)),
+ ('caption_style', models.CharField(blank=True, max_length=400, null=True)),
+ ('images', models.ManyToManyField(to='media.LuxImage')),
+ ('thumb', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='gallery_thumb', to='media.luximage')),
+ ],
+ options={
+ 'verbose_name_plural': 'Galleries',
+ 'ordering': ('-pub_date', 'id'),
+ 'get_latest_by': 'pub_date',
+ },
+ ),
+ ]
diff --git a/app/media/migrations/__init__.py b/app/media/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/app/media/migrations/__init__.py
diff --git a/app/media/models.py b/app/media/models.py
new file mode 100644
index 0000000..c385018
--- /dev/null
+++ b/app/media/models.py
@@ -0,0 +1,359 @@
+import os.path
+import io
+import datetime
+from PIL import Image
+
+from django.core.exceptions import ValidationError
+from django.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 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)
+
+
+def get_audio_upload_path(self, filename):
+ return "audio/%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)
+ height = models.CharField(max_length=6, blank=True, null=True)
+ width = models.CharField(max_length=6, blank=True, null=True)
+ is_public = models.BooleanField(default=True)
+ sizes = models.ManyToManyField(LuxImageSize, blank=True)
+
+ 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 = ""
+ 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':
+ 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('<a href="%s"><img src="%s"></a>' % (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 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):
+ super(LuxImage, self).save()
+
+
+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)
+ 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 thumbs(self):
+ lst = [x.image.name for x in self.images.all()]
+ lst = ["<a href='/media/%s'>%s</a>" % (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'
+ verbose_name = 'Video'
+ get_latest_by = 'pub_date'
+
+ def save(self, *args, **kwargs):
+ super(LuxVideo, self).save(*args, **kwargs)
+
+
+class LuxAudio(models.Model):
+ title = models.CharField(max_length=200)
+ subtitle = models.CharField(max_length=200, blank=True)
+ slug = models.SlugField(unique_for_date='pub_date', blank=True)
+ body_html = models.TextField(blank=True)
+ body_markdown = models.TextField(blank=True)
+ pub_date = models.DateTimeField(default=datetime.datetime.now)
+ mp3 = models.FileField(blank=True, null=True, upload_to=get_audio_upload_path)
+ ogg = models.FileField(blank=True, null=True, upload_to=get_audio_upload_path)
+
+ class Meta:
+ ordering = ('-pub_date',)
+ verbose_name_plural = 'Audio'
+ verbose_name = 'Audio'
+ get_latest_by = 'pub_date'
+
+ def __str__(self):
+ return self.title
+
+ def get_absolute_url(self):
+ return reverse("prompt:detail", kwargs={"slug": self.slug})
+
+ @property
+ def get_previous_published(self):
+ return self.get_previous_by_pub_date(status__exact=1)
+
+ @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_published(self):
+ return self.get_next_by_pub_date(status__exact=1)
+
+ @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 ''
+
+ def get_type(self):
+ return str(self.__class__.__name__)
+
+ def save(self, *args, **kwargs):
+ md = render_images(self.body_markdown)
+ self.body_html = markdown_to_html(md)
+ super(LuxAudio, self).save(*args, **kwargs)
+
+
+@receiver(post_save, sender=LuxImage)
+def post_save_events(sender, update_fields, created, instance, **kwargs):
+ 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
+ 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)
+
+
diff --git a/app/media/photos.js b/app/media/photos.js
new file mode 100644
index 0000000..b93467a
--- /dev/null
+++ b/app/media/photos.js
@@ -0,0 +1,71 @@
+//Utility functions for map info window
+function mapit(obj) {
+ lat = parseFloat(obj.attr('data-latitude'));
+ lon = parseFloat(obj.attr('data-longitude'));
+ elid= obj.attr('data-imgid');
+ map = L.map(document.getElementById("mw-"+elid));
+ centerCoord = new L.LatLng(lat, lon);
+ zoom = 8;
+ L.tileLayer.provider('Esri.WorldTopoMap', {maxZoom: 18, attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Tiles &copy; Esri and the GIS User Community'}).addTo(map);
+ map.setView(centerCoord, zoom);
+ L.marker([lat, lon]).addTo(map);
+}
+ //########## utility functions to create/remove map container ############
+function create_map(obj) {
+ //find id of this image caption:
+ var imgid = obj.attr('data-imgid');
+ //create container divs
+ $('<div class="map-container" id="mc-'+imgid+'">').insertBefore($(obj).parent().parent());
+ //$(obj).parent().parent().parent().prepend('<div class="map-container" id="mc-'+imgid+'">');
+ $('#mc-'+imgid).append('<div class="map-wrapper" id="mw-'+imgid+'">');
+ //deal with the variable height of div.legend
+ $('#mc-'+imgid).css({
+ bottom: function(index, value) {
+ return parseFloat($(obj).parent().parent().height())+20;
+ }
+ });
+
+ mapit(obj);
+}
+function remove_map(imgid) {
+ $('#mc-'+imgid).remove();
+}
+
+//############ Document.ready events ##############
+$(document).ready(function(){
+
+ //set up click events for map button
+ $('.map-link').click( function() {
+ imgid = $(this).attr('data-imgid');
+ if ($('#mc-'+imgid).is(":visible")) {
+ remove_map(imgid);
+ } else {
+ create_map($(this));
+ }
+ return false;
+
+ });
+ var $ele = $('#slides').children();
+ var $curr = 0;
+ $(document).bind('keydown', function (e) {
+ var code = e.which;
+ switch (code) {
+ case 39:
+ if ($curr <= $ele.size()) {
+ $.scrollTo($ele[$curr], 800 );
+ $curr++;
+ }
+ break;
+ case 37:
+ if ($curr > 0) {
+ $curr--;
+ var $now = $curr;
+ $now--;
+ $.scrollTo($ele[$now], 800 );
+ }
+ break;
+ }
+ return;
+ });
+});
+
diff --git a/app/media/resize.py b/app/media/resize.py
new file mode 100644
index 0000000..13c0151
--- /dev/null
+++ b/app/media/resize.py
@@ -0,0 +1,53 @@
+import os
+import io
+
+from django.conf import settings
+
+try:
+ import Image
+ import ImageFile
+except ImportError:
+ try:
+ from PIL import Image
+ from PIL import ImageFile
+ except ImportError:
+ raise ImportError("Could not import the Python Imaging Library.")
+
+ImageFile.MAXBLOCK = 1000000
+
+
+def make_local_copies(self,photo):
+ orig_dir = settings.IMAGES_ROOT + '/flickr/full/' + photo.pub_date.strftime("%Y")
+ if not os.path.isdir(orig_dir):
+ os.makedirs(orig_dir)
+ im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image
+ img = Image.open(im)
+ local_full = '%s/%s.jpg' % (orig_dir, photo.flickr_id)
+ img.save(local_full)
+
+ #calculate crop:
+ cur_width, cur_height = img.size
+ new_width, new_height = 291, 350
+ ratio = max(float(new_width) / cur_width, float(new_height) / cur_height)
+ x = (cur_width * ratio)
+ y = (cur_height * ratio)
+ xd = abs(new_width - x)
+ yd = abs(new_height - y)
+ x_diff = int(xd / 2)
+ y_diff = int(yd / 2)
+ box = (int(x_diff), int(y_diff), int(x_diff + new_width), int(y_diff + new_height))
+
+ # create resized file
+ resized = img.resize((int(x), int(y)), Image.ANTIALIAS).crop(box)
+ # save resized file
+ resized_filename = '%s/%s.jpg' % (crop_dir, set.id)
+ try:
+ if img.format == 'JPEG':
+ resized.save(resized_filename, 'JPEG', quality=95, optimize=True)
+ else:
+ resized.save(resized_filename)
+ except IOError as e:
+ if os.path.isfile(resized_filename):
+ os.unlink(resized_filename)
+ raise e
+ os.unlink(img)
diff --git a/app/media/retriever.py b/app/media/retriever.py
new file mode 100644
index 0000000..f5cae68
--- /dev/null
+++ b/app/media/retriever.py
@@ -0,0 +1,323 @@
+import json
+import datetime
+import os
+import io
+import urllib.request
+import urllib.parse
+import urllib.error
+
+from django.template.defaultfilters import slugify
+from django.core.exceptions import ObjectDoesNotExist
+from django.utils.encoding import force_text
+from django.conf import settings
+
+from photos.models import Photo, PhotoGallery
+
+# from https://github.com/alexis-mignon/python-flickr-api
+# terribly documented, but offers a good clean OOP approach if you're willing to figure it out...
+import flickr_api
+import flickrapi
+
+# Required PIL classes may or may not be available from the root namespace depending on the installation
+try:
+ import Image
+ import ImageFile
+except ImportError:
+ try:
+ from PIL import Image
+ from PIL import ImageFile
+ except ImportError:
+ raise ImportError("Could not import the Python Imaging Library.")
+
+ImageFile.MAXBLOCK = 1000000
+
+EXIF_PARAMS = {
+ "FNumber": 'f/2.8',
+ "Make": 'Apple',
+ "Model": 'iPhone',
+ "ExposureTime": '',
+ "ISO": '',
+ "FocalLength": '',
+ "LensModel": '',
+ 'DateTimeOriginal': '2013:09:03 22:44:25'
+}
+
+class SyncFlickr():
+
+ def __init__(self):
+ self.flickr = flickrapi.FlickrAPI(settings.FLICKR_API_KEY, settings.FLICKR_API_SECRET,format='parsed-json')
+
+
+ def sync_sets(self, *args, **kwargs):
+ p = self.flickr.photosets.getList(user_id='85322932@N00')
+ disregard = [
+ 'POTD 2008',
+ 'Snow Day',
+ 'Wedding',
+ 'Some random stuff',
+ 'Lilah & Olivia',
+ '6 months+',
+ '6-9 months',
+ '9-18 months',
+ ]
+ for photoset in p['photosets']['photoset']:
+ if photoset['title']['_content'] in disregard:
+ pass
+ else:
+ try:
+ row = PhotoGallery.objects.get(set_id__exact=photoset['id'])
+ print(('%s %s %s' % ('already have', row.set_title, 'moving on...')))
+ # okay it already exists, but is it up-to-date?
+ self.get_photos_in_set(photoset['id'],row)
+ except ObjectDoesNotExist:
+ s = PhotoGallery.objects.get_or_create(
+ set_id=force_text(photoset['id']),
+ set_title=force_text(photoset['title']['_content']),
+ set_desc=force_text(photoset['description']['_content']),
+ set_slug=slugify(force_text(photoset['title']['_content'])[:40]),
+ primary=force_text(photoset['primary']),
+ pub_date=datetime.datetime.fromtimestamp(float(photoset['date_create']))
+ )
+
+ #get_photos_in_set(photoset, s)
+ #create the gallery thumbnail image:
+ #photo = Photo.objects.get(flickr_id__exact=str(photoset['primary']))
+ #make_gallery_thumb(photo, s)
+
+
+
+ def get_photos_in_set(self, flickr_id, photoset):
+ photos = self.flickr.photosets.getPhotos(photoset_id=flickr_id)
+ for photo in photos['photoset']['photo']:
+ try:
+ p = Photo.objects.get(flickr_id__exact=str(photo['id']))
+ except ObjectDoesNotExist:
+ p = self.get_photo(photo['id'])
+ if p.is_public:
+ pass #photoset.photos.add(p)
+ #slideshow_image(p, 1000, 800, 95)
+ print(p)
+
+ def get_photo(self, photo_id):
+ photo = self.flickr.photos.getInfo(photo_id=photo_id)
+ info = photo['photo']
+ try:
+ geo = self.flickr.photos.geo.getLocation(photo_id=photo_id)
+ location, region = self.get_geo(float(geo['photo']['location']['latitude']), float(geo['photo']['location']['longitude']))
+ except KeyError:
+ print("no effing geodata asshat")
+ exif = self.exif_handler(self.flickr.photos.getExif(photo_id=photo_id)['photo']['exif'])
+ p, created = Photo.objects.get_or_create(
+ title=info['title']['_content'],
+ flickr_id=info['id'],
+ flickr_owner=info['owner']['nsid'],
+ flickr_server=info['server'],
+ flickr_secret=info['secret'],
+ flickr_originalsecret=info['originalsecret'],
+ flickr_farm=info['farm'],
+ pub_date=self.flickr_datetime_to_datetime(exif["DateTimeOriginal"].replace(':', '-', 2)),
+ description=info['description']['_content'],
+ exif_aperture=exif['FNumber'],
+ exif_make=exif['Make'],
+ exif_model=exif['Model'],
+ exif_exposure=exif['ExposureTime'],
+ exif_iso=exif['ISO'],
+ exif_lens=exif['LensModel'],
+ exif_focal_length=exif['FocalLength'],
+ exif_date=self.flickr_datetime_to_datetime(exif["DateTimeOriginal"].replace(':', '-', 2)),
+ lat=float(geo['photo']['location']['latitude']),
+ lon=float(geo['photo']['location']['longitude']),
+ region=region,
+ location=location,
+ )
+ if created:
+ for tag in info['tags']['tag']:
+ p.tags.add(tag['raw'])
+ p.save()
+
+ local = FlickrImage()
+ local.make_local_copies(p)
+ #retina image:
+ #slideshow_image(p, 2000, 1600, 75)
+ #normal image
+ print("grabbing... "+p.title)
+ return p
+
+
+ def sync_flickr_photos(self, *args, **kwargs):
+ photos = self.flickr.people.getPhotos(user_id="85322932@N00", extras="date_upload,date_taken,geo")
+ for photo in photos['photos']['photo']:
+ try:
+ row = Photo.objects.get(flickr_id=photo['id'], flickr_secret=photo['secret'])
+ print('already have ' + photo['id'] + ' moving on')
+ except ObjectDoesNotExist:
+ p = self.get_photo(photo['id'])
+
+
+
+ """
+ ################################################
+ ## Various meta data and geo helper functions ##
+ ################################################
+ """
+
+ def exif_handler(self, data):
+ converted = {}
+ try:
+ for t in data:
+ converted[t['tag']] = t['raw']['_content']
+ except:
+ pass
+ for k, v in list(EXIF_PARAMS.items()):
+ if k not in converted:
+ converted[k] = v
+ return converted
+
+
+ def flickr_datetime_to_datetime(self, fdt):
+ from datetime import datetime
+ from time import strptime
+ date_parts = strptime(fdt, '%Y-%m-%d %H:%M:%S')
+ return datetime(*date_parts[0:6])
+
+
+ def get_geo(self, lat, lon):
+ from locations.models import Location, Region
+ from django.contrib.gis.geos import Point
+ pnt_wkt = Point(lon, lat)
+ try:
+ location = Location.objects.get(geometry__contains=pnt_wkt)
+ except Location.DoesNotExist:
+ location = None
+ try:
+ region = Region.objects.get(geometry__contains=pnt_wkt)
+ except Region.DoesNotExist:
+ region = None
+ return location, region
+
+
+
+
+
+
+class FlickrImage():
+ """
+ ## Photo retrieval functions to pull down images from Flickr servers ##
+ """
+
+ def slideshow_image(self, photo, max_width, max_height, quality):
+ slide_dir = settings.IMAGES_ROOT + '/slideshow/' + photo.pub_date.strftime("%Y")
+ if not os.path.isdir(slide_dir):
+ os.makedirs(slide_dir)
+
+ # Is it a retina image or not?
+ if max_width >= 1001 or max_height >= 801:
+ filename = '%s/%sx2.jpg' % (slide_dir, photo.flickr_id)
+ else:
+ filename = '%s/%s.jpg' % (slide_dir, photo.flickr_id)
+
+ flickr_photo = photo.get_original_url()
+ fname = urllib.request.urlopen(flickr_photo)
+ im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image
+ img = Image.open(im)
+ cur_width, cur_height = img.size
+ #if image landscape
+ if cur_width > cur_height:
+ new_width = max_width
+ #check to make sure we aren't upsizing
+ if cur_width > new_width:
+ ratio = float(new_width) / cur_width
+ x = (cur_width * ratio)
+ y = (cur_height * ratio)
+ resized = img.resize((int(x), int(y)), Image.ANTIALIAS)
+ resized.save(filename, 'JPEG', quality=quality, optimize=True)
+ else:
+ img.save(filename)
+ else:
+ #image portrait
+ new_height = max_height
+ #check to make sure we aren't upsizing
+ if cur_height > new_height:
+ ratio = float(new_height) / cur_height
+ x = (cur_width * ratio)
+ y = (cur_height * ratio)
+ resized = img.resize((int(x), int(y)), Image.ANTIALIAS)
+ resized.save(filename, 'JPEG', quality=quality, optimize=True)
+ else:
+ img.save(filename)
+ photo.slideshowimage_width = photo.get_width
+ photo.slideshowimage_height = photo.get_height
+ photo.slideshowimage_margintop = photo.get_margin_top
+ photo.slideshowimage_marginleft = photo.get_margin_left
+ photo.save()
+ #now resize the local copy
+
+
+ def make_local_copies(self,photo):
+ orig_dir = settings.IMAGES_ROOT + '/flickr/full/' + photo.pub_date.strftime("%Y")
+ if not os.path.isdir(orig_dir):
+ os.makedirs(orig_dir)
+ full = photo.get_original_url()
+ fname = urllib.request.urlopen(full)
+ im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image
+ img = Image.open(im)
+ local_full = '%s/%s.jpg' % (orig_dir, photo.flickr_id)
+ img.save(local_full)
+ #save large size
+ large_dir = settings.IMAGES_ROOT + '/flickr/large/' + photo.pub_date.strftime("%Y")
+ if not os.path.isdir(large_dir):
+ os.makedirs(large_dir)
+ large = photo.get_large_url()
+ fname = urllib.request.urlopen(large)
+ im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image
+ img = Image.open(im)
+ local_large = '%s/%s.jpg' % (large_dir, photo.flickr_id)
+ if img.format == 'JPEG':
+ img.save(local_large)
+ #save medium size
+ med_dir = settings.IMAGES_ROOT + '/flickr/med/' + photo.pub_date.strftime("%Y")
+ if not os.path.isdir(med_dir):
+ os.makedirs(med_dir)
+ med = photo.get_medium_url()
+ fname = urllib.request.urlopen(med)
+ im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image
+ img = Image.open(im)
+ local_med = '%s/%s.jpg' % (med_dir, photo.flickr_id)
+ img.save(local_med)
+
+
+ def make_gallery_thumb(self, photo, set):
+ crop_dir = settings.IMAGES_ROOT + '/gallery_thumbs/'
+ if not os.path.isdir(crop_dir):
+ os.makedirs(crop_dir)
+ remote = photo.get_original_url()
+ print(remote)
+ fname = urllib.request.urlopen(remote)
+ im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image
+ img = Image.open(im)
+ #calculate crop:
+ cur_width, cur_height = img.size
+ new_width, new_height = 291, 350
+ ratio = max(float(new_width) / cur_width, float(new_height) / cur_height)
+ x = (cur_width * ratio)
+ y = (cur_height * ratio)
+ xd = abs(new_width - x)
+ yd = abs(new_height - y)
+ x_diff = int(xd / 2)
+ y_diff = int(yd / 2)
+ box = (int(x_diff), int(y_diff), int(x_diff + new_width), int(y_diff + new_height))
+
+ # create resized file
+ resized = img.resize((int(x), int(y)), Image.ANTIALIAS).crop(box)
+ # save resized file
+ resized_filename = '%s/%s.jpg' % (crop_dir, set.id)
+ try:
+ if img.format == 'JPEG':
+ resized.save(resized_filename, 'JPEG', quality=95, optimize=True)
+ else:
+ resized.save(resized_filename)
+ except IOError as e:
+ if os.path.isfile(resized_filename):
+ os.unlink(resized_filename)
+ raise e
+ os.unlink(img)
diff --git a/app/media/retriever.py.bak b/app/media/retriever.py.bak
new file mode 100644
index 0000000..d3c572a
--- /dev/null
+++ b/app/media/retriever.py.bak
@@ -0,0 +1,314 @@
+from __future__ import division
+import datetime
+import os
+import cStringIO
+import urllib
+
+from django.template.defaultfilters import slugify
+from django.core.exceptions import ObjectDoesNotExist
+from django.utils.encoding import force_unicode
+from django.conf import settings
+
+# Required PIL classes may or may not be available from the root namespace
+# depending on the installation
+try:
+ import Image
+ import ImageFile
+except ImportError:
+ try:
+ from PIL import Image
+ from PIL import ImageFile
+ except ImportError:
+ raise ImportError("Could not import the Python Imaging Library.")
+
+ImageFile.MAXBLOCK = 1000000
+
+from photos.models import Photo, PhotoGallery
+
+# from https://github.com/alexis-mignon/python-flickr-api
+# terribly documented, but offers a good clean OOP approach if you're willing to figure it out...
+import flickr_api
+
+EXIF_PARAMS = {
+ "FNumber": 'f/2.8',
+ "Make": 'Apple',
+ "Model": 'iPhone',
+ "ExposureTime": '',
+ "ISO": '',
+ "FocalLength": '',
+ "LensModel": '',
+ 'DateTimeOriginal': '2013:09:03 22:44:25'
+}
+
+
+def sync_flickr_photos(*args, **kwargs):
+ flickr_api.set_keys(api_key=settings.FLICKR_API_KEY, api_secret=settings.FLICKR_API_SECRET)
+ flickr_api.set_auth_handler("app/photos/flickrauth")
+ user = flickr_api.test.login()
+ photos = user.getPhotos(extras="date_upload,date_taken,geo")
+ # reverse! reverse!
+ photos.reverse()
+ for photo in photos:
+ info = photo.getInfo()
+ try:
+ row = Photo.objects.get(flickr_id=info['id'], flickr_secret=info['secret'])
+ print('already have ' + info['id'] + ' moving on')
+ except ObjectDoesNotExist:
+ get_photo(photo)
+
+
+def get_photo(photo):
+ info = photo.getInfo()
+ geo = photo.getLocation()
+ location, region = get_geo(float(geo['latitude']), float(geo['longitude']))
+ exif = exif_handler(photo.getExif())
+ p, created = Photo.objects.get_or_create(
+ title=info['title'],
+ flickr_id=info['id'],
+ flickr_owner=info['owner']['id'],
+ flickr_server=info['server'],
+ flickr_secret=info['secret'],
+ flickr_originalsecret=info['originalsecret'],
+ flickr_farm=info['farm'],
+ pub_date=flickr_datetime_to_datetime(info['taken']),
+ description=info['description'],
+ exif_aperture=exif['FNumber'],
+ exif_make=exif['Make'],
+ exif_model=exif['Model'],
+ exif_exposure=exif['ExposureTime'],
+ exif_iso=exif['ISO'],
+ exif_lens=exif['LensModel'],
+ exif_focal_length=exif['FocalLength'],
+ exif_date=flickr_datetime_to_datetime(exif["DateTimeOriginal"].replace(':', '-', 2)),
+ lat=float(geo['latitude']),
+ lon=float(geo['longitude']),
+ region=region,
+ location=location,
+ )
+ if created:
+ for tag in info['tags']:
+ p.tags.add(tag['raw'])
+ p.save()
+ make_local_copies(p)
+ #retina image:
+ #slideshow_image(p, 2000, 1600, 75)
+ #normal image
+ print(p.title)
+ return p
+
+
+def sync_sets(*args, **kwargs):
+ flickr_api.set_keys(api_key=settings.FLICKR_API_KEY, api_secret=settings.FLICKR_API_SECRET)
+ flickr_api.set_auth_handler("app/photos/flickrauth")
+ user = flickr_api.test.login()
+ photosets = user.getPhotosets()
+ # reverse! reverse!
+ photosets.reverse()
+ disregard = [
+ 'POTD 2008',
+ 'Snow Day',
+ 'Wedding',
+ 'Some random stuff',
+ 'Lilah & Olivia',
+ '6 months+',
+ '6-9 months',
+ '9-18 months',
+ ]
+ for photoset in photosets:
+ if photoset['title'] in disregard:
+ pass
+ else:
+ try:
+ row = PhotoGallery.objects.get(set_id__exact=photoset['id'])
+ print('%s %s %s' % ('already have', row.set_title, 'moving on...'))
+ # okay it already exists, but is it up-to-date?
+ #get_photos_in_set(row,set.id)
+ except ObjectDoesNotExist:
+ s = PhotoGallery.objects.create(
+ set_id=force_unicode(photoset['id']),
+ set_title=force_unicode(photoset['title']),
+ set_desc=force_unicode(photoset['description']),
+ set_slug=slugify(force_unicode(photoset['title'])),
+ primary=force_unicode(photoset['primary']),
+ pub_date=datetime.datetime.fromtimestamp(float(photoset['date_create']))
+ )
+
+ get_photos_in_set(photoset, s)
+ #create the gallery thumbnail image:
+ photo = Photo.objects.get(flickr_id__exact=str(photoset['primary']))
+ make_gallery_thumb(photo, s)
+
+
+def get_photos_in_set(flickr_photoset, photoset):
+ for photo in flickr_photoset.getPhotos():
+ try:
+ p = Photo.objects.get(flickr_id__exact=str(photo['id']))
+ except ObjectDoesNotExist:
+ p = get_photo(photo)
+ if p.is_public:
+ photoset.photos.add(p)
+ slideshow_image(p, 1000, 800, 95)
+
+
+################################################
+## Various meta data and geo helper functions ##
+################################################
+
+
+def exif_handler(data):
+ converted = {}
+ try:
+ for t in data:
+ converted[t['tag']] = t['raw']
+ except:
+ pass
+ for k, v in EXIF_PARAMS.items():
+ if not converted.has_key(k):
+ converted[k] = v
+ return converted
+
+
+def flickr_datetime_to_datetime(fdt):
+ from datetime import datetime
+ from time import strptime
+ date_parts = strptime(fdt, '%Y-%m-%d %H:%M:%S')
+ return datetime(*date_parts[0:6])
+
+def get_geo(lat,lon):
+ from locations.models import Location, Region
+ from django.contrib.gis.geos import Point
+ pnt_wkt = Point(lon, lat)
+ try:
+ location = Location.objects.get(geometry__contains=pnt_wkt)
+ except Location.DoesNotExist:
+ location = None
+ try:
+ region = Region.objects.get(geometry__contains=pnt_wkt)
+ except Region.DoesNotExist:
+ region = None
+ return location, region
+
+#######################################################################
+## Photo retrieval functions to pull down images from Flickr servers ##
+#######################################################################
+
+def slideshow_image(photo,max_width, max_height, quality):
+ slide_dir = settings.IMAGES_ROOT + '/slideshow/'+ photo.pub_date.strftime("%Y")
+ if not os.path.isdir(slide_dir):
+ os.makedirs(slide_dir)
+
+ # Is it a retina image or not?
+ if max_width >= 1001 or max_height >= 801:
+ filename = '%s/%sx2.jpg' %(slide_dir, photo.flickr_id)
+ else:
+ filename = '%s/%s.jpg' %(slide_dir, photo.flickr_id)
+
+ flickr_photo = photo.get_original_url()
+ fname = urllib.urlopen(flickr_photo)
+ im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image
+ img = Image.open(im)
+ cur_width, cur_height = img.size
+ #if image landscape
+ if cur_width > cur_height:
+ new_width = max_width
+ #check to make sure we aren't upsizing
+ if cur_width > new_width:
+ ratio = float(new_width)/cur_width
+ x = (cur_width * ratio)
+ y = (cur_height * ratio)
+ resized = img.resize((int(x), int(y)), Image.ANTIALIAS)
+ resized.save(filename, 'JPEG', quality=quality, optimize=True)
+ else:
+ img.save(filename)
+ else:
+ #image portrait
+ new_height = max_height
+ #check to make sure we aren't upsizing
+ if cur_height > new_height:
+ ratio = float(new_height)/cur_height
+ x = (cur_width * ratio)
+ y = (cur_height * ratio)
+ resized = img.resize((int(x), int(y)), Image.ANTIALIAS)
+ resized.save(filename, 'JPEG', quality=quality, optimize=True)
+ else:
+ img.save(filename)
+ photo.slideshowimage_width = photo.get_width
+ photo.slideshowimage_height = photo.get_height
+ photo.slideshowimage_margintop = photo.get_margin_top
+ photo.slideshowimage_marginleft = photo.get_margin_left
+ photo.save()
+ #now resize the local copy
+
+
+
+def make_local_copies(photo):
+ orig_dir = settings.IMAGES_ROOT + '/flickr/full/'+ photo.pub_date.strftime("%Y")
+ if not os.path.isdir(orig_dir):
+ os.makedirs(orig_dir)
+ full = photo.get_original_url()
+ fname = urllib.urlopen(full)
+ im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image
+ img = Image.open(im)
+ local_full = '%s/%s.jpg' %(orig_dir, photo.flickr_id)
+ img.save(local_full)
+ #save large size
+ large_dir = settings.IMAGES_ROOT + '/flickr/large/'+ photo.pub_date.strftime("%Y")
+ if not os.path.isdir(large_dir):
+ os.makedirs(large_dir)
+ large = photo.get_large_url()
+ fname = urllib.urlopen(large)
+ im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image
+ img = Image.open(im)
+ local_large = '%s/%s.jpg' %(large_dir, photo.flickr_id)
+ if img.format == 'JPEG':
+ img.save(local_large)
+ #save medium size
+ med_dir = settings.IMAGES_ROOT + '/flickr/med/'+ photo.pub_date.strftime("%Y")
+ if not os.path.isdir(med_dir):
+ os.makedirs(med_dir)
+ med = photo.get_medium_url()
+ fname = urllib.urlopen(med)
+ im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image
+ img = Image.open(im)
+ local_med = '%s/%s.jpg' %(med_dir, photo.flickr_id)
+ img.save(local_med)
+
+def make_gallery_thumb(photo,set):
+ crop_dir = settings.IMAGES_ROOT + '/gallery_thumbs/'
+ if not os.path.isdir(crop_dir):
+ os.makedirs(crop_dir)
+ remote = photo.get_original_url()
+ print(remote)
+ fname = urllib.urlopen(remote)
+ im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image
+ img = Image.open(im)
+
+ #calculate crop:
+ cur_width, cur_height = img.size
+ new_width, new_height = 291, 350
+ ratio = max(float(new_width)/cur_width,float(new_height)/cur_height)
+ x = (cur_width * ratio)
+ y = (cur_height * ratio)
+ xd = abs(new_width - x)
+ yd = abs(new_height - y)
+ x_diff = int(xd / 2)
+ y_diff = int(yd / 2)
+ box = (int(x_diff), int(y_diff), int(x_diff+new_width), int(y_diff+new_height))
+
+ #create resized file
+ resized = img.resize((int(x), int(y)), Image.ANTIALIAS).crop(box)
+ # save resized file
+ resized_filename = '%s/%s.jpg' %(crop_dir, set.id)
+ try:
+ if img.format == 'JPEG':
+ resized.save(resized_filename, 'JPEG', quality=95, optimize=True)
+ else:
+ resized.save(resized_filename)
+ except IOError, e:
+ if os.path.isfile(resized_filename):
+ os.unlink(resized_filename)
+ raise e
+ #os.unlink(img)
+
+
+
diff --git a/app/media/static/image-preview.js b/app/media/static/image-preview.js
new file mode 100644
index 0000000..b8fead5
--- /dev/null
+++ b/app/media/static/image-preview.js
@@ -0,0 +1,42 @@
+function build_image_preview () {
+ var url = window.location.href
+ var cur = url.split('/')[6];
+ if (cur) {
+ var container = document.createElement("div");
+ container.className = "form-row field-image";
+ var wrapper = document.createElement("div");
+ var label = document.createElement("label");
+ label.textContent = "Image:";
+ var pwrap = document.createElement("p");
+ var img = document.createElement("img");
+
+ var request = new XMLHttpRequest();
+ request.open('GET', '/photos/luximage/data/admin/preview/'+cur+'/', true);
+ request.onload = function() {
+ if (request.status >= 200 && request.status < 400) {
+ var data = JSON.parse(request.responseText);
+ //console.log(data);
+ img.src = data['url'];
+ } else {
+ console.log("server error");
+ }
+ };
+ request.onerror = function() {
+ console.log("error on request");
+ };
+ request.send();
+ pwrap.appendChild(img);
+ wrapper.appendChild(label);
+ wrapper.appendChild(pwrap);
+ container.appendChild(wrapper);
+ parent = document.getElementById("luximage_form");
+ node = parent.children[1].children[0];
+ node.parentNode.insertBefore(container, node.previousSibling);
+ } else {
+ return;
+ }
+}
+
+document.addEventListener("DOMContentLoaded", function(event) {
+ build_image_preview();
+});
diff --git a/app/media/static/my_styles.css b/app/media/static/my_styles.css
new file mode 100644
index 0000000..d13c8e4
--- /dev/null
+++ b/app/media/static/my_styles.css
@@ -0,0 +1,40 @@
+
+/*o.v.*/
+
+#id_featured_image {
+ /*style the "box" in its minimzed state*/
+ border:1px solid black; width:230px; overflow:hidden;
+ height:300px; overflow-y:scroll;
+ /*animate collapsing the dropdown from open to closed state (v. fast)*/
+}
+#id_featured_image input {
+ /*hide the nasty default radio buttons. like, completely!*/
+ position:absolute;top:0;left:0;opacity:0;
+}
+
+
+#id_featured_image label {
+ /*style the labels to look like dropdown options, kinda*/
+ color: #000;
+ display:block;
+ margin: 2px 2px 2px 10px;
+ height:102px;
+ opacity:.6;
+ background-repeat: no-repeat;
+}
+#id_featured_image:hover label{
+ /*this is how labels render in the "expanded" state. we want to see only the selected radio button in the collapsed menu, and all of them when expanded*/
+}
+#id_featured_image label:hover {
+ opacity:.8;
+}
+#id_featured_image input:checked + label {
+ /*tricky! labels immediately following a checked radio button (with our markup they are semantically related) should be fully opaque regardless of hover, and they should always be visible (i.e. even in the collapsed menu*/
+ opacity:1 !important;
+ display:block;
+ background: #333;
+}
+
+/*pfft, nothing as cool here, just the value trace*/
+#trace {margin:0 0 20px;}
+#id_featured_image li:first-child { display: none;}
diff --git a/app/media/templatetags/__init__.py b/app/media/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/app/media/templatetags/__init__.py
diff --git a/app/media/templatetags/get_image_by_size.py b/app/media/templatetags/get_image_by_size.py
new file mode 100644
index 0000000..c56c44e
--- /dev/null
+++ b/app/media/templatetags/get_image_by_size.py
@@ -0,0 +1,8 @@
+from django import template
+
+register = template.Library()
+
+@register.simple_tag
+def get_image_by_size(obj, *args):
+ method = getattr(obj, "get_image_by_size")
+ return method(*args)
diff --git a/app/media/templatetags/get_image_width.py b/app/media/templatetags/get_image_width.py
new file mode 100644
index 0000000..ac39184
--- /dev/null
+++ b/app/media/templatetags/get_image_width.py
@@ -0,0 +1,9 @@
+from math import floor
+from django import template
+
+register = template.Library()
+
+@register.simple_tag
+def get_image_width(obj, size, *args):
+ ratio = floor(int(size)*100/int(obj.height))/100
+ return floor(ratio*int(obj.height))
diff --git a/app/media/templatetags/get_size_by_name.py b/app/media/templatetags/get_size_by_name.py
new file mode 100644
index 0000000..fc64a61
--- /dev/null
+++ b/app/media/templatetags/get_size_by_name.py
@@ -0,0 +1,8 @@
+from django import template
+
+register = template.Library()
+
+@register.simple_tag
+def get_size_by_name(obj, *args):
+ method = getattr(obj, "get_size_by_name")
+ return method(*args)
diff --git a/app/media/urls.py b/app/media/urls.py
new file mode 100644
index 0000000..6673135
--- /dev/null
+++ b/app/media/urls.py
@@ -0,0 +1,74 @@
+from django.urls import path, re_path
+from django.views.generic.base import RedirectView
+
+from . import views
+
+app_name = "photos"
+
+urlpatterns = [
+ path(
+ r'daily/<int:page>',
+ views.DailyPhotoList.as_view(),
+ name="daily_photo_list"
+ ),
+ path(
+ r'daily/',
+ views.DailyPhotoList.as_view(),
+ {'page': 1},
+ name="daily_photo_list"
+ ),
+ path(
+ r'data/(<str:slug>/',
+ views.photo_json
+ ),
+ re_path(
+ r'data/admin/preview/(?P<pk>\d+)/$',
+ views.photo_preview_json,
+ name="admin_image_preview"
+ ),
+ re_path(
+ r'data/admin/tn/(?P<pk>\d+)/$',
+ views.thumb_preview_json,
+ name="admin_thumb_preview"
+ ),
+ re_path(
+ r'galleries/private/(?P<slug>[-\w]+)$',
+ views.PrivateGallery.as_view(),
+ name="private"
+ ),
+ re_path(
+ r'galleries/private/(?P<page>\d+)/$',
+ views.PrivateGalleryList.as_view(),
+ name="private_list"
+ ),
+ re_path(
+ r'galleries/private/$',
+ RedirectView.as_view(url="/photos/galleries/private/1/", permanent=False)
+ ),
+ re_path(
+ r'galleries/(?P<slug>[-\w]+)$',
+ views.Gallery.as_view(),
+ name="private"
+ ),
+ re_path(
+ r'galleries/(?P<page>\d+)/$',
+ views.GalleryList.as_view(),
+ name="private_list"
+ ),
+ re_path(
+ r'galleries/$',
+ RedirectView.as_view(url="/photos/galleries/1/", permanent=False)
+ ),
+ re_path(
+ r'(?P<page>\d+)/$',
+ views.gallery_list,
+ ),
+ re_path(
+ r'(?P<slug>[-\w]+)/$',
+ RedirectView.as_view(url="/photos/%(slug)s/1/", permanent=False)
+ ),
+ re_path(
+ r'',
+ RedirectView.as_view(url="/photos/1/", permanent=False)
+ ),
+]
diff --git a/app/media/utils.py b/app/media/utils.py
new file mode 100644
index 0000000..84e72f5
--- /dev/null
+++ b/app/media/utils.py
@@ -0,0 +1,28 @@
+import os
+import re
+import subprocess
+
+from django.apps import apps
+from django.conf import settings
+
+from PIL import ImageFile
+from bs4 import BeautifulSoup
+# pip install python-resize-image
+from resizeimage import resizeimage
+
+
+def resize_image(img, width=None, height=None, quality=72, base_path="", filename=""):
+ if width and height:
+ newimg = resizeimage.resize_cover(img, [width, height])
+ if width and not height:
+ newimg = resizeimage.resize_width(img, width)
+ if height and not width:
+ newimg = resizeimage.resize_height(img, height)
+ if not os.path.isdir(base_path):
+ os.makedirs(base_path)
+ path = "%s%s" % (base_path, filename)
+ ImageFile.MAXBLOCK = img.size[0] * img.size[1] * 4
+ newimg.save(path, newimg.format, quality=quality)
+ subprocess.call(["jpegoptim", "%s" % path])
+
+
diff --git a/app/media/views.py b/app/media/views.py
new file mode 100644
index 0000000..915b022
--- /dev/null
+++ b/app/media/views.py
@@ -0,0 +1,130 @@
+import json
+from django.shortcuts import render_to_response, render
+from django.template import RequestContext
+from django.http import Http404, HttpResponse
+from django.core import serializers
+
+from .models import Photo, PhotoGallery, LuxGallery, LuxImage
+from locations.models import Country, Region
+
+from utils.views import PaginatedListView
+from django.views.generic import ListView
+from django.views.generic.detail import DetailView
+
+
+class PrivateGallery(DetailView):
+ model = LuxGallery
+ slug_field = "slug"
+ template_name = "details/photo_gallery.html"
+
+
+class PrivateGalleryList(PaginatedListView):
+ template_name = 'archives/gallery_list.html'
+
+ def get_queryset(self):
+ return LuxGallery.objects.filter(is_public=False)
+
+ def get_context_data(self, **kwargs):
+ # Call the base implementation first to get a context
+ context = super(PrivateGalleryList, self).get_context_data(**kwargs)
+ context['is_private'] = True
+ return context
+
+
+class Gallery(DetailView):
+ model = LuxGallery
+ slug_field = "slug"
+ template_name = "details/photo_gallery.html"
+
+
+class GalleryList(PaginatedListView):
+ template_name = 'archives/gallery_list.html'
+
+ def get_queryset(self):
+ return LuxGallery.objects.filter(is_public=True)
+
+ def get_context_data(self, **kwargs):
+ # Call the base implementation first to get a context
+ context = super(GalleryList, self).get_context_data(**kwargs)
+ context['is_private'] = False
+ return context
+
+
+class OldGalleryList(PaginatedListView):
+ template_name = 'archives/gallery_list.html'
+ model = PhotoGallery
+
+ def get_queryset(self):
+ return PhotoGallery.objects.filter(is_public=True)
+
+ def get_context_data(self, **kwargs):
+ # Call the base implementation first to get a context
+ context = super(OldGalleryList, self).get_context_data(**kwargs)
+ return context
+
+
+class DailyPhotoList(PaginatedListView):
+ template_name = 'archives/photo_daily_list.html'
+
+ def get_queryset(self):
+ return LuxImage.objects.filter(is_public=True, title__startswith="daily_")
+
+
+def gallery_list(request, page):
+ request.page_url = '/photos/%d/'
+ request.page = int(page)
+ context = {
+ 'object_list': PhotoGallery.objects.all(),
+ 'page': page,
+ }
+ return render(request, "archives/photos.html", context)
+
+
+def gallery(request, slug):
+ context = {
+ 'object': PhotoGallery.objects.get(set_slug=slug)
+ }
+ return render_to_response('details/photo_galleries.html', context, context_instance=RequestContext(request))
+
+
+def photo_json(request, slug):
+ p = PhotoGallery.objects.filter(set_slug=slug)
+ return HttpResponse(serializers.serialize('json', p), mimetype='application/json')
+
+
+def photo_preview_json(request, pk):
+ p = LuxImage.objects.get(pk=pk)
+ data = {}
+ data['url'] = p.get_admin_image()
+ data = json.dumps(data)
+ return HttpResponse(data)
+
+
+def thumb_preview_json(request, pk):
+ p = LuxImage.objects.get(pk=pk)
+ data = {}
+ data['url'] = p.get_admin_insert()
+ data = json.dumps(data)
+ return HttpResponse(data)
+
+
+def gallery_list_by_area(request, slug, page):
+ """Grabs entries by region or country"""
+ request.page_url = '/photos/' + slug + '/%d/'
+ request.page = int(page)
+ try:
+ region = Region.objects.get(slug__exact=slug)
+ qs = PhotoGallery.objects.filter(region=region).order_by('-id')
+ except:
+ region = Country.objects.get(slug__exact=slug)
+ qs = PhotoGallery.objects.filter(location__state__country=region).order_by('-id')
+ if not region:
+ raise Http404
+ context = {
+ 'object_list': qs,
+ 'country_list': Country.objects.filter(visited=True),
+ 'region_list': Region.objects.all(),
+ 'region': region,
+ 'page': page
+ }
+ return render_to_response("archives/photos.html", context, context_instance=RequestContext(request))