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)