diff --git a/.gitignore b/.gitignore index b619abf8e..9991b423d 100644 --- a/.gitignore +++ b/.gitignore @@ -24,8 +24,5 @@ firebase.json # Logs logs/ -# Migrations scripts -migrations/versions/*.py - # Pytest-cov .coverage diff --git a/app/endpoints/booking.py b/app/endpoints/booking.py index e4002cf82..ad0c5370f 100644 --- a/app/endpoints/booking.py +++ b/app/endpoints/booking.py @@ -183,14 +183,36 @@ async def get_bookings_for_manager( @router.get( - "/booking/bookings/confirmed", - response_model=list[schemas_booking.BookingReturn], + "/booking/bookings/confirmed/users/me/manage", + response_model=list[schemas_booking.BookingReturnApplicant], status_code=200, tags=[Tags.booking], ) async def get_confirmed_bookings_for_manager( db: AsyncSession = Depends(get_db), user: models_core.CoreUser = Depends(is_user_a_member), +): + """ + Return all confirmed bookings a user can manage. + **The user must be authenticated to use this endpoint** + """ + + user_managers = await cruds_booking.get_user_managers(user=user, db=db) + + bookings = await cruds_booking.get_confirmed_bookings(db=db) + + return [booking for booking in bookings if booking.room.manager in user_managers] + + +@router.get( + "/booking/bookings/confirmed", + response_model=list[schemas_booking.BookingReturnSimpleApplicant], + status_code=200, + tags=[Tags.booking], +) +async def get_confirmed_bookings( + db: AsyncSession = Depends(get_db), + user: models_core.CoreUser = Depends(is_user_a_member), ): """ Return all confirmed bookings. @@ -204,28 +226,22 @@ async def get_confirmed_bookings_for_manager( @router.get( - "/booking/bookings/users/{applicant_id}", + "/booking/bookings/users/me", response_model=list[schemas_booking.BookingReturn], status_code=200, tags=[Tags.booking], ) async def get_applicant_bookings( - applicant_id: str, db: AsyncSession = Depends(get_db), user: models_core.CoreUser = Depends(is_user_a_member), ): """ - Get one user bookings. + Get the user bookings. **Only usable by the user** """ - if user.id == applicant_id: - bookings = await cruds_booking.get_applicant_bookings( - db=db, applicant_id=applicant_id - ) - return bookings - else: - raise HTTPException(status_code=403) + bookings = await cruds_booking.get_applicant_bookings(db=db, applicant_id=user.id) + return bookings @router.post( @@ -344,7 +360,7 @@ async def confirm_booking( else: raise HTTPException( status_code=403, - detail="You are not allowed to delete this booking", + detail="You are not allowed to give a decision to this booking", ) diff --git a/app/models/models_booking.py b/app/models/models_booking.py index 4def91be0..f73ed8cc5 100644 --- a/app/models/models_booking.py +++ b/app/models/models_booking.py @@ -36,6 +36,7 @@ class Booking(Base): reason: Mapped[str] = mapped_column(String, nullable=False) start: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) end: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) + creation: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) note: Mapped[str] = mapped_column(String, nullable=True) room_id: Mapped[str] = mapped_column( ForeignKey("booking_room.id"), nullable=False, index=True diff --git a/app/schemas/schemas_booking.py b/app/schemas/schemas_booking.py index e3462edeb..2b4cde289 100644 --- a/app/schemas/schemas_booking.py +++ b/app/schemas/schemas_booking.py @@ -1,4 +1,4 @@ -"""Schemas file for endpoint /bdebooking""" +"""Schemas file for endpoint /booking""" from datetime import datetime @@ -46,6 +46,7 @@ class BookingBase(BaseModel): reason: str start: datetime end: datetime + creation: datetime note: str | None room_id: str key: bool @@ -72,6 +73,10 @@ class Config: orm_mode = True +class BookingReturnSimpleApplicant(BookingReturn): + applicant: CoreUserSimple + + class BookingReturnApplicant(BookingReturn): applicant: Applicant @@ -81,7 +86,7 @@ class BookingEdit(BaseModel): start: datetime | None = None end: datetime | None = None note: str | None = None - room: str | None = None + room_id: str | None = None key: bool | None = None recurrence_rule: str | None = None entity: str | None = None diff --git a/app/utils/types/module_list.py b/app/utils/types/module_list.py index fa23627d7..0746dc08d 100644 --- a/app/utils/types/module_list.py +++ b/app/utils/types/module_list.py @@ -13,7 +13,7 @@ class ModuleList(Module, Enum): root="amap", default_allowed_groups_ids=[GroupType.student, GroupType.staff], ) - bdebooking = Module( + booking = Module( root="booking", default_allowed_groups_ids=[GroupType.student, GroupType.staff], ) diff --git a/migrations/versions/.gitkeep b/migrations/versions/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/migrations/versions/28aa5ef44bf3_booking_enhancements.py b/migrations/versions/28aa5ef44bf3_booking_enhancements.py new file mode 100644 index 000000000..2cd57ddf6 --- /dev/null +++ b/migrations/versions/28aa5ef44bf3_booking_enhancements.py @@ -0,0 +1,60 @@ +"""booking enhancements + +Revision ID: 28aa5ef44bf3 +Revises: +Create Date: 2023-12-18 00:35:59.678336 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = "28aa5ef44bf3" +down_revision = "f20685c9761e" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # Schema migration + op.add_column( + "booking", + sa.Column( + "creation", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + ) + + # Data migration + t_booking = sa.Table( + "booking", + sa.MetaData(), + sa.Column("id", sa.String()), + sa.Column("start", sa.DateTime()), + sa.Column("end", sa.DateTime()), + sa.Column("recurrence_rule", sa.String()), + ) + + conn = op.get_bind() + res = conn.execute( + sa.select(t_booking.c.id, t_booking.c.start, t_booking.c.end).where( + t_booking.c.recurrence_rule != "" + ) + ).fetchall() + for id_, booking_start, booking_end in res: + endBooking = booking_end.replace( + year=booking_start.year, + month=booking_start.month, + day=booking_start.day, + ) + endBooking = endBooking.astimezone().replace(tzinfo=None) + conn.execute( + t_booking.update().where(t_booking.c.id == id_).values(end=endBooking) + ) + + +def downgrade() -> None: + # Schema migration + op.drop_column("booking", "creation") diff --git a/tests/test_booking.py b/tests/test_booking.py index 3af1cbee5..a8a4981a6 100644 --- a/tests/test_booking.py +++ b/tests/test_booking.py @@ -81,12 +81,16 @@ async def init_objects(): ) await add_object_to_db(room_to_delete) + global booking_id + booking_id = str(uuid.uuid4()) + global booking booking = models_booking.Booking( - id=str(uuid.uuid4()), + id=booking_id, reason="HH", start=datetime.datetime.fromisoformat("2023-09-22T20:00:00"), end=datetime.datetime.fromisoformat("2023-09-22T23:00:00"), + creation=datetime.datetime.fromisoformat("2023-09-10T10:00:00"), room_id=room.id, key=True, decision="approved", @@ -101,6 +105,7 @@ async def init_objects(): reason="Test", start=datetime.datetime.fromisoformat("2023-09-22T20:00:00"), end=datetime.datetime.fromisoformat("2023-09-22T23:00:00"), + creation=datetime.datetime.fromisoformat("2023-09-10T10:00:00"), room_id=room.id, key=True, decision="pending", @@ -171,6 +176,15 @@ def test_get_user_bookings_manage(): def test_get_user_bookings_manage_confirmed(): + response = client.get( + "/booking/bookings/confirmed/users/me/manage", + headers={"Authorization": f"Bearer {token_manager}"}, + ) + assert response.status_code == 200 + assert booking_id in map(lambda booking: booking["id"], response.json()) + + +def test_get_bookings_confirmed(): response = client.get( "/booking/bookings/confirmed", headers={"Authorization": f"Bearer {token_manager}"}, @@ -180,7 +194,7 @@ def test_get_user_bookings_manage_confirmed(): def test_get_user_bookings(): response = client.get( - f"/booking/bookings/users/{simple_user.id}", + "/booking/bookings/users/me", headers={"Authorization": f"Bearer {token_simple}"}, ) assert response.status_code == 200 @@ -193,6 +207,7 @@ def test_post_bookings(): "reason": "Test", "start": "2022-09-15T08:00:00Z", "end": "2022-09-15T09:00:00Z", + "creation": "2022-09-15T07:00:00Z", "note": "do", "room_id": room.id, "key": True,