From 3ebd4b1a0b1f7c290923f49080a9360670312dd6 Mon Sep 17 00:00:00 2001 From: imperosol Date: Tue, 22 Apr 2025 12:36:15 +0200 Subject: [PATCH] reservable rooms API --- reservation/api.py | 37 ++++++++++++++++++++++++++++ reservation/schemas.py | 37 ++++++++++++++++++++++++++++ reservation/tests.py | 1 - reservation/tests/__init__.py | 0 reservation/tests/test_room_api.py | 28 +++++++++++++++++++++ reservation/tests/test_slot_api.py | 39 ++++++++++++++++++++++++++++++ 6 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 reservation/api.py create mode 100644 reservation/schemas.py delete mode 100644 reservation/tests.py create mode 100644 reservation/tests/__init__.py create mode 100644 reservation/tests/test_room_api.py create mode 100644 reservation/tests/test_slot_api.py diff --git a/reservation/api.py b/reservation/api.py new file mode 100644 index 00000000..3c3d8925 --- /dev/null +++ b/reservation/api.py @@ -0,0 +1,37 @@ +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 core.auth.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")) diff --git a/reservation/schemas.py b/reservation/schemas.py new file mode 100644 index 00000000..448325cd --- /dev/null +++ b/reservation/schemas.py @@ -0,0 +1,37 @@ +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", "address"] + + club: ClubSchema + + +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 diff --git a/reservation/tests.py b/reservation/tests.py deleted file mode 100644 index a39b155a..00000000 --- a/reservation/tests.py +++ /dev/null @@ -1 +0,0 @@ -# Create your tests here. diff --git a/reservation/tests/__init__.py b/reservation/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/reservation/tests/test_room_api.py b/reservation/tests/test_room_api.py new file mode 100644 index 00000000..4465bb14 --- /dev/null +++ b/reservation/tests/test_room_api.py @@ -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("") diff --git a/reservation/tests/test_slot_api.py b/reservation/tests/test_slot_api.py new file mode 100644 index 00000000..cfbff898 --- /dev/null +++ b/reservation/tests/test_slot_api.py @@ -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("")