diff options
Diffstat (limited to 'app/photos/forms.py')
-rw-r--r-- | app/photos/forms.py | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/app/photos/forms.py b/app/photos/forms.py new file mode 100644 index 0000000..126cfaf --- /dev/null +++ b/app/photos/forms.py @@ -0,0 +1,173 @@ +import zipfile +from zipfile import BadZipFile +import logging +import datetime +import os +from io import BytesIO +try: + import Image +except ImportError: + from PIL import Image + +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.contrib import messages +from django.core.files.base import ContentFile +from django.contrib.admin import widgets +from django.utils.safestring import mark_safe + +from photos.models import LuxImage, LuxGallery, LuxImageSize + +logger = logging.getLogger('photos.forms') + + +class GalleryForm(forms.ModelForm): + class Meta: + fields = '__all__' + widgets = { + 'images': forms.SelectMultiple, + } + + def __init__(self, *args, **kwargs): + super(GalleryForm, self).__init__(*args, **kwargs) + self.fields['images'].choices = [(image.id, mark_safe('%sqq%sqq%s' % (image.title, image.get_image_by_size('tn'), image.pk))) for image in LuxImage.objects.all()[:40]] + self.fields['images'].allow_tags = True + + +class ImageChoiceField(forms.ModelMultipleChoiceField): + + def label_from_instance(self, obj): + + return mark_safe('%sqq%sqq%s' % (obj.title, obj.get_image_by_size('tn'), obj.pk)) + + +class FKGalleryForm(forms.ModelForm): + class Meta: + fields = '__all__' + widgets = { + 'image': ImageChoiceField(queryset=LuxImage.objects.all()), + } + + def __init__(self, *args, **kwargs): + super(FKGalleryForm, self).__init__(*args, **kwargs) + self.fields['image'].choices = [(o.id, str(o.image.url)) for o in LuxImage.objects.all()] + self.fields['image'].allow_tags = True + + +class UploadZipForm(forms.Form): + """ + Handles the uploading of a gallery of photos packed in a .zip file + Creates Gallery object, adds photos with all metadata that's available + """ + zip_file = forms.FileField() + title = forms.CharField(label=_('Gallery Title'), max_length=250) + slug = forms.SlugField(label=_('Gallery Slug')) + desc = forms.CharField(label=_('Gallery Caption'), widget=forms.Textarea, required=False) + date = forms.SplitDateTimeField(label=_('Date'), widget=widgets.AdminSplitDateTime) + is_public = forms.BooleanField(label=_('Is public'), initial=True, required=False, help_text=_('Show on site')) + + def clean_zip_file(self): + """Open the zip file a first time, to check that it is a valid zip archive. + We'll open it again in a moment, so we have some duplication, but let's focus + on keeping the code easier to read! + """ + zip_file = self.cleaned_data['zip_file'] + try: + zip = zipfile.ZipFile(zip_file) + except BadZipFile as e: + raise forms.ValidationError(str(e)) + bad_file = zip.testzip() + if bad_file: + zip.close() + raise forms.ValidationError('"%s" in the .zip archive is corrupt.' % bad_file) + zip.close() # Close file in all cases. + return zip_file + + def clean_title(self): + title = self.cleaned_data['title'] + if title and LuxGallery.objects.filter(title=title).exists(): + raise forms.ValidationError(_('A gallery with that title already exists.')) + return title + + def clean(self): + cleaned_data = super(UploadZipForm, self).clean() + if not self['title'].errors: + # If there's already an error in the title, no need to add another + # error related to the same field. + if not cleaned_data.get('title', None) and not cleaned_data['gallery']: + raise forms.ValidationError( + _('Select an existing gallery, or enter a title for a new gallery.')) + return cleaned_data + + def save(self, request=None, zip_file=None): + if not zip_file: + zip_file = self.cleaned_data['zip_file'] + + gallery, created = LuxGallery.objects.get_or_create( + title=self.cleaned_data['title'], + description=self.cleaned_data['desc'], + slug=self.cleaned_data['slug'], + pub_date=self.cleaned_data['date'], + is_public=self.cleaned_data['is_public'] + ) + zipper = zipfile.ZipFile(zip_file) + count = 1 + for filename in sorted(zipper.namelist()): + f, file_extension = os.path.splitext(filename) + logger.debug('Reading file "{0}".'.format(filename)) + if filename.startswith('__') or filename.startswith('.'): + logger.debug('Ignoring file "{0}".'.format(filename)) + continue + if os.path.dirname(filename): + logger.warning('Ignoring file "{0}" as it is in a subfolder; all images should be in the top ' + 'folder of the zip.'.format(filename)) + if request: + messages.warning(request, + _('Ignoring file "{filename}" as it is in a subfolder').format(filename=filename), fail_silently=True) + continue + data = zipper.read(filename) + + if not len(data): + logger.debug('File "{0}" is empty.'.format(filename)) + continue + + fn, file_extension = os.path.splitext(filename) + if file_extension != ".mp4": + # Basic check that we have a valid image. + try: + file = BytesIO(data) + opened = Image.open(file) + opened.verify() + except Exception: + # Pillow (or PIL) doesn't recognize it as an image. + # If a "bad" file is found we just skip it. + # But we do flag this both in the logs and to the user. + logger.error('Could not process file "{0}" in the .zip archive.'.format(filename)) + if request: + messages.warning(request, + _('Could not process file "{0}" in the .zip archive.').format( + filename), + fail_silently=True) + continue + image = LuxImage( + pub_date=datetime.datetime.now() + ) + contentfile = ContentFile(data) + image.image.save(filename, contentfile) + if file_extension != ".mp4": + img = Image.open(image.image.path) + if img.size[0] > img.size[1]: + image.sizes.add(LuxImageSize.objects.get(width=2560)) + image.sizes.add(LuxImageSize.objects.get(width=1170)) + image.sizes.add(LuxImageSize.objects.get(width=720)) + if img.size[1] > img.size[0]: + image.sizes.add(LuxImageSize.objects.get(height=1600)) + image.sizes.add(LuxImageSize.objects.get(height=800)) + image.sizes.add(LuxImageSize.objects.get(height=460)) + image.save() + gallery.images.add(image) + + zipper.close() + + if request: + messages.success(request, _('The photos have been uploaded')) |