reservable rooms API

This commit is contained in:
imperosol 2025-04-22 12:36:15 +02:00
parent 8e0e8b6ffe
commit 46dd863b22
6 changed files with 176 additions and 1 deletions

63
reservation/api.py Normal file
View File

@ -0,0 +1,63 @@
from datetime import timedelta
from typing import Any, Literal
from django.core.exceptions import ValidationError
from ninja import Query
from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.pagination import PageNumberPaginationExtra
from ninja_extra.schemas import PaginatedResponseSchema
from pydantic import FutureDatetime
from api.permissions import HasPerm
from reservation.models import ReservationSlot, Room
from reservation.schemas import (
RoomFilterSchema,
RoomSchema,
SlotFilterSchema,
SlotSchema,
)
@api_controller("/reservation/room")
class ReservableRoomController(ControllerBase):
@route.get(
"",
response=list[RoomSchema],
permissions=[HasPerm("reservation.viem_room")],
url_name="fetch_reservable_rooms",
)
def fetch_rooms(self, filters: Query[RoomFilterSchema]):
return filters.filter(Room.objects.select_related("club"))
@api_controller("/reservation/slot")
class ReservationSlotController(ControllerBase):
@route.get(
"",
response=PaginatedResponseSchema[SlotSchema],
permissions=[HasPerm("reservation.view_reservationslot")],
)
@paginate(PageNumberPaginationExtra)
def fetch_slots(self, filters: Query[SlotFilterSchema]):
return filters.filter(
ReservationSlot.objects.select_related("author").order_by("start_at")
)
@route.patch(
"/reservation/slot/{int:slot_id}",
permissions=[HasPerm("reservation.change_reservationslot")],
response={
200: None,
409: dict[Literal["detail"], dict[str, list[str]]],
422: dict[Literal["detail"], list[dict[str, Any]]],
},
)
def update_slot(self, start: FutureDatetime, duration: timedelta, slot_id: int):
slot = self.get_object_or_exception(ReservationSlot, id=slot_id)
slot.start_at = start
slot.end_at = start + duration
try:
slot.full_clean()
slot.save()
except ValidationError as e:
return self.create_response({"detail": dict(e)}, status_code=409)

46
reservation/schemas.py Normal file
View File

@ -0,0 +1,46 @@
from datetime import datetime
from ninja import FilterSchema, ModelSchema
from pydantic import Field
from club.schemas import ClubSchema
from core.schemas import SimpleUserSchema
from reservation.models import ReservationSlot, Room
class RoomFilterSchema(FilterSchema):
club: set[int] | None = Field(None, q="club_id__in")
class RoomSchema(ModelSchema):
class Meta:
model = Room
fields = ["id", "name", "description", "location"]
club: ClubSchema
url: str
@staticmethod
def resolve_url(obj: Room):
return obj.get_absolute_url()
@staticmethod
def resolve_location(obj: Room):
return obj.get_location_display()
class SlotFilterSchema(FilterSchema):
after: datetime = Field(default=None, q="end_at__gt")
before: datetime = Field(default=None, q="start_at__lt")
room: set[int] | None = None
club: set[int] | None = None
class SlotSchema(ModelSchema):
class Meta:
model = ReservationSlot
fields = ["id", "room", "nb_people", "comment"]
start: datetime = Field(alias="start_at")
end: datetime = Field(alias="end_at")
author: SimpleUserSchema

View File

@ -1 +0,0 @@
# Create your tests here.

View File

View File

@ -0,0 +1,28 @@
import pytest
from model_bakery import baker
from ninja_extra.testing import TestClient
from pytest_django.asserts import assertNumQueries
from reservation.api import ReservableRoomController
from reservation.models import Room
@pytest.mark.django_db
class TestFetchRoom:
def test_fetch_simple(self):
rooms = baker.make(Room, _quantity=3, _bulk_create=True)
response = TestClient(ReservableRoomController).get("")
assert response.json() == [
{
"id": room.id,
"name": room.name,
"description": room.description,
"address": room.address,
"club": {"id": room.club.id, "name": room.club.name},
}
for room in rooms
]
def test_nb_queries(self):
with assertNumQueries(1):
TestClient(ReservableRoomController).get("")

View File

@ -0,0 +1,39 @@
import pytest
from model_bakery import baker
from ninja_extra.testing import TestClient
from pytest_django.asserts import assertNumQueries
from reservation.api import ReservableRoomController, ReservationSlotController
from reservation.models import ReservationSlot
@pytest.mark.django_db
class TestFetchRoom:
def test_fetch_simple(self):
slots = baker.make(ReservationSlot, _quantity=5, _bulk_create=True)
response = TestClient(ReservationSlotController).get("")
assert response.json() == [
{
"id": slot.id,
"room": slot.room_id,
"comment": slot.comment,
"nb_people": slot.nb_people,
"start": slot.start_at.isoformat(timespec="milliseconds").replace(
"+00:00", "Z"
),
"end": slot.end_at.isoformat(timespec="milliseconds").replace(
"+00:00", "Z"
),
"author": {
"id": slot.author.id,
"first_name": slot.author.first_name,
"last_name": slot.author.last_name,
"nick_name": slot.author.nick_name,
},
}
for slot in slots
]
def test_nb_queries(self):
with assertNumQueries(1):
TestClient(ReservableRoomController).get("")