Commits

b7w  committed 0359637

Add bulk time edit and update from exif actions for images in profile

  • Participants
  • Parent commits 969acfc

Comments (0)

Files changed (7)

File bviewer/core/models.py

 
 from bviewer.core.files.storage import ImageStorage
 from bviewer.core.exceptions import HttpError, ViewerError
+from bviewer.core.utils import set_time_from_exif
+
 
 logger = logging.getLogger(__name__)
 
     home = models.CharField(max_length=256, blank=True, default='')
 
     cache_size = models.PositiveIntegerField(default=32,
-        validators=[MinValueValidator(CACHE_SIZE_MIN), MaxValueValidator(CACHE_SIZE_MAX)])
+                                             validators=[MinValueValidator(CACHE_SIZE_MIN), MaxValueValidator(CACHE_SIZE_MAX)])
     cache_archive_size = models.PositiveIntegerField(default=256,
-        validators=[MinValueValidator(CACHE_ARCHIVE_SIZE_MIN), MaxValueValidator(CACHE_ARCHIVE_SIZE_MAX)])
+                                                     validators=[MinValueValidator(CACHE_ARCHIVE_SIZE_MIN),
+                                                                 MaxValueValidator(CACHE_ARCHIVE_SIZE_MAX)])
 
     top_gallery = models.ForeignKey('Gallery', related_name='top', null=True, blank=True, on_delete=models.DO_NOTHING)
     about_title = models.CharField(max_length=256, blank=True)
     title = models.CharField(max_length=256)
     user = models.ForeignKey(ProxyUser)
     visibility = models.SmallIntegerField(max_length=1, choices=VISIBILITY_CHOICE, default=VISIBLE,
-        help_text='HIDDEN - not shown on page for anonymous, PRIVATE - available only to the holder')
+                                          help_text='HIDDEN - not shown on page for anonymous, '
+                                                    'PRIVATE - available only to the holder')
     gallery_sorting = models.SmallIntegerField(max_length=1, choices=SORT_CHOICE, default=ASK,
-        help_text='How to sort galleries inside')
+                                               help_text='How to sort galleries inside')
     allow_archiving = models.BooleanField(default=True)
     description = models.TextField(max_length=512, null=True, blank=True)
     thumbnail = models.ForeignKey('Image', null=True, blank=True, related_name='thumbnail', on_delete=models.SET_NULL)
     """
     if created:
         storage = ImageStorage(instance.gallery.user)
-        image_path = storage.get_path(instance.path)
-        if image_path.is_image and image_path.exif.ctime:
-            instance.time = image_path.exif.ctime
-            instance.save()
+        set_time_from_exif(storage, instance, save=True)
 
 
 post_save.connect(update_time_from_exif, sender=Image)

File bviewer/core/utils.py

 import mockredis
 import time
 import logging
-
 from django.conf import settings
 from django.utils.encoding import smart_text, smart_bytes
 from django.utils.functional import wraps
     value = request.GET.get('year', '')
     if len(value) == 4 and value.isdigit():
         return int(value)
-    return default
+    return default
+
+
+def set_time_from_exif(storage, image, save=False):
+    """
+    :type storage: bviewer.core.files.storage.ImageStorage
+    :type image: bviewer.core.model.Image
+    """
+    image_path = storage.get_path(image.path)
+    if image_path.is_image and image_path.exif.ctime:
+        image.time = image_path.exif.ctime
+        if save:
+            image.save()

File bviewer/profile/actions.py

+# -*- coding: utf-8 -*-
+import logging
+from django.contrib import admin
+from django.db.models import F
+from django.shortcuts import redirect, render
+
+from bviewer.core.utils import set_time_from_exif
+from bviewer.core.files.storage import ImageStorage
+from bviewer.profile.forms import BulkTimeUpdateForm
+
+
+logger = logging.getLogger(__name__)
+
+
+def bulk_time_update(model_admin, request, queryset):
+    form = None
+    if 'apply' in request.POST:
+        form = BulkTimeUpdateForm(request.POST)
+        if form.is_valid():
+            method = form.cleaned_data['method']
+            interval = form.cleaned_data['interval']
+            if interval and method == BulkTimeUpdateForm.ADD:
+                queryset.update(time=F('time') + interval)
+            if interval and method == BulkTimeUpdateForm.SUBTRACT:
+                queryset.update(time=F('time') - interval)
+
+            model_admin.message_user(request, '{0} {1}'.format(method.capitalize(), interval))
+            return redirect(request.get_full_path())
+
+    if not form:
+        form = BulkTimeUpdateForm(initial={'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME)})
+    return render(request, 'profile/bulk_time_update.html', {
+        'title': 'Bulk time update',
+        'form': form,
+        'dimensions': BulkTimeUpdateForm.DIMENSIONS,
+    })
+
+
+def update_time_from_exif(model_admin, request, queryset):
+    for image in queryset:
+        storage = ImageStorage(image.gallery.user)
+        set_time_from_exif(storage, image, save=True)

File bviewer/profile/admin.py

 # -*- coding: utf-8 -*-
+import os
 from collections import Counter
-import os
 from django.contrib.admin import AdminSite, ModelAdmin
 from django.contrib.auth.admin import UserAdmin
 from django.core.urlresolvers import reverse
 from bviewer.core.admin import ProxyUserForm
 from bviewer.core.files.storage import ImageStorage
 from bviewer.core.models import Gallery, Image, ProxyUser, Video
+from bviewer.profile.actions import bulk_time_update, update_time_from_exif
 from bviewer.profile.forms import AdminGalleryForm
 
 
 
 class ProfileImageAdmin(ProfileModelAdmin):
     list_select_related = True
+    actions = [bulk_time_update, update_time_from_exif, ]
 
     list_display = ('path', 'file_name', 'gallery_title', 'time', )
     list_filter = ('gallery__title', 'time',)
     ordering = ('-time', 'gallery', )
 
+    readonly_fields = ('image_thumbnail',)
     search_fields = ('gallery__title', 'path',)
 
     def file_name(self, obj):
     def gallery_title(self, obj):
         return obj.gallery.title
 
+    def image_thumbnail(self, obj):
+        url = reverse('core.download', kwargs=dict(size='small', uid=obj.id))
+        return smart_text('<img class="thumbnail" src="{0}">').format(url)
+
+    image_thumbnail.allow_tags = True
+
     def queryset(self, request):
         return super(ProfileImageAdmin, self).queryset(request).filter(gallery__user=request.user)
 

File bviewer/profile/forms.py

 # -*- coding: utf-8 -*-
+import re
+from datetime import timedelta
 from django.core.exceptions import ValidationError
-from django.forms import ModelForm
+from django.forms import Form, ModelForm, ChoiceField, CharField, MultipleHiddenInput
 
 from bviewer.core.models import Gallery, Video
 
 
+class BulkTimeUpdateForm(Form):
+    ADD = 'add'
+    SUBTRACT = 'subtract'
+    CHOICES = (
+        (ADD, 'Add'),
+        (SUBTRACT, 'Subtract')
+    )
+    DIMENSIONS = ('days', 'seconds', 'minutes', 'hours', 'weeks')
+    _selected_action = CharField(widget=MultipleHiddenInput)
+    method = ChoiceField(choices=CHOICES)
+    interval = CharField()
+
+    def clean_interval(self):
+        interval = self.cleaned_data['interval']
+        dimensions_symbol = tuple(i[0] for i in self.DIMENSIONS)
+        if not re.match(r'[\d{0}]'.format(''.join(dimensions_symbol)), interval):
+            raise ValidationError('Wrong format, support only {0}'.format(self.DIMENSIONS))
+        kwargs = {}
+        for symbol, dimension in zip(dimensions_symbol, self.DIMENSIONS):
+            match = re.search(r'\d+{0}'.format(symbol), interval)
+            if match:
+                kwargs[dimension] = int(match.group()[:-1])
+        return timedelta(**kwargs)
+
+
 class AdminGalleryForm(ModelForm):
     def clean_title(self):
         title = self.cleaned_data['title']

File bviewer/profile/templates/profile/bulk_time_update.html

+{% extends "admin/base_site.html" %}
+
+
+{% block content %}
+    <p>Interval dimensions: {{ dimensions|join:', ' }}</p>
+    <p>Example: 10d 4h</p>
+    <form action="" method="post">{% csrf_token %}
+        {{ form }}
+        <input type="hidden" name="action" value="bulk_time_update"/>
+        <input type="submit" name="apply" value="Save"/>
+    </form>
+{% endblock %}

File docs/releases/notes.rst

 
 * Fix bug not safe HTML_EXTRA
 * Fix bug admin gallery title unique integrity error
+* Add bulk time edit and update from exif actions for images in profile
 * Add option to allow/disallow gallery archiving
 * Add disk cache info in user profile