photo26/photologue/views.py
2022-01-30 11:09:55 +01:00

164 lines
5.6 KiB
Python

# Copyright (C) 2021 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import zipfile
from io import BytesIO
from pathlib import Path
from django.contrib import messages
from django.contrib.auth.mixins import (LoginRequiredMixin,
PermissionRequiredMixin)
from django.core.mail import mail_managers
from django.db import IntegrityError
from django.http import HttpResponse
from django.urls import reverse_lazy
from django.utils.text import slugify
from django.views.generic.dates import ArchiveIndexView, YearArchiveView
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormView
from PIL import Image
from .forms import UploadForm
from .models import Gallery, Photo, Tag
class GalleryDateView(LoginRequiredMixin):
queryset = Gallery.objects.filter(is_public=True)
date_field = 'date_start'
uses_datetime_field = False # Fix related object access
allow_empty = True
class GalleryArchiveIndexView(GalleryDateView, ArchiveIndexView):
pass
class GalleryYearArchiveView(GalleryDateView, YearArchiveView):
make_object_list = True
class PhotoDetailView(LoginRequiredMixin, DetailView):
queryset = Photo.objects.filter(is_public=True)
class TagDetail(LoginRequiredMixin, DetailView):
model = Tag
def get_context_data(self, **kwargs):
"""
Insert the single object into the context dict.
"""
current_tag = self.get_object().slug
context = super().get_context_data(**kwargs)
context['galleries'] = Gallery.objects.filter(is_public=True) \
.filter(tags__slug=current_tag) \
.order_by('-date_start')
return context
class CustomGalleryDetailView(LoginRequiredMixin, DetailView):
"""
Custom gallery detail view to filter on photo owner
"""
queryset = Gallery.objects.filter(is_public=True)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Query with owner to reduce database lag
context['photos'] = self.object.public().select_related('owner')
# List owners
context['owners'] = []
for photo in context['photos']:
if photo.owner not in context['owners']:
context['owners'].append(photo.owner)
# Filter on owner
if 'owner' in self.kwargs:
context['photos'] = context['photos'].filter(owner__id=self.kwargs['owner'])
return context
class GalleryDownload(LoginRequiredMixin, DetailView):
model = Gallery
def get(self, request, *args, **kwargs):
"""
Download a zip file of the gallery on GET request.
"""
# Create zip file with pictures
gallery = self.get_object()
byte_data = BytesIO()
zip_file = zipfile.ZipFile(byte_data, "w")
for photo in gallery.public():
filename = os.path.basename(os.path.normpath(photo.image.path))
zip_file.write(photo.image.path, filename)
zip_file.close()
# Return zip file
response = HttpResponse(byte_data.getvalue(), content_type='application/x-zip-compressed')
response['Content-Disposition'] = f"attachment; filename={gallery.slug}.zip"
return response
class GalleryUpload(PermissionRequiredMixin, FormView):
"""
Form to upload new photos in a gallery
"""
form_class = UploadForm
template_name = "photologue/upload.html"
success_url = reverse_lazy("photologue:pl-gallery-upload")
permission_required = 'photologue.add_gallery'
def form_valid(self, form):
# Upload photos
# We take files from the request to support multiple upload
files = self.request.FILES.getlist('file_field')
gallery = form.get_or_create_gallery()
gallery_year = Path(str(gallery.date_start.year))
gallery_dir = gallery_year / gallery.slug
failed_upload = 0
for photo_file in files:
# Check that we have a valid image
try:
opened = Image.open(photo_file)
opened.verify()
except Exception:
# Pillow doesn't recognize it as an image, skip it
messages.error(self.request, f"{photo_file.name} was not recognized as an image")
failed_upload += 1
continue
title = f"{gallery.title} - {photo_file.name}"
try:
photo = Photo(
title=title,
slug=slugify(title),
owner=self.request.user,
)
photo_name = str(gallery_dir / photo_file.name)
photo.image.save(photo_name, photo_file)
photo.save()
photo.galleries.set([gallery])
except IntegrityError:
messages.error(self.request, f"{photo_file.name} was not uploaded. Maybe the photo was already uploaded.")
failed_upload += 1
# Notify user then managers
if not failed_upload:
messages.success(self.request, "All photos has been successfully uploaded.")
else:
n_success = len(files) - failed_upload
messages.warning(self.request, f"Only {n_success} photos were successfully uploaded !")
gallery_title = form.cleaned_data['gallery'] or form.cleaned_data.get('new_gallery_title', '')
photos = ", ".join(f.name for f in files)
mail_managers(
subject="New photos upload",
message=f"{self.request.user.username} has uploaded in `{gallery_title}`: {photos}",
)
return super().form_valid(form)