mirror of
https://github.com/MarcZierle/photo-log-backend.git
synced 2025-01-04 05:37:58 +00:00
259 lines
7.8 KiB
Python
Executable File
259 lines
7.8 KiB
Python
Executable File
from rest_framework import generics, views, status
|
|
from rest_framework.response import Response
|
|
from django.shortcuts import get_list_or_404
|
|
from photo_log.models import PhotoGroup, Photo, PhotoLog
|
|
from .serializers import (
|
|
PhotoGroupSerializer,
|
|
PhotosSerializer,
|
|
PhotoSerializer,
|
|
PhotoUpdateSerializer,
|
|
PhotoLogSerializer,
|
|
PhotoLogsSerializer,
|
|
)
|
|
|
|
from .photolog_generator import generate_photolog_from_latex
|
|
from .autocrop.autocrop import autocrop
|
|
|
|
from django.db.models import FileField
|
|
from django.core.files.uploadedfile import InMemoryUploadedFile
|
|
from django.core.files.base import ContentFile
|
|
|
|
from io import BytesIO
|
|
from PIL import Image, ExifTags
|
|
|
|
|
|
class PhotoGroupAPIView(generics.ListAPIView):
|
|
queryset = PhotoGroup.objects.all()
|
|
serializer_class = PhotoGroupSerializer
|
|
|
|
|
|
class PhotosAPIView(generics.ListAPIView):
|
|
|
|
serializer_class = PhotoSerializer
|
|
|
|
def get_queryset(self):
|
|
queryset = Photo.objects.all()
|
|
self.serializer_class = PhotosSerializer
|
|
|
|
photogroup = self.request.query_params.get('photogroup')
|
|
if photogroup is not None:
|
|
queryset = get_list_or_404(queryset, group=photogroup)
|
|
self.serializer_class = PhotoSerializer
|
|
|
|
return queryset
|
|
|
|
|
|
class PhotoAPIView(generics.RetrieveAPIView):
|
|
serializer_class = PhotoSerializer
|
|
queryset = Photo.objects.all()
|
|
|
|
|
|
class AddPhotoAPIView(generics.CreateAPIView):
|
|
queryset = Photo.objects.all()
|
|
serializer_class = PhotoSerializer
|
|
|
|
|
|
class AddPhotoGroupAPIView(generics.CreateAPIView):
|
|
queryset = PhotoGroup.objects.all()
|
|
serializer_class = PhotoGroupSerializer
|
|
|
|
|
|
class PhotoLogAPIView(generics.RetrieveAPIView):
|
|
serializer_class = PhotoLogSerializer
|
|
queryset = PhotoLog.objects.all()
|
|
|
|
|
|
class PhotoLogsAPIView(generics.ListAPIView):
|
|
serializer_class = PhotoLogsSerializer
|
|
queryset = PhotoLog.objects.all()
|
|
|
|
|
|
class AddPhotoLogAPIView(generics.CreateAPIView):
|
|
queryset = PhotoLog.objects.all()
|
|
serializer_class = PhotoLogSerializer
|
|
|
|
|
|
class DestroyPhotoLogAPIView(generics.DestroyAPIView):
|
|
queryset = PhotoLog.objects.all()
|
|
serializer_class = PhotoLogSerializer
|
|
|
|
|
|
class DestroyPhotoAPIView(generics.DestroyAPIView):
|
|
queryset = Photo.objects.all()
|
|
serializer_class = PhotoSerializer
|
|
|
|
|
|
class UpdatePhotoLogAPIView(generics.UpdateAPIView):
|
|
queryset = PhotoLog.objects.all()
|
|
serializer_class = PhotoLogSerializer
|
|
lookup_field = 'pk'
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
instance = self.get_object()
|
|
serializer = self.get_serializer(instance, data=request.data, partial=True)
|
|
|
|
if serializer.is_valid():
|
|
serializer.save()
|
|
return Response({'ok'})
|
|
else:
|
|
return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
class UpdatePhotoAPIView(generics.UpdateAPIView):
|
|
queryset = Photo.objects.all()
|
|
serializer_class = PhotoUpdateSerializer
|
|
lookup_field = 'pk'
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
instance = self.get_object()
|
|
serializer = self.get_serializer(instance, data=request.data, partial=True)
|
|
|
|
if serializer.is_valid():
|
|
serializer.save()
|
|
return Response({'ok'})
|
|
else:
|
|
return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
class GeneratePhotoLogAPIView(generics.RetrieveAPIView):
|
|
|
|
queryset = PhotoLog.objects.all()
|
|
serializer_class = PhotoLogSerializer
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
photolog = self.get_object()
|
|
photolog_data = self.get_serializer(photolog).data
|
|
|
|
if photolog_data:
|
|
title = photolog_data['title']
|
|
date = photolog_data['date']
|
|
render_date = photolog_data['render_date']
|
|
start_slide_image = photolog_data['start_slide_image']
|
|
slides = photolog_data['slides']
|
|
|
|
log_bytes = generate_photolog_from_latex(request, title, date, render_date, start_slide_image, slides)
|
|
|
|
if log_bytes:
|
|
photolog.pdf.save('log.pdf', ContentFile(log_bytes))
|
|
return Response({'pdf': 'https://server.riezel.com%s' % str(photolog.pdf.url)})
|
|
|
|
return Response({"error": "Not Found"}, status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
|
def crop_photo_and_save(id, save=False):
|
|
return "code run with id " + str(id)
|
|
|
|
def save_cropped_pillow_image(photo, pil_img):
|
|
cropped_img_field = photo.cropped_image
|
|
|
|
buffer = BytesIO()
|
|
pil_img.save(fp=buffer, format="PNG")
|
|
pil_img = ContentFile(buffer.getvalue())
|
|
|
|
img_name = "cropped_image.png"
|
|
cropped_img_field.save(img_name, InMemoryUploadedFile(
|
|
pil_img,
|
|
None, # field_name
|
|
img_name, # image name
|
|
'image/png',
|
|
pil_img.tell, # image size
|
|
None
|
|
))
|
|
|
|
def simple_crop_to_bbox(photo):
|
|
img = Image.open(photo.original_image)
|
|
img = rotateByExif(img)
|
|
bbox = photo.bbox_coords
|
|
rotate_angle = photo.rotate
|
|
|
|
if not img:
|
|
return None
|
|
|
|
#if rotate_angle:
|
|
# img = img.rotate(rotate_angle, expand=1)
|
|
|
|
if bbox:
|
|
img = img.crop((
|
|
bbox[0][0],
|
|
bbox[0][1],
|
|
bbox[2][0],
|
|
bbox[2][1]
|
|
))
|
|
|
|
return img
|
|
|
|
|
|
def rotateByExif(img):
|
|
try:
|
|
for orientation in ExifTags.TAGS.keys():
|
|
if ExifTags.TAGS[orientation]=='Orientation':
|
|
break
|
|
|
|
exif = img._getexif()
|
|
|
|
if exif[orientation] == 3:
|
|
img=img.rotate(180, expand=True)
|
|
elif exif[orientation] == 6:
|
|
img=img.rotate(270, expand=True)
|
|
elif exif[orientation] == 8:
|
|
img=img.rotate(90, expand=True)
|
|
|
|
except (AttributeError, KeyError, IndexError):
|
|
# cases: image don't have getexif
|
|
pass
|
|
return img
|
|
|
|
|
|
class AutoCropPhotoAPIView(generics.RetrieveAPIView):
|
|
|
|
queryset = Photo.objects.all()
|
|
serializer_class = PhotoSerializer
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
photo = None
|
|
|
|
photo_data = self.get_serializer(self.get_object()).data
|
|
|
|
if photo_data:
|
|
photo_id = photo_data['id']
|
|
|
|
if photo_id:
|
|
if not 'mode' in request.query_params:
|
|
return Response({'error':'cropping mode not specified (auto, bbox or inters)'}, status=status.HTTP_400_BAD_REQUEST)
|
|
mode = request.query_params.get('mode')
|
|
|
|
if mode == 'bbox':
|
|
photo = Photo.objects.get(id=photo_id)
|
|
cropped_img = simple_crop_to_bbox(photo)
|
|
save_cropped_pillow_image(photo, cropped_img)
|
|
elif mode == 'auto' or mode == 'inters':
|
|
photo = Photo.objects.get(id=photo_id)
|
|
img = Image.open(photo.original_image)
|
|
|
|
img = rotateByExif(img)
|
|
|
|
try:
|
|
cropped_img, _, bbox, intersections = autocrop(img)
|
|
except Exception:
|
|
cropped_img = img
|
|
bbox = None
|
|
intersections = None
|
|
|
|
if mode == 'auto':
|
|
save_cropped_pillow_image(photo, cropped_img)
|
|
|
|
if bbox:
|
|
photo.bbox_coords = bbox
|
|
photo.save()
|
|
|
|
if intersections:
|
|
photo.intersections = intersections
|
|
photo.save()
|
|
else:
|
|
return Response({'error':'invalid cropping mode (auto, bbox or inters)'}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
if photo:
|
|
return self.retrieve(request, *args, **kwargs)
|
|
|
|
return Response({"error": "Not Found"}, status=status.HTTP_404_NOT_FOUND)
|