From f1c9959a2e33f6757a1e8b6b64480893c029308b Mon Sep 17 00:00:00 2001 From: petrCher <88943157+petrCher@users.noreply.github.com> Date: Sat, 25 Oct 2025 22:06:25 +0300 Subject: [PATCH 1/7] added filter --- rental_backend/routes/rental_session.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/rental_backend/routes/rental_session.py b/rental_backend/routes/rental_session.py index 25d62df..7a8be5e 100644 --- a/rental_backend/routes/rental_session.py +++ b/rental_backend/routes/rental_session.py @@ -42,6 +42,7 @@ async def check_sessions_expiration(): RentalSession.query(session=db.session) .filter(RentalSession.status == RentStatus.RESERVED) .filter(RentalSession.reservation_ts < datetime.datetime.now(tz=datetime.timezone.utc) - RENTAL_SESSION_EXPIRY) + .filter(RentalSession.is_deleted == False) .all() ) @@ -66,6 +67,7 @@ async def check_sessions_overdue(): RentalSession.query(session=db.session) .filter(RentalSession.status == RentStatus.ACTIVE) .filter(RentalSession.deadline_ts < datetime.datetime.now(tz=datetime.timezone.utc)) + .filter(RentalSession.is_deleted == False) .all() ) for rental_session in rental_session_list: @@ -96,7 +98,7 @@ async def create_rental_session( """ exist_session_item: list[RentalSession] = RentalSession.query(session=db.session).filter( RentalSession.user_id == user.get("id"), RentalSession.item_type_id == item_type_id - ) + ).filter(RentalSession.is_deleted == False) blocking_session = exist_session_item.filter( or_( RentalSession.status == RentStatus.RESERVED, @@ -114,7 +116,7 @@ async def create_rental_session( exist_session_item.filter( or_(RentalSession.status == RentStatus.EXPIRED, RentalSession.status == RentStatus.CANCELED), RentalSession.reservation_ts > cutoff_time, - ) + ).filter(RentalSession.is_deleted == False) .order_by(RentalSession.reservation_ts) .all() ) @@ -302,6 +304,7 @@ async def get_rental_session(session_id: int, user=Depends(UnionAuth(scopes=["re db.session.query(RentalSession) .options(joinedload(RentalSession.strike)) .filter(RentalSession.id == session_id) + .filter(RentalSession.is_deleted == False) .first() ) @@ -346,7 +349,7 @@ async def get_rental_sessions_common( to_show = list(RentStatus) query = db_session.query(RentalSession).options(joinedload(RentalSession.strike)) - query = query.filter(RentalSession.status.in_(to_show)) + query = query.filter(RentalSession.status.in_(to_show)).filter(RentalSession.is_deleted == False) if is_admin: status_to_show = { @@ -496,6 +499,7 @@ async def cancel_rental_session(session_id: int, user=Depends(UnionAuth())): id=session_id, status=RentStatus.CANCELED, end_ts=datetime.datetime.now(tz=datetime.timezone.utc), + is_deleted=True, ) session.item.is_available = True From 38cab2f1aa7106a956f4d04ad31fd1b01f251b2b Mon Sep 17 00:00:00 2001 From: petrCher <88943157+petrCher@users.noreply.github.com> Date: Sat, 25 Oct 2025 22:07:48 +0300 Subject: [PATCH 2/7] addded filter --- rental_backend/routes/rental_session.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rental_backend/routes/rental_session.py b/rental_backend/routes/rental_session.py index 7a8be5e..9282c82 100644 --- a/rental_backend/routes/rental_session.py +++ b/rental_backend/routes/rental_session.py @@ -96,9 +96,11 @@ async def create_rental_session( :raises NoneAvailable: Если нет доступных предметов указанного типа. :raises SessionExists: Если у пользователя уже есть сессия с указанным типом предмета. """ - exist_session_item: list[RentalSession] = RentalSession.query(session=db.session).filter( - RentalSession.user_id == user.get("id"), RentalSession.item_type_id == item_type_id - ).filter(RentalSession.is_deleted == False) + exist_session_item: list[RentalSession] = ( + RentalSession.query(session=db.session) + .filter(RentalSession.user_id == user.get("id"), RentalSession.item_type_id == item_type_id) + .filter(RentalSession.is_deleted == False) + ) blocking_session = exist_session_item.filter( or_( RentalSession.status == RentStatus.RESERVED, @@ -116,7 +118,8 @@ async def create_rental_session( exist_session_item.filter( or_(RentalSession.status == RentStatus.EXPIRED, RentalSession.status == RentStatus.CANCELED), RentalSession.reservation_ts > cutoff_time, - ).filter(RentalSession.is_deleted == False) + ) + .filter(RentalSession.is_deleted == False) .order_by(RentalSession.reservation_ts) .all() ) From 10b3d583118c980b5bfc4fa40e78bd4c165695ad Mon Sep 17 00:00:00 2001 From: petrCher <88943157+petrCher@users.noreply.github.com> Date: Sat, 25 Oct 2025 22:33:45 +0300 Subject: [PATCH 3/7] added endpoint to delete rental session --- rental_backend/routes/rental_session.py | 35 ++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/rental_backend/routes/rental_session.py b/rental_backend/routes/rental_session.py index 9282c82..f27ebfd 100644 --- a/rental_backend/routes/rental_session.py +++ b/rental_backend/routes/rental_session.py @@ -476,6 +476,40 @@ async def get_my_sessions( ) +@rental_session.delete("/{session_id}", response_model=RentalSessionGet) +async def delete_rental_session(session_id: int, user=Depends(UnionAuth(scopes=["rental.session.admin"]))): + """ + Deletes a session. + + Scopes: `["rental.session.admin"]` + + - **session_id**: The ID of the rental session to delete. + + Returns the deleted rental session. + + Raises **ForbiddenAction** if the session is in RESERVED or ACTIVE status. + Raises **ObjectNotFound** if the session does not exist. + """ + session = RentalSession.get(id=session_id, session=db.session) + if not session: + raise ObjectNotFound(RentalSession, session_id) + if session.status == RentStatus.ACTIVE or session.status == RentStatus.RESERVED: + raise ForbiddenAction(RentalSession) + + updated_session = RentalSession.update(session=db.session, id=session_id, is_deleted=True) + session.item.is_available = True + + ActionLogger.log_event( + user_id=user.get("id"), + admin_id=None, + session_id=session.id, + action_type="DELETE_SESSION", + details={"id": session_id}, + ) + + return RentalSessionGet.model_validate(updated_session) + + @rental_session.delete( "/{session_id}/cancel", response_model=RentalSessionGet, dependencies=[Depends(check_sessions_expiration)] ) @@ -502,7 +536,6 @@ async def cancel_rental_session(session_id: int, user=Depends(UnionAuth())): id=session_id, status=RentStatus.CANCELED, end_ts=datetime.datetime.now(tz=datetime.timezone.utc), - is_deleted=True, ) session.item.is_available = True From 96bcde6e884a19466e738ed73be7efd13fe4384f Mon Sep 17 00:00:00 2001 From: petrCher <88943157+petrCher@users.noreply.github.com> Date: Sat, 25 Oct 2025 22:45:04 +0300 Subject: [PATCH 4/7] fix --- rental_backend/routes/rental_session.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/rental_backend/routes/rental_session.py b/rental_backend/routes/rental_session.py index f27ebfd..d4dfe12 100644 --- a/rental_backend/routes/rental_session.py +++ b/rental_backend/routes/rental_session.py @@ -16,6 +16,7 @@ SessionExists, ) from rental_backend.models.db import Item, ItemType, RentalSession, Strike +from rental_backend.schemas.base import StatusResponseModel from rental_backend.schemas.models import ( RentalSessionGet, RentalSessionPatch, @@ -476,7 +477,7 @@ async def get_my_sessions( ) -@rental_session.delete("/{session_id}", response_model=RentalSessionGet) +@rental_session.delete("/{session_id}", response_model=StatusResponseModel) async def delete_rental_session(session_id: int, user=Depends(UnionAuth(scopes=["rental.session.admin"]))): """ Deletes a session. @@ -495,20 +496,11 @@ async def delete_rental_session(session_id: int, user=Depends(UnionAuth(scopes=[ raise ObjectNotFound(RentalSession, session_id) if session.status == RentStatus.ACTIVE or session.status == RentStatus.RESERVED: raise ForbiddenAction(RentalSession) - - updated_session = RentalSession.update(session=db.session, id=session_id, is_deleted=True) - session.item.is_available = True - - ActionLogger.log_event( - user_id=user.get("id"), - admin_id=None, - session_id=session.id, - action_type="DELETE_SESSION", - details={"id": session_id}, + RentalSession.delete(id=session_id, session=db.session) + return StatusResponseModel( + status="Success", message="Rental session has been deleted", ru="Сессия удалена из RentalAPI" ) - return RentalSessionGet.model_validate(updated_session) - @rental_session.delete( "/{session_id}/cancel", response_model=RentalSessionGet, dependencies=[Depends(check_sessions_expiration)] From 73c4f6c47ce13c49d66b84d25cd528e1c111be07 Mon Sep 17 00:00:00 2001 From: petrCher <88943157+petrCher@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:28:11 +0300 Subject: [PATCH 5/7] minor fixes --- rental_backend/routes/rental_session.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/rental_backend/routes/rental_session.py b/rental_backend/routes/rental_session.py index d4dfe12..90c7bb1 100644 --- a/rental_backend/routes/rental_session.py +++ b/rental_backend/routes/rental_session.py @@ -43,7 +43,6 @@ async def check_sessions_expiration(): RentalSession.query(session=db.session) .filter(RentalSession.status == RentStatus.RESERVED) .filter(RentalSession.reservation_ts < datetime.datetime.now(tz=datetime.timezone.utc) - RENTAL_SESSION_EXPIRY) - .filter(RentalSession.is_deleted == False) .all() ) @@ -68,7 +67,6 @@ async def check_sessions_overdue(): RentalSession.query(session=db.session) .filter(RentalSession.status == RentStatus.ACTIVE) .filter(RentalSession.deadline_ts < datetime.datetime.now(tz=datetime.timezone.utc)) - .filter(RentalSession.is_deleted == False) .all() ) for rental_session in rental_session_list: @@ -97,10 +95,8 @@ async def create_rental_session( :raises NoneAvailable: Если нет доступных предметов указанного типа. :raises SessionExists: Если у пользователя уже есть сессия с указанным типом предмета. """ - exist_session_item: list[RentalSession] = ( - RentalSession.query(session=db.session) - .filter(RentalSession.user_id == user.get("id"), RentalSession.item_type_id == item_type_id) - .filter(RentalSession.is_deleted == False) + exist_session_item: list[RentalSession] = RentalSession.query(session=db.session).filter( + RentalSession.user_id == user.get("id"), RentalSession.item_type_id == item_type_id ) blocking_session = exist_session_item.filter( or_( @@ -120,7 +116,6 @@ async def create_rental_session( or_(RentalSession.status == RentStatus.EXPIRED, RentalSession.status == RentStatus.CANCELED), RentalSession.reservation_ts > cutoff_time, ) - .filter(RentalSession.is_deleted == False) .order_by(RentalSession.reservation_ts) .all() ) @@ -308,7 +303,6 @@ async def get_rental_session(session_id: int, user=Depends(UnionAuth(scopes=["re db.session.query(RentalSession) .options(joinedload(RentalSession.strike)) .filter(RentalSession.id == session_id) - .filter(RentalSession.is_deleted == False) .first() ) @@ -353,7 +347,7 @@ async def get_rental_sessions_common( to_show = list(RentStatus) query = db_session.query(RentalSession).options(joinedload(RentalSession.strike)) - query = query.filter(RentalSession.status.in_(to_show)).filter(RentalSession.is_deleted == False) + query = query.filter(RentalSession.status.in_(to_show)) if is_admin: status_to_show = { @@ -492,9 +486,11 @@ async def delete_rental_session(session_id: int, user=Depends(UnionAuth(scopes=[ Raises **ObjectNotFound** if the session does not exist. """ session = RentalSession.get(id=session_id, session=db.session) - if not session: - raise ObjectNotFound(RentalSession, session_id) - if session.status == RentStatus.ACTIVE or session.status == RentStatus.RESERVED: + if ( + session.status == RentStatus.ACTIVE + or session.status == RentStatus.RESERVED + or session.status == RentStatus.OVERDUE + ): raise ForbiddenAction(RentalSession) RentalSession.delete(id=session_id, session=db.session) return StatusResponseModel( From 3a9c8cc633a419fa67b426a66316121a5385a25d Mon Sep 17 00:00:00 2001 From: petrCher <88943157+petrCher@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:24:25 +0300 Subject: [PATCH 6/7] added soft deletes --- rental_backend/routes/item.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/rental_backend/routes/item.py b/rental_backend/routes/item.py index b58e0f0..72031dc 100644 --- a/rental_backend/routes/item.py +++ b/rental_backend/routes/item.py @@ -4,7 +4,7 @@ from rental_backend import settings from rental_backend.exceptions import ObjectNotFound -from rental_backend.models.db import Item, ItemType +from rental_backend.models.db import Item, ItemType, RentalSession, RentStatus, Strike, Event from rental_backend.schemas.base import StatusResponseModel from rental_backend.schemas.models import ItemGet, ItemPost from rental_backend.settings import Settings, get_settings @@ -105,10 +105,19 @@ async def delete_item( Raises **ObjectNotFound** if the item with the specified ID is not found. """ - item = Item.get(id, session=db.session) - if item is None: + rental_sessions = db.session.query(RentalSession).filter(RentalSession.item_id == id) + session = rental_sessions.filter(RentalSession.status.in_([RentStatus.ACTIVE, RentStatus.OVERDUE, RentStatus.RESERVED])).one_or_none() + if session is not None: raise ObjectNotFound(Item, id) Item.delete(id, session=db.session) + for rental_session in rental_sessions: + RentalSession.delete(rental_session.id, session=db.session) + strikes = db.session.query(Strike).filter(Strike.session_id == rental_session.id) + for strike in strikes: + Strike.delete(strike.id, session=db.session) + events = db.session.query(Event).filter(Event.session_id == rental_session.id) + for event in events: + Event.delete(event.id, session=db.session) ActionLogger.log_event( user_id=None, admin_id=user.get('id'), From ad6f38e6cdb1362c902e28165e60c6549a10db5e Mon Sep 17 00:00:00 2001 From: petrCher <88943157+petrCher@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:26:03 +0300 Subject: [PATCH 7/7] added soft deletes --- rental_backend/routes/item.py | 6 ++++-- rental_backend/routes/rental_session.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/rental_backend/routes/item.py b/rental_backend/routes/item.py index 72031dc..7653662 100644 --- a/rental_backend/routes/item.py +++ b/rental_backend/routes/item.py @@ -4,7 +4,7 @@ from rental_backend import settings from rental_backend.exceptions import ObjectNotFound -from rental_backend.models.db import Item, ItemType, RentalSession, RentStatus, Strike, Event +from rental_backend.models.db import Event, Item, ItemType, RentalSession, RentStatus, Strike from rental_backend.schemas.base import StatusResponseModel from rental_backend.schemas.models import ItemGet, ItemPost from rental_backend.settings import Settings, get_settings @@ -106,7 +106,9 @@ async def delete_item( Raises **ObjectNotFound** if the item with the specified ID is not found. """ rental_sessions = db.session.query(RentalSession).filter(RentalSession.item_id == id) - session = rental_sessions.filter(RentalSession.status.in_([RentStatus.ACTIVE, RentStatus.OVERDUE, RentStatus.RESERVED])).one_or_none() + session = rental_sessions.filter( + RentalSession.status.in_([RentStatus.ACTIVE, RentStatus.OVERDUE, RentStatus.RESERVED]) + ).one_or_none() if session is not None: raise ObjectNotFound(Item, id) Item.delete(id, session=db.session) diff --git a/rental_backend/routes/rental_session.py b/rental_backend/routes/rental_session.py index 90c7bb1..14c0810 100644 --- a/rental_backend/routes/rental_session.py +++ b/rental_backend/routes/rental_session.py @@ -494,7 +494,9 @@ async def delete_rental_session(session_id: int, user=Depends(UnionAuth(scopes=[ raise ForbiddenAction(RentalSession) RentalSession.delete(id=session_id, session=db.session) return StatusResponseModel( - status="Success", message="Rental session has been deleted", ru="Сессия удалена из RentalAPI" + status="Success", + message=f"Rental session with id = {session_id} has been deleted", + ru="Сессия удалена из RentalAPI", )