diff options
author | luxagraf <sng@luxagraf.net> | 2024-12-27 09:47:42 -0600 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2024-12-27 09:47:42 -0600 |
commit | 8c7b0e1abe1983fac38322c3bc01165c4c693d7e (patch) | |
tree | 2f91e32f0dae419fe46f773fe9a11817b809fcf0 /app | |
parent | 05b386315c09c735201566cf8945fc8ff78d2741 (diff) |
jrnl: added a photo essay post type and new url/template/views. also cleared out some old unneeded files
Diffstat (limited to 'app')
-rw-r--r-- | app/media/0002_auto_20201201_2054.py | 46 | ||||
-rw-r--r-- | app/media/0003_auto_20201201_2055.py | 118 | ||||
-rw-r--r-- | app/media/admin.py | 16 | ||||
-rw-r--r-- | app/media/migrations/0009_remove_luximage_exif_aperture_and_more.py | 49 | ||||
-rw-r--r-- | app/media/migrations/0010_luximage_image_type.py | 18 | ||||
-rw-r--r-- | app/media/models.py | 25 | ||||
-rw-r--r-- | app/media/retriever.py | 323 | ||||
-rw-r--r-- | app/media/retriever.py.bak | 314 | ||||
-rw-r--r-- | app/media/static/image-preview.js | 42 | ||||
-rw-r--r-- | app/media/static/my_styles.css | 40 | ||||
-rw-r--r-- | app/media/views.py | 10 | ||||
-rw-r--r-- | app/posts/models.py | 1 | ||||
-rw-r--r-- | app/posts/templates/photo_essay_detail.html | 34 | ||||
-rw-r--r-- | app/posts/urls/photo_essay_urls.py (renamed from app/posts/urls/film_urls.py) | 10 | ||||
-rw-r--r-- | app/posts/views/photo_essay_views.py (renamed from app/posts/views/film_views.py) | 27 |
15 files changed, 153 insertions, 920 deletions
diff --git a/app/media/0002_auto_20201201_2054.py b/app/media/0002_auto_20201201_2054.py deleted file mode 100644 index 843f48b..0000000 --- a/app/media/0002_auto_20201201_2054.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 deleted file mode 100644 index 4aeec12..0000000 --- a/app/media/0003_auto_20201201_2055.py +++ /dev/null @@ -1,118 +0,0 @@ -# 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/admin.py b/app/media/admin.py index 2ccce29..f46475c 100644 --- a/app/media/admin.py +++ b/app/media/admin.py @@ -6,7 +6,7 @@ from django.shortcuts import render from django.contrib.admin import helpers from django.http import HttpResponseRedirect -from utils.widgets import OLAdminBase +from utils.widgets import OLAdminBase, LGEntryForm, ImageRadioSelect @admin.register(LuxImageSize) class LuxImageSizeAdmin(admin.ModelAdmin): @@ -41,7 +41,7 @@ class LuxImageAdmin(OLAdminBase): 'fields': ( ('is_public'), ('photo_credit_source', 'photo_credit_url'), - 'exif_raw', 'exif_aperture', 'exif_make', 'exif_model', 'exif_exposure', 'exif_iso', 'exif_focal_length', 'exif_lens', 'exif_date', 'height', 'width'), + 'height', 'width'), }), ) @@ -53,3 +53,15 @@ class LuxImageAdmin(OLAdminBase): class Media: js = ('image-preview.js', 'next-prev-links.js') + + +@admin.register(LuxGallery) +class LuxGalleryAdmin(admin.ModelAdmin): + form = LGEntryForm + class Meta: + widgets = { + 'images': ImageRadioSelect, + } + autocomplete_fields = ['images'] + list_display = ('title','pub_date', 'thumb') + diff --git a/app/media/migrations/0009_remove_luximage_exif_aperture_and_more.py b/app/media/migrations/0009_remove_luximage_exif_aperture_and_more.py new file mode 100644 index 0000000..631c12c --- /dev/null +++ b/app/media/migrations/0009_remove_luximage_exif_aperture_and_more.py @@ -0,0 +1,49 @@ +# Generated by Django 5.0.4 on 2024-12-25 11:50 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('media', '0008_auto_20201202_1155'), + ] + + operations = [ + migrations.RemoveField( + model_name='luximage', + name='exif_aperture', + ), + migrations.RemoveField( + model_name='luximage', + name='exif_date', + ), + migrations.RemoveField( + model_name='luximage', + name='exif_exposure', + ), + migrations.RemoveField( + model_name='luximage', + name='exif_focal_length', + ), + migrations.RemoveField( + model_name='luximage', + name='exif_iso', + ), + migrations.RemoveField( + model_name='luximage', + name='exif_lens', + ), + migrations.RemoveField( + model_name='luximage', + name='exif_make', + ), + migrations.RemoveField( + model_name='luximage', + name='exif_model', + ), + migrations.RemoveField( + model_name='luximage', + name='exif_raw', + ), + ] diff --git a/app/media/migrations/0010_luximage_image_type.py b/app/media/migrations/0010_luximage_image_type.py new file mode 100644 index 0000000..ed8cad9 --- /dev/null +++ b/app/media/migrations/0010_luximage_image_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-12-25 11:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('media', '0009_remove_luximage_exif_aperture_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='luximage', + name='image_type', + field=models.IntegerField(choices=[(0, 'Photo'), (1, 'Sketch')], default=0), + ), + ] diff --git a/app/media/models.py b/app/media/models.py index d1ef32a..f8ce3b5 100644 --- a/app/media/models.py +++ b/app/media/models.py @@ -22,7 +22,6 @@ from resizeimage.imageexceptions import ImageSizeError from taggit.managers import TaggableManager -from .readexif import readexif from .utils import resize_image from locations.models import Location, CheckIn @@ -78,19 +77,10 @@ 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) + photo_credit_source = models.CharField(null=True, blank=True, max_length=300) + photo_credit_url = models.CharField(null=True, blank=True, max_length=300) 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) @@ -98,6 +88,11 @@ class LuxImage(models.Model): 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) + IMAGE_TYPE = ( + (0, 'Photo'), + (1, 'Sketch'), + ) + image_type = models.IntegerField(choices=IMAGE_TYPE, default=0) class Meta: ordering = ('-pub_date', 'id') @@ -255,9 +250,9 @@ class LuxGallery(models.Model): def get_absolute_url(self): if self.is_public: - return "/photos/galleries/%s" % (self.slug) + return "/image//%s" % (self.slug) else: - return "/photos/galleries/private/%s" % (self.slug) + return None def thumbs(self): lst = [x.image.name for x in self.images.all()] @@ -368,8 +363,6 @@ class LuxAudio(models.Model): @receiver(post_save, sender=LuxImage) def post_save_events(sender, update_fields, created, instance, **kwargs): if created: - if instance.exif_raw == '': - instance = readexif(instance) instance.sizes.add(LuxImageSize.objects.get(slug="tn")) img = Image.open(instance.image.path) instance.height = img.height diff --git a/app/media/retriever.py b/app/media/retriever.py deleted file mode 100644 index f5cae68..0000000 --- a/app/media/retriever.py +++ /dev/null @@ -1,323 +0,0 @@ -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 deleted file mode 100644 index d3c572a..0000000 --- a/app/media/retriever.py.bak +++ /dev/null @@ -1,314 +0,0 @@ -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 deleted file mode 100644 index c829084..0000000 --- a/app/media/static/image-preview.js +++ /dev/null @@ -1,42 +0,0 @@ -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/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 deleted file mode 100644 index d13c8e4..0000000 --- a/app/media/static/my_styles.css +++ /dev/null @@ -1,40 +0,0 @@ - -/*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/views.py b/app/media/views.py index 04ac11c..82c68ba 100644 --- a/app/media/views.py +++ b/app/media/views.py @@ -38,14 +38,20 @@ class Gallery(DetailView): class GalleryList(PaginatedListView): - template_name = 'archives/gallery_list.html' + #template_name = 'luxgallery_list.html' + template_name = 'luxgallery_detail.html' def get_queryset(self): - return LuxGallery.objects.filter(is_public=True) + self.gallery = LuxGallery.objects.get(slug="placeholder") + return self.gallery + #actual return statement for when I have some galleries: + #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['breadcrumbs'] = ('Images',) + context['crumb_url'] = '/media/' context['is_private'] = False return context diff --git a/app/posts/models.py b/app/posts/models.py index 377225f..6179fbe 100644 --- a/app/posts/models.py +++ b/app/posts/models.py @@ -73,6 +73,7 @@ class PostType(models.IntegerChoices): SRC = 3, ('src') JRNL = 4, ('jrnl') FIELD_NOTE = 5, ('field note') + PHOTO_ESSAY = 6, ('photo essay') class PostTopic(models.IntegerChoices): diff --git a/app/posts/templates/photo_essay_detail.html b/app/posts/templates/photo_essay_detail.html new file mode 100644 index 0000000..8195ae7 --- /dev/null +++ b/app/posts/templates/photo_essay_detail.html @@ -0,0 +1,34 @@ +{% extends 'base.html' %} +{%block htmlclass%}class="detail single"{% endblock %} +{% load typogrify_tags %} +{% load html5_datetime %} +{% load pagination_tags %} +{% block pagetitle %} Photos | luxagraf {% endblock %} +{% block metadescription %} Recent Images {% endblock %} +{%block bodyid%}class="photos" id="notes-archive"{%endblock%} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %} + <main role="main"> + <figure class="large-top-image">{%with object=object_list%} + <a href="{{object.get_absolute_url}}" title="{{object.title}}">{%with image=object.featured_image%} + <img style="margin:0;" sizes="(max-width: 960px) 100vw" + srcset="{{image.get_srcset}}" + src="{{image.get_src}}" + alt="{{image.alt}} photographed by {% if image.photo_credit_source %}{{image.photo_credit_source}}{%else%}luxagraf{%endif%}"> + </a> + </figure>{%endwith%} + <div class="folio-wrapper"> + <article class="h-entry hentry content" itemscope itemType="http://schema.org/BlogPosting"> + <header id="header" class="post-header"> + <h1 class="p-name post-title" itemprop="headline">{{object.title|smartypants|safe}}</h1> + <div class="post-dateline"> + <time class="hide dt-published published dt-updated post-date lttr-box" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished"><span>{{object.pub_date|date:"F j, Y"}}</span></time> + <span class="hide" itemprop="author" itemscope itemtype="http://schema.org/Person">by <a class="p-author h-card" href="/about"><span itemprop="name">Scott Gilbertson</span></a></span> + </div> + </header> + <div id="article" class="e-content entry-content post-body" itemprop="articleBody"> + {{object.body_html|safe|smartypants}} + </div> + </div> + </main>{%endwith%} +{% endblock%} diff --git a/app/posts/urls/film_urls.py b/app/posts/urls/photo_essay_urls.py index 872d3f4..e69f07c 100644 --- a/app/posts/urls/film_urls.py +++ b/app/posts/urls/photo_essay_urls.py @@ -1,23 +1,23 @@ from django.urls import path, re_path -from ..views import film_views as views +from ..views import photo_essay_views as views -app_name = "film" +app_name = "photo_essay" urlpatterns = [ path( r'<str:slug>', - views.FilmDetailView.as_view(), + views.PhotoEssayDetailView.as_view(), name="detail" ), path( r'<int:page>/', - views.FilmListView.as_view(), + views.PhotoEssayListView.as_view(), name="list" ), path( r'', - views.FilmListView.as_view(), + views.PhotoEssayListView.as_view(), {'page':1}, name="list" ), diff --git a/app/posts/views/film_views.py b/app/posts/views/photo_essay_views.py index 48bdaa4..4f08910 100644 --- a/app/posts/views/film_views.py +++ b/app/posts/views/photo_essay_views.py @@ -11,44 +11,47 @@ from ..models import Post, PostType from taxonomy.models import Category -class FilmListView(PaginatedListView): +class PhotoEssayListView(PaginatedListView): model = Post - template_name = "posts/film_list.html" + # TODO: change this when I have an actual archive to show + template_name = "photo_essay_detail.html" def get_queryset(self): - queryset = super(FilmListView, self).get_queryset() - return queryset.filter(site__domain='luxagraf.net').filter(post_type__in=[PostType.FILM]).filter(status__exact=1).order_by('-pub_date').prefetch_related('location').prefetch_related('featured_image') + queryset = super(PhotoEssayListView, self).get_queryset() + return queryset.get(slug="dawn") + # real queryset + #return queryset.filter(site__domain='luxagraf.net').filter(post_type__in=[PostType.PHOTO_ESSAY]).filter(status__exact=1).order_by('-pub_date').prefetch_related('location').prefetch_related('featured_image') def get_context_data(self, **kwargs): ''' override for custom breadcrumb path ''' # Call the base implementation first to get a context - context = super(FilmListView, self).get_context_data(**kwargs) - context['breadcrumbs'] = ('Film',) + context = super(PhotoEssayListView, self).get_context_data(**kwargs) + context['breadcrumbs'] = ('Photos',) return context -class FilmDetailView(LuxDetailView): +class PhotoEssayDetailView(LuxDetailView): model = Post slug_field = "slug" def get_queryset(self): - queryset = super(FilmDetailView, self).get_queryset() + queryset = super(PhotoEssayDetailView, self).get_queryset() return queryset.select_related('location').prefetch_related('field_notes') def get_context_data(self, **kwargs): - context = super(FilmDetailView, self).get_context_data(**kwargs) + context = super(PhotoEssayDetailView, self).get_context_data(**kwargs) related = [] for obj in self.object.related.all(): model = apps.get_model(obj.model_name.app_label, obj.model_name.model) related.append(model.objects.get(slug=obj.slug, pub_date=obj.pub_date)) context['related'] = related - context['breadcrumbs'] = ('Range',) - context['crumb_url'] = reverse('range:range-list') + context['breadcrumbs'] = ('Photos',) + context['crumb_url'] = reverse('posts:photo-essay-list') return context def get_template_names(self): obj = self.get_object() - return ["posts/film_detail.html"] + return ["film_detail.html"] |