summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/media/0002_auto_20201201_2054.py46
-rw-r--r--app/media/0003_auto_20201201_2055.py118
-rw-r--r--app/media/__init__.py0
-rw-r--r--app/media/admin.py38
-rw-r--r--app/media/build.py48
-rw-r--r--app/media/migrations/0001_initial.py129
-rw-r--r--app/media/migrations/0002_auto_20201201_2122.py46
-rw-r--r--app/media/migrations/0003_auto_20201201_2125.py65
-rw-r--r--app/media/migrations/0004_auto_20201201_2139.py40
-rw-r--r--app/media/migrations/0005_auto_20201201_2141.py56
-rw-r--r--app/media/migrations/0006_auto_20201201_2145.py70
-rw-r--r--app/media/migrations/0007_luximage_sizes_cache.py18
-rw-r--r--app/media/migrations/0008_auto_20201202_1155.py18
-rw-r--r--app/media/migrations/__init__.py0
-rw-r--r--app/media/models.py382
-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.py26
-rw-r--r--app/media/views.py130
-rw-r--r--app/photos/migrations/0020_auto_20201201_2116.py20
-rw-r--r--app/photos/migrations/0021_auto_20201201_2118.py18
-rw-r--r--app/photos/models.py7
31 files changed, 2215 insertions, 2 deletions
diff --git a/app/media/0002_auto_20201201_2054.py b/app/media/0002_auto_20201201_2054.py
new file mode 100644
index 0000000..843f48b
--- /dev/null
+++ b/app/media/0002_auto_20201201_2054.py
@@ -0,0 +1,46 @@
+# Generated by Django 3.1 on 2020-12-01 19:26
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luximagesize (
+ id,
+ name,
+ width,
+ height,
+ quality
+ )
+ SELECT
+ id,
+ name,
+ width,
+ height,
+ quality
+ FROM
+ photos_luximagesize;
+ """, reverse_sql="""
+ INSERT INTO photos_luximagesize (
+ id,
+ name,
+ width,
+ height,
+ quality
+ )
+ SELECT
+ id,
+ name,
+ width,
+ height,
+ quality
+ FROM
+ media_luximagesize;
+ """)
+ ]
diff --git a/app/media/0003_auto_20201201_2055.py b/app/media/0003_auto_20201201_2055.py
new file mode 100644
index 0000000..4aeec12
--- /dev/null
+++ b/app/media/0003_auto_20201201_2055.py
@@ -0,0 +1,118 @@
+# Generated by Django 3.1 on 2020-12-01 20:49
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0002_auto_20201201_2054'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luximage (
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ height,
+ width,
+ is_public,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ point,
+ location,
+ sizes
+ )
+ SELECT
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ height,
+ width,
+ is_public,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ point,
+ location,
+ sizes
+ FROM
+ photos_luximage;
+ """, reverse_sql="""
+ INSERT INTO photos_luximage (
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ height,
+ width,
+ is_public,
+ sizes,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ point,
+ location
+ )
+ SELECT
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ height,
+ width,
+ is_public,
+ sizes,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ point,
+ location
+ FROM
+ media_luximage;
+ """)
+ ]
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..3c5a86a
--- /dev/null
+++ b/app/media/admin.py
@@ -0,0 +1,38 @@
+from django.contrib import admin
+from django import forms
+from django.contrib.gis.admin import OSMGeoAdmin
+from django.conf.urls import url
+from django.utils.translation import ungettext, ugettext_lazy as _
+from .models import LuxImage, LuxGallery, LuxImageSize, LuxVideo
+from django.shortcuts import render
+from django.contrib.admin import helpers
+from django.http import HttpResponseRedirect
+
+
+class LuxImageSizeAdmin(OSMGeoAdmin):
+ list_display = ('name', 'width', 'height', 'quality')
+ pass
+
+
+admin.site.register(LuxImageSize, LuxImageSizeAdmin)
+
+
+@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')
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..4506248
--- /dev/null
+++ b/app/media/migrations/0001_initial.py
@@ -0,0 +1,129 @@
+# Generated by Django 3.1 on 2020-12-01 21:21
+
+import datetime
+import django.contrib.gis.db.models.fields
+from django.db import migrations, models
+import django.db.models.deletion
+import media.models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('locations', '0028_auto_20200308_1152'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='LuxImageSize',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(blank=True, max_length=30, null=True)),
+ ('slug', models.SlugField(blank=True, 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.AutoField(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)),
+ ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)),
+ ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.location')),
+ ],
+ options={
+ 'verbose_name': 'Video',
+ 'verbose_name_plural': 'Videos',
+ 'ordering': ('-pub_date', 'id'),
+ 'get_latest_by': 'pub_date',
+ },
+ ),
+ migrations.CreateModel(
+ name='LuxImage',
+ fields=[
+ ('id', models.AutoField(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)),
+ ('exif_raw', models.TextField(blank=True, null=True)),
+ ('exif_aperture', models.CharField(blank=True, max_length=50, null=True)),
+ ('exif_make', models.CharField(blank=True, max_length=50, null=True)),
+ ('exif_model', models.CharField(blank=True, max_length=50, null=True)),
+ ('exif_exposure', models.CharField(blank=True, max_length=50, null=True)),
+ ('exif_iso', models.CharField(blank=True, max_length=50, null=True)),
+ ('exif_focal_length', models.CharField(blank=True, max_length=50, null=True)),
+ ('exif_lens', models.CharField(blank=True, max_length=50, null=True)),
+ ('exif_date', models.DateTimeField(blank=True, null=True)),
+ ('height', models.CharField(blank=True, max_length=6, null=True)),
+ ('width', models.CharField(blank=True, max_length=6, null=True)),
+ ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)),
+ ('is_public', models.BooleanField(default=True)),
+ ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.location')),
+ ('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.AutoField(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',
+ },
+ ),
+ migrations.CreateModel(
+ name='LuxAudio',
+ fields=[
+ ('id', models.AutoField(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)),
+ ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)),
+ ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.location')),
+ ],
+ options={
+ 'verbose_name': 'Audio',
+ 'verbose_name_plural': 'Audio',
+ 'ordering': ('-pub_date',),
+ 'get_latest_by': 'pub_date',
+ },
+ ),
+ ]
diff --git a/app/media/migrations/0002_auto_20201201_2122.py b/app/media/migrations/0002_auto_20201201_2122.py
new file mode 100644
index 0000000..f9021b6
--- /dev/null
+++ b/app/media/migrations/0002_auto_20201201_2122.py
@@ -0,0 +1,46 @@
+# Generated by Django 3.1 on 2020-12-01 21:22
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luximagesize (
+ id,
+ name,
+ width,
+ height,
+ quality
+ )
+ SELECT
+ id,
+ name,
+ width,
+ height,
+ quality
+ FROM
+ photos_luximagesize;
+ """, reverse_sql="""
+ INSERT INTO photos_luximagesize (
+ id,
+ name,
+ width,
+ height,
+ quality
+ )
+ SELECT
+ id,
+ name,
+ width,
+ height,
+ quality
+ FROM
+ media_luximagesize;
+ """)
+ ]
diff --git a/app/media/migrations/0003_auto_20201201_2125.py b/app/media/migrations/0003_auto_20201201_2125.py
new file mode 100644
index 0000000..498232a
--- /dev/null
+++ b/app/media/migrations/0003_auto_20201201_2125.py
@@ -0,0 +1,65 @@
+# Generated by Django 3.1 on 2020-12-01 21:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0002_auto_20201201_2122'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luximage (
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ height,
+ width,
+ point,
+ location_id,
+ is_public
+ )
+ SELECT
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ height,
+ width,
+ point,
+ location_id,
+ is_public
+ FROM
+ photos_luximage;
+ """),
+ ]
+
diff --git a/app/media/migrations/0004_auto_20201201_2139.py b/app/media/migrations/0004_auto_20201201_2139.py
new file mode 100644
index 0000000..4847ece
--- /dev/null
+++ b/app/media/migrations/0004_auto_20201201_2139.py
@@ -0,0 +1,40 @@
+# Generated by Django 3.1 on 2020-12-01 21:39
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0003_auto_20201201_2125'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luximage_sizes (
+ id,
+ luximage_id,
+ luximagesize_id
+ )
+ SELECT
+ id,
+ luximage_id,
+ luximagesize_id
+ FROM
+ photos_luximage_sizes;
+ """, reverse_sql="""
+ INSERT INTO photos_luximage_sizes (
+ id,
+ luximage_id,
+ luximagesize_id
+ )
+ SELECT
+ id,
+ luximage_id,
+ luximagesize_id
+ FROM
+ media_luximage_sizes;
+ """)
+ ]
+
+
diff --git a/app/media/migrations/0005_auto_20201201_2141.py b/app/media/migrations/0005_auto_20201201_2141.py
new file mode 100644
index 0000000..e211ea2
--- /dev/null
+++ b/app/media/migrations/0005_auto_20201201_2141.py
@@ -0,0 +1,56 @@
+# Generated by Django 3.1 on 2020-12-01 21:41
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0004_auto_20201201_2139'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luxvideo(
+ id,
+ video_mp4,
+ video_webm,
+ video_poster,
+ title,
+ pub_date,
+ youtube_url,
+ vimeo_url
+ )
+ SELECT
+ id,
+ video_mp4,
+ video_webm,
+ video_poster,
+ title,
+ pub_date,
+ youtube_url,
+ vimeo_url
+ FROM
+ photos_luxvideo;
+ """, reverse_sql="""
+ INSERT INTO photos_luxvideos(
+ video_mp4,
+ video_webm,
+ video_poster,
+ title,
+ pub_date,
+ youtube_url,
+ vimeo_url
+ )
+ SELECT
+ video_mp4,
+ video_webm,
+ video_poster,
+ title,
+ pub_date,
+ youtube_url,
+ vimeo_url
+ FROM
+ media_luxvideo;
+ """)
+ ]
diff --git a/app/media/migrations/0006_auto_20201201_2145.py b/app/media/migrations/0006_auto_20201201_2145.py
new file mode 100644
index 0000000..9827416
--- /dev/null
+++ b/app/media/migrations/0006_auto_20201201_2145.py
@@ -0,0 +1,70 @@
+# Generated by Django 3.1 on 2020-12-01 21:45
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0005_auto_20201201_2141'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luxaudio (
+ id,
+ title,
+ subtitle,
+ slug,
+ body_html,
+ body_markdown,
+ pub_date,
+ mp3,
+ ogg,
+ point,
+ location_id
+ )
+ SELECT
+ id,
+ title,
+ subtitle,
+ slug,
+ body_html,
+ body_markdown,
+ pub_date,
+ mp3,
+ ogg,
+ point,
+ location_id
+ FROM
+ recordings_audio;
+ """, reverse_sql="""
+ INSERT INTO recordings_audio (
+ id,
+ title,
+ subtitle,
+ slug,
+ body_html,
+ body_markdown,
+ pub_date,
+ mp3,
+ ogg,
+ point,
+ location_id
+ )
+ SELECT
+ id,
+ title,
+ subtitle,
+ slug,
+ body_html,
+ body_markdown,
+ pub_date,
+ mp3,
+ ogg,
+ point,
+ location_id
+ FROM
+ media_luxaudio;
+ """)
+ ]
diff --git a/app/media/migrations/0007_luximage_sizes_cache.py b/app/media/migrations/0007_luximage_sizes_cache.py
new file mode 100644
index 0000000..944600f
--- /dev/null
+++ b/app/media/migrations/0007_luximage_sizes_cache.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1 on 2020-12-02 08:14
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0006_auto_20201201_2145'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='luximage',
+ name='sizes_cache',
+ field=models.CharField(blank=True, max_length=300, null=True),
+ ),
+ ]
diff --git a/app/media/migrations/0008_auto_20201202_1155.py b/app/media/migrations/0008_auto_20201202_1155.py
new file mode 100644
index 0000000..0e807d5
--- /dev/null
+++ b/app/media/migrations/0008_auto_20201202_1155.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1 on 2020-12-02 11:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0007_luximage_sizes_cache'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='luximage',
+ name='sizes',
+ field=models.ManyToManyField(blank=True, related_name='sizes', to='media.LuxImageSize'),
+ ),
+ ]
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..fb44c9e
--- /dev/null
+++ b/app/media/models.py
@@ -0,0 +1,382 @@
+import os.path
+import io
+import datetime
+from pathlib import Path
+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.db.models.signals import post_save
+from django.dispatch import receiver
+from django.db.models.signals import m2m_changed
+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 resizeimage.imageexceptions import ImageSizeError
+
+from taggit.managers import TaggableManager
+
+from .utils import resize_image
+from locations.models import Location
+
+
+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)
+ slug = models.SlugField(null=True, blank=True)
+ 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)
+ is_public = models.BooleanField(default=True)
+ sizes = models.ManyToManyField(LuxImageSize, blank=True, related_name='sizes')
+ sizes_cache = 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_url_by_size(size.name)
+
+ 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])
+
+ @cached_property
+ def image_name(self):
+ return os.path.basename(self.image.path)[:-4]
+
+ @cached_property
+ def image_ext(self):
+ return self.image.url[-3:]
+
+ @cached_property
+ def get_image_filename(self):
+ return os.path.basename(self.image.path)
+
+ @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.image_name, size.name, self.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.image_name, 'picwide-med', self.image_ext)
+ else:
+ src += "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.image_name, [size.name for size in self.sizes.all()], self.image_ext)
+ return src
+
+ def get_image_url_by_size(self, size="original"):
+ if size == "original":
+ return self.image.url
+ else:
+ luximagesize = LuxImageSize.objects.get(name=size)
+ if luximagesize not in self.get_sizes:
+ print("new size is "+luximagesize.name)
+ self.sizes.add(luximagesize)
+ return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.image_name, luximagesize.slug, self.image_ext)
+
+ def get_image_path_by_size(self, size="original"):
+ if size == "original":
+ return self.image.path
+ else:
+ luximagesize = LuxImageSize.objects.get(name=size)
+ return "%s%s/%s_%s.%s" % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), self.image_name, luximagesize.slug, self.image_ext)
+
+ def get_thumbnail_url(self):
+ return self.get_image_url_by_size("tn")
+
+ def admin_thumbnail(self):
+ return format_html('<a href="%s"><img src="%s"></a>' % (self.get_image_url_by_size(), self.get_image_url_by_size("admin-thumbnail")))
+ admin_thumbnail.short_description = 'Thumbnail'
+
+ @cached_property
+ def get_sizes(self):
+ return self.sizes_cache.split(",")
+
+ @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):
+ created = self.pk is None
+ if created:
+ self.sizes.add(LuxImageSize.objects.get(name="admin-thumbnail"))
+ img = Image.open(self.image.path)
+ self.height = img.height
+ self.width = img.width
+ self.sizes_cache = ",".join(s.slug for s in self.sizes.all())
+ 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)
+ point = models.PointField(blank=True, null=True)
+ location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True, null=True)
+
+ 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):
+ if not self.point:
+ self.point = CheckIn.objects.latest().point
+ try:
+ self.location = Location.objects.filter(geometry__contains=self.point).get()
+ except Location.DoesNotExist:
+ raise forms.ValidationError("There is no location associated with that point, add it: %sadmin/locations/location/add/" % (settings.BASE_URL))
+ 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)
+ point = models.PointField(blank=True, null=True)
+ location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True, null=True)
+
+ 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)
+ if not self.point:
+ self.point = CheckIn.objects.latest().point
+ try:
+ self.location = Location.objects.filter(geometry__contains=self.point).get()
+ except Location.DoesNotExist:
+ raise forms.ValidationError("There is no location associated with that point, add it: %sadmin/locations/location/add/" % (settings.BASE_URL))
+ super(LuxAudio, self).save(*args, **kwargs)
+
+
+
+@receiver(m2m_changed, sender=LuxImage.sizes.through)
+def update_photo_sizes(sender, instance, **kwargs):
+ # update the local cache of sizes
+ instance.sizes_cache = ",".join(s.slug for s in instance.sizes.all())
+ instance.save()
+ for size in instance.sizes.all():
+ # check each size and see if there's an image there already
+ my_file = Path(instance.get_image_path_by_size(size.name))
+ if not my_file.is_file():
+ print("creating new image file")
+ #file doesn't exist, so create it
+ if size.width:
+ img = Image.open(instance.image.path)
+ try:
+ if size.width <= img.width:
+ resize_image(img, size.width, None, size.quality, instance.get_image_path_by_size(size.name))
+ 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, instance.get_image_path_by_size(size.name))
+ 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)
+ else:
+ # file exists, might add something here to force it to do the above when I want
+ print("file %s exists" % size)
+ pass
+
+
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..a0a62f0
--- /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_url_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..893663c
--- /dev/null
+++ b/app/media/utils.py
@@ -0,0 +1,26 @@
+import os
+import subprocess
+
+from PIL import ImageFile
+# pip install python-resize-image
+from resizeimage import resizeimage
+
+
+def resize_image(img, width=None, height=None, quality=72, filepath=""):
+ """
+ given an image object, size, and filepath
+ resize the image, then save it , size, and filepath
+ resize the image, then save it at the filepath
+ """
+ base_path = os.path.dirname(filepath)
+ if not os.path.isdir(base_path):
+ os.makedirs(base_path)
+ 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)
+ ImageFile.MAXBLOCK = img.size[0] * img.size[1] * 4
+ newimg.save(filepath, newimg.format, quality=quality)
+ subprocess.call(["jpegoptim", "%s" % filepath])
diff --git a/app/media/views.py b/app/media/views.py
new file mode 100644
index 0000000..fdd3c22
--- /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_image_url_by_size('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))
diff --git a/app/photos/migrations/0020_auto_20201201_2116.py b/app/photos/migrations/0020_auto_20201201_2116.py
new file mode 100644
index 0000000..39d4fd9
--- /dev/null
+++ b/app/photos/migrations/0020_auto_20201201_2116.py
@@ -0,0 +1,20 @@
+# Generated by Django 3.1 on 2020-12-01 21:16
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('locations', '0028_auto_20200308_1152'),
+ ('photos', '0019_auto_20190704_0903'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='luximage',
+ name='location',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='locations.location'),
+ ),
+ ]
diff --git a/app/photos/migrations/0021_auto_20201201_2118.py b/app/photos/migrations/0021_auto_20201201_2118.py
new file mode 100644
index 0000000..25e4f8f
--- /dev/null
+++ b/app/photos/migrations/0021_auto_20201201_2118.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1 on 2020-12-01 21:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('photos', '0020_auto_20201201_2116'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='luximage',
+ name='sizes',
+ field=models.ManyToManyField(blank=True, related_name='_luximage_sizes_+', to='photos.LuxImageSize'),
+ ),
+ ]
diff --git a/app/photos/models.py b/app/photos/models.py
index 6253336..24a218f 100644
--- a/app/photos/models.py
+++ b/app/photos/models.py
@@ -70,9 +70,9 @@ class LuxImage(models.Model):
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)
+ 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)
+ 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)
@@ -110,6 +110,9 @@ class LuxImage(models.Model):
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:]