add api endpoint to upload a sas picture

This commit is contained in:
imperosol 2025-03-26 15:06:41 +01:00
parent d1767187e4
commit d821dfa180
4 changed files with 79 additions and 11 deletions

View File

@ -17,6 +17,7 @@ from datetime import date, timedelta
# Image utils
from io import BytesIO
from typing import Final
import PIL
from django.conf import settings
@ -26,6 +27,19 @@ from django.utils.timezone import localdate
from PIL import ExifTags
from PIL.Image import Image, Resampling
RED_PIXEL_PNG: Final[bytes] = (
b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
b"\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53"
b"\xde\x00\x00\x00\x0c\x49\x44\x41\x54\x08\xd7\x63\xf8\xcf\xc0\x00"
b"\x00\x03\x01\x01\x00\x18\xdd\x8d\xb0\x00\x00\x00\x00\x49\x45\x4e"
b"\x44\xae\x42\x60\x82"
)
"""A single red pixel, in PNG format.
Can be used in tests and in dev, when there is a need
to generate a dummy image that is considered valid nonetheless
"""
def get_start_of_semester(today: date | None = None) -> date:
"""Return the date of the start of the semester of the given date.

View File

@ -32,17 +32,10 @@ from django.utils import timezone
from club.models import Club, Membership
from core.models import Group, Page, SithFile, User
from core.utils import RED_PIXEL_PNG
from sas.models import Album, PeoplePictureRelation, Picture
from subscription.models import Subscription
RED_PIXEL_PNG: Final[bytes] = (
b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
b"\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53"
b"\xde\x00\x00\x00\x0c\x49\x44\x41\x54\x08\xd7\x63\xf8\xcf\xc0\x00"
b"\x00\x03\x01\x01\x00\x18\xdd\x8d\xb0\x00\x00\x00\x00\x49\x45\x4e"
b"\x44\xae\x42\x60\x82"
)
USER_PACK_SIZE: Final[int] = 1000

View File

@ -1,7 +1,9 @@
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db.models import F
from django.urls import reverse
from ninja import Query
from ninja import Body, Query, UploadedFile
from ninja.errors import HttpError
from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.exceptions import NotFound, PermissionDenied
from ninja_extra.pagination import PageNumberPaginationExtra
@ -9,7 +11,13 @@ from ninja_extra.permissions import IsAuthenticated
from ninja_extra.schemas import PaginatedResponseSchema
from pydantic import NonNegativeInt
from core.auth.api_permissions import CanAccessLookup, CanView, IsInGroup, IsRoot
from core.auth.api_permissions import (
CanAccessLookup,
CanEdit,
CanView,
IsInGroup,
IsRoot,
)
from core.models import Notification, User
from sas.models import Album, PeoplePictureRelation, Picture
from sas.schemas import (
@ -92,6 +100,34 @@ class PicturesController(ControllerBase):
.annotate(album=F("parent__name"))
)
@route.post(
"",
permissions=[CanEdit],
response={200: None, 409: dict[str, list[str]]},
url_name="upload_picture",
)
def upload_picture(self, album_id: Body[int], picture: UploadedFile):
album = self.get_object_or_exception(Album, pk=album_id)
user = self.context.request.user
self_moderate = user.has_perm("sas.moderate_sasfile")
new = Picture(
parent=album,
name=picture.name,
file=picture,
owner=user,
is_moderated=self_moderate,
is_folder=False,
mime_type=picture.content_type,
)
if self_moderate:
new.moderator = user
try:
new.generate_thumbnails()
new.full_clean()
new.save()
except ValidationError as e:
raise HttpError(status_code=409, message=str(e)) from e
@route.get(
"/{picture_id}/identified",
permissions=[IsAuthenticated, CanView],

View File

@ -1,13 +1,16 @@
import pytest
from django.conf import settings
from django.core.cache import cache
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import transaction
from django.test import TestCase
from django.test import Client, TestCase
from django.urls import reverse
from model_bakery import baker
from model_bakery.recipe import Recipe
from core.baker_recipes import old_subscriber_user, subscriber_user
from core.models import Group, SithFile, User
from core.utils import RED_PIXEL_PNG
from sas.baker_recipes import picture_recipe
from sas.models import Album, PeoplePictureRelation, Picture, PictureModerationRequest
@ -241,3 +244,25 @@ class TestAlbumSearch(TestSas):
# - 1 for pagination
# - 1 for the actual results
self.client.get(reverse("api:search-album"))
@pytest.mark.django_db
def test_upload_picture(client: Client):
sas = SithFile.objects.get(pk=settings.SITH_SAS_ROOT_DIR_ID)
album = baker.make(Album, is_in_sas=True, parent=sas, name="test album")
user = baker.make(User, is_superuser=True)
client.force_login(user)
img = SimpleUploadedFile(
name="img.png", content=RED_PIXEL_PNG, content_type="image/png"
)
res = client.post(
reverse("api:upload_picture"), {"album_id": album.id, "picture": img}
)
assert res.status_code == 200
picture = Picture.objects.filter(parent_id=album.id).first()
assert picture is not None
assert picture.name == "img.png"
assert picture.owner == user
assert picture.file.name == "SAS/test album/img.png"
assert picture.compressed.name == ".compressed/SAS/test album/img.webp"
assert picture.thumbnail.name == ".thumbnails/SAS/test album/img.webp"