diff --git a/program/models.py b/program/models.py index 5217837d807e0d84fc32a995edfd52ea1771e55b..fc926cbfc9002d432311d28518b2c696e116789e 100644 --- a/program/models.py +++ b/program/models.py @@ -145,6 +145,18 @@ class Image(models.Model): ppoi = PPOIField() width = models.PositiveIntegerField(blank=True, null=True) + def render(self, width: int | None = None, height: int | None = None): + if width is None and height is None: + return self.image.url + elif width and height: + return self.image.crop[f"{width}x{height}"].url + aspect_ratio = self.width / self.height + if width is None: + width = int(height * aspect_ratio) + if height is None: + height = int(width / aspect_ratio) + return self.image.thumbnail[f"{width}x{height}"].url + def save(self, *args, **kwargs): super().save(*args, **kwargs) diff --git a/program/serializers.py b/program/serializers.py index 97d8df0ac9ccec24787555e23d319dfcb24e2024..131303e8ae6cc826177e724a2b093aa2e7adbb53 100644 --- a/program/serializers.py +++ b/program/serializers.py @@ -342,6 +342,11 @@ class ImageSerializer(serializers.ModelSerializer): return instance +class ImageRenderSerializer(serializers.Serializer): + width = serializers.IntegerField(required=False, min_value=1) + height = serializers.IntegerField(required=False, min_value=1) + + class HostSerializer(serializers.ModelSerializer): image_id = serializers.PrimaryKeyRelatedField( allow_null=True, queryset=Image.objects.all(), required=False, source="image" diff --git a/program/views.py b/program/views.py index 23439554013f33c15a7a653e818565d980f53975..6caeb13e3ca0792d383112d2589a387cb44a4db8 100644 --- a/program/views.py +++ b/program/views.py @@ -25,15 +25,23 @@ from itertools import pairwise from textwrap import dedent from django_filters.rest_framework import DjangoFilterBackend -from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_view +from drf_spectacular.utils import ( + OpenApiParameter, + OpenApiResponse, + OpenApiTypes, + extend_schema, + extend_schema_view, +) +from rest_framework import decorators from rest_framework import filters as drf_filters from rest_framework import mixins, permissions, status, viewsets from rest_framework.pagination import LimitOffsetPagination from rest_framework.response import Response +from django.conf import settings from django.contrib.auth.models import User from django.db import IntegrityError -from django.http import HttpResponse, JsonResponse +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404 from django.utils import timezone from django.utils.translation import gettext as _ @@ -61,6 +69,7 @@ from program.serializers import ( ErrorSerializer, FundingCategorySerializer, HostSerializer, + ImageRenderSerializer, ImageSerializer, LanguageSerializer, LicenseSerializer, @@ -347,6 +356,33 @@ class APIImageViewSet(viewsets.ModelViewSet): return Response(status=status.HTTP_204_NO_CONTENT) + @extend_schema( + parameters=[ + ImageRenderSerializer, + OpenApiParameter( + name="Location", + type=OpenApiTypes.URI, + location=OpenApiParameter.HEADER, + description="/", + response=[301], + ), + ], + responses={301: None}, + ) + @decorators.action(["GET"], detail=True) + def render(self, *args, **kwargs): + image = self.get_object() + serializer = ImageRenderSerializer(data=self.request.GET) + if serializer.is_valid(): + image_spec = serializer.validated_data + url = image.render( + width=image_spec.get("width", None), + height=image_spec.get("height", None), + ) + return HttpResponseRedirect(settings.SITE_URL + url) + else: + return Response(status=status.HTTP_400_BAD_REQUEST) + @extend_schema_view( create=extend_schema(summary="Create a new show."),