update
This commit is contained in:
BIN
Backend/src/rooms/routes/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
Backend/src/rooms/routes/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Backend/src/rooms/routes/__pycache__/room_routes.cpython-312.pyc
Normal file
BIN
Backend/src/rooms/routes/__pycache__/room_routes.cpython-312.pyc
Normal file
Binary file not shown.
@@ -1,6 +1,7 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Request, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import and_, or_, func
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
from ...shared.config.database import get_db
|
||||
@@ -9,6 +10,8 @@ from ...security.middleware.auth import get_current_user, authorize_roles
|
||||
from ...auth.models.user import User
|
||||
from ..models.room import Room, RoomStatus
|
||||
from ..models.room_type import RoomType
|
||||
from ..schemas.room import CreateRoomRequest, UpdateRoomRequest, BulkDeleteRoomsRequest
|
||||
from ...shared.utils.response_helpers import success_response
|
||||
from ...reviews.models.review import Review, ReviewStatus
|
||||
from ...bookings.models.booking import Booking, BookingStatus
|
||||
from ..services.room_service import get_rooms_with_ratings, get_amenities_list, normalize_images, get_base_url
|
||||
@@ -210,22 +213,44 @@ async def get_room_by_number(room_number: str, request: Request, db: Session=Dep
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post('/', dependencies=[Depends(authorize_roles('admin'))])
|
||||
async def create_room(room_data: dict, request: Request, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
|
||||
async def create_room(room_data: CreateRoomRequest, request: Request, current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)):
|
||||
"""Create a new room with validated input using Pydantic schema."""
|
||||
# Start transaction
|
||||
transaction = db.begin()
|
||||
try:
|
||||
room_type = db.query(RoomType).filter(RoomType.id == room_data.get('room_type_id')).first()
|
||||
# Lock room type to prevent race conditions
|
||||
room_type = db.query(RoomType).filter(RoomType.id == room_data.room_type_id).with_for_update().first()
|
||||
if not room_type:
|
||||
transaction.rollback()
|
||||
raise HTTPException(status_code=404, detail='Room type not found')
|
||||
existing = db.query(Room).filter(Room.room_number == room_data.get('room_number')).first()
|
||||
|
||||
# Check for duplicate room number with locking
|
||||
existing = db.query(Room).filter(Room.room_number == room_data.room_number).with_for_update().first()
|
||||
if existing:
|
||||
transaction.rollback()
|
||||
raise HTTPException(status_code=400, detail='Room number already exists')
|
||||
amenities_value = room_data.get('amenities', [])
|
||||
if amenities_value is None:
|
||||
amenities_value = []
|
||||
elif not isinstance(amenities_value, list):
|
||||
amenities_value = []
|
||||
room = Room(room_type_id=room_data.get('room_type_id'), room_number=room_data.get('room_number'), floor=room_data.get('floor'), status=RoomStatus(room_data.get('status', 'available')), featured=room_data.get('featured', False), price=room_data.get('price', room_type.base_price), description=room_data.get('description'), capacity=room_data.get('capacity'), room_size=room_data.get('room_size'), view=room_data.get('view'), amenities=amenities_value)
|
||||
|
||||
# Use price from request or default to room type base price
|
||||
room_price = room_data.price if room_data.price is not None else float(room_type.base_price) if room_type.base_price else 0.0
|
||||
|
||||
room = Room(
|
||||
room_type_id=room_data.room_type_id,
|
||||
room_number=room_data.room_number,
|
||||
floor=room_data.floor,
|
||||
status=RoomStatus(room_data.status),
|
||||
featured=room_data.featured,
|
||||
price=room_price,
|
||||
description=room_data.description,
|
||||
capacity=room_data.capacity,
|
||||
room_size=room_data.room_size,
|
||||
view=room_data.view,
|
||||
amenities=room_data.amenities or []
|
||||
)
|
||||
db.add(room)
|
||||
db.commit()
|
||||
db.flush()
|
||||
|
||||
# Commit transaction
|
||||
transaction.commit()
|
||||
db.refresh(room)
|
||||
base_url = get_base_url(request)
|
||||
room_dict = {'id': room.id, 'room_type_id': room.room_type_id, 'room_number': room.room_number, 'floor': room.floor, 'status': room.status.value if isinstance(room.status, RoomStatus) else room.status, 'price': float(room.price) if room.price is not None and room.price > 0 else None, 'featured': room.featured, 'description': room.description, 'capacity': room.capacity, 'room_size': room.room_size, 'view': room.view, 'amenities': room.amenities if room.amenities else [], 'created_at': room.created_at.isoformat() if room.created_at else None, 'updated_at': room.updated_at.isoformat() if room.updated_at else None}
|
||||
@@ -235,67 +260,118 @@ async def create_room(room_data: dict, request: Request, current_user: User=Depe
|
||||
room_dict['images'] = []
|
||||
if room.room_type:
|
||||
room_dict['room_type'] = {'id': room.room_type.id, 'name': room.room_type.name, 'description': room.room_type.description, 'base_price': float(room.room_type.base_price) if room.room_type.base_price else 0.0, 'capacity': room.room_type.capacity, 'amenities': room.room_type.amenities if room.room_type.amenities else [], 'images': []}
|
||||
return {'status': 'success', 'message': 'Room created successfully', 'data': {'room': room_dict}}
|
||||
return success_response(data={'room': room_dict}, message='Room created successfully')
|
||||
except HTTPException:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
raise
|
||||
except IntegrityError as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
logger.error(f'Database integrity error during room creation: {str(e)}')
|
||||
raise HTTPException(status_code=409, detail='Room conflict detected. Please check room number.')
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
logger.error(f'Error creating room: {str(e)}', exc_info=True)
|
||||
raise HTTPException(status_code=500, detail='An error occurred while creating the room')
|
||||
|
||||
@router.put('/{id}', dependencies=[Depends(authorize_roles('admin'))])
|
||||
async def update_room(id: int, room_data: dict, request: Request, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
|
||||
async def update_room(id: int, room_data: UpdateRoomRequest, request: Request, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
|
||||
"""Update a room with validated input using Pydantic schema."""
|
||||
# Start transaction
|
||||
transaction = db.begin()
|
||||
try:
|
||||
room = db.query(Room).filter(Room.id == id).first()
|
||||
# Lock room row to prevent race conditions
|
||||
room = db.query(Room).filter(Room.id == id).with_for_update().first()
|
||||
if not room:
|
||||
transaction.rollback()
|
||||
raise HTTPException(status_code=404, detail='Room not found')
|
||||
if room_data.get('room_type_id'):
|
||||
room_type = db.query(RoomType).filter(RoomType.id == room_data['room_type_id']).first()
|
||||
|
||||
if room_data.room_type_id:
|
||||
room_type = db.query(RoomType).filter(RoomType.id == room_data.room_type_id).first()
|
||||
if not room_type:
|
||||
transaction.rollback()
|
||||
raise HTTPException(status_code=404, detail='Room type not found')
|
||||
if 'room_type_id' in room_data:
|
||||
room.room_type_id = room_data['room_type_id']
|
||||
if 'room_number' in room_data:
|
||||
room.room_number = room_data['room_number']
|
||||
if 'floor' in room_data:
|
||||
room.floor = room_data['floor']
|
||||
if 'status' in room_data:
|
||||
room.status = RoomStatus(room_data['status'])
|
||||
if 'featured' in room_data:
|
||||
room.featured = room_data['featured']
|
||||
if 'price' in room_data:
|
||||
room.price = room_data['price']
|
||||
if 'description' in room_data:
|
||||
room.description = room_data['description']
|
||||
if 'capacity' in room_data:
|
||||
room.capacity = room_data['capacity']
|
||||
if 'room_size' in room_data:
|
||||
room.room_size = room_data['room_size']
|
||||
if 'view' in room_data:
|
||||
room.view = room_data['view']
|
||||
if 'amenities' in room_data:
|
||||
amenities_value = room_data['amenities']
|
||||
if amenities_value is None:
|
||||
room.amenities = []
|
||||
elif isinstance(amenities_value, list):
|
||||
room.amenities = amenities_value
|
||||
else:
|
||||
room.amenities = []
|
||||
db.commit()
|
||||
room.room_type_id = room_data.room_type_id
|
||||
|
||||
if room_data.room_number is not None:
|
||||
# Check for duplicate room number
|
||||
existing = db.query(Room).filter(Room.room_number == room_data.room_number, Room.id != id).first()
|
||||
if existing:
|
||||
transaction.rollback()
|
||||
raise HTTPException(status_code=400, detail='Room number already exists')
|
||||
room.room_number = room_data.room_number
|
||||
|
||||
if room_data.floor is not None:
|
||||
room.floor = room_data.floor
|
||||
if room_data.status is not None:
|
||||
room.status = RoomStatus(room_data.status)
|
||||
if room_data.featured is not None:
|
||||
room.featured = room_data.featured
|
||||
if room_data.price is not None:
|
||||
room.price = room_data.price
|
||||
if room_data.description is not None:
|
||||
room.description = room_data.description
|
||||
if room_data.capacity is not None:
|
||||
room.capacity = room_data.capacity
|
||||
if room_data.room_size is not None:
|
||||
room.room_size = room_data.room_size
|
||||
if room_data.view is not None:
|
||||
room.view = room_data.view
|
||||
if room_data.amenities is not None:
|
||||
room.amenities = room_data.amenities or []
|
||||
|
||||
# Commit transaction
|
||||
transaction.commit()
|
||||
db.refresh(room)
|
||||
|
||||
base_url = get_base_url(request)
|
||||
room_dict = {'id': room.id, 'room_type_id': room.room_type_id, 'room_number': room.room_number, 'floor': room.floor, 'status': room.status.value if isinstance(room.status, RoomStatus) else room.status, 'price': float(room.price) if room.price is not None and room.price > 0 else None, 'featured': room.featured, 'description': room.description, 'capacity': room.capacity, 'room_size': room.room_size, 'view': room.view, 'amenities': room.amenities if room.amenities else [], 'created_at': room.created_at.isoformat() if room.created_at else None, 'updated_at': room.updated_at.isoformat() if room.updated_at else None}
|
||||
room_dict = {
|
||||
'id': room.id,
|
||||
'room_type_id': room.room_type_id,
|
||||
'room_number': room.room_number,
|
||||
'floor': room.floor,
|
||||
'status': room.status.value if isinstance(room.status, RoomStatus) else room.status,
|
||||
'price': float(room.price) if room.price is not None and room.price > 0 else None,
|
||||
'featured': room.featured,
|
||||
'description': room.description,
|
||||
'capacity': room.capacity,
|
||||
'room_size': room.room_size,
|
||||
'view': room.view,
|
||||
'amenities': room.amenities if room.amenities else [],
|
||||
'created_at': room.created_at.isoformat() if room.created_at else None,
|
||||
'updated_at': room.updated_at.isoformat() if room.updated_at else None
|
||||
}
|
||||
try:
|
||||
room_dict['images'] = normalize_images(room.images, base_url)
|
||||
except:
|
||||
room_dict['images'] = []
|
||||
if room.room_type:
|
||||
room_dict['room_type'] = {'id': room.room_type.id, 'name': room.room_type.name, 'description': room.room_type.description, 'base_price': float(room.room_type.base_price) if room.room_type.base_price else 0.0, 'capacity': room.room_type.capacity, 'amenities': room.room_type.amenities if room.room_type.amenities else [], 'images': []}
|
||||
return {'status': 'success', 'message': 'Room updated successfully', 'data': {'room': room_dict}}
|
||||
room_dict['room_type'] = {
|
||||
'id': room.room_type.id,
|
||||
'name': room.room_type.name,
|
||||
'description': room.room_type.description,
|
||||
'base_price': float(room.room_type.base_price) if room.room_type.base_price else 0.0,
|
||||
'capacity': room.room_type.capacity,
|
||||
'amenities': room.room_type.amenities if room.room_type.amenities else [],
|
||||
'images': []
|
||||
}
|
||||
return success_response(data={'room': room_dict}, message='Room updated successfully')
|
||||
except HTTPException:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
raise
|
||||
except IntegrityError as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
logger.error(f'Database integrity error during room update: {str(e)}')
|
||||
raise HTTPException(status_code=409, detail='Room conflict detected. Please check room number.')
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
logger.error(f'Error updating room: {str(e)}', exc_info=True)
|
||||
raise HTTPException(status_code=500, detail='An error occurred while updating the room')
|
||||
|
||||
@router.delete('/{id}', dependencies=[Depends(authorize_roles('admin'))])
|
||||
async def delete_room(id: int, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
|
||||
@@ -313,30 +389,44 @@ async def delete_room(id: int, current_user: User=Depends(authorize_roles('admin
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post('/bulk-delete', dependencies=[Depends(authorize_roles('admin'))])
|
||||
async def bulk_delete_rooms(room_ids: dict, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
|
||||
async def bulk_delete_rooms(room_ids: BulkDeleteRoomsRequest, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
|
||||
"""Bulk delete rooms with validated input using Pydantic schema."""
|
||||
# Start transaction
|
||||
transaction = db.begin()
|
||||
try:
|
||||
ids = room_ids.get('ids', [])
|
||||
if not ids or not isinstance(ids, list):
|
||||
raise HTTPException(status_code=400, detail='Invalid room IDs provided')
|
||||
if len(ids) == 0:
|
||||
raise HTTPException(status_code=400, detail='No room IDs provided')
|
||||
try:
|
||||
ids = [int(id) for id in ids]
|
||||
except (ValueError, TypeError):
|
||||
raise HTTPException(status_code=400, detail='All room IDs must be integers')
|
||||
ids = room_ids.room_ids
|
||||
|
||||
# Check if rooms exist
|
||||
rooms = db.query(Room).filter(Room.id.in_(ids)).all()
|
||||
found_ids = [room.id for room in rooms]
|
||||
not_found_ids = [id for id in ids if id not in found_ids]
|
||||
if not_found_ids:
|
||||
transaction.rollback()
|
||||
raise HTTPException(status_code=404, detail=f'Rooms with IDs {not_found_ids} not found')
|
||||
|
||||
# Delete rooms
|
||||
deleted_count = db.query(Room).filter(Room.id.in_(ids)).delete(synchronize_session=False)
|
||||
db.commit()
|
||||
return {'status': 'success', 'message': f'Successfully deleted {deleted_count} room(s)', 'data': {'deleted_count': deleted_count, 'deleted_ids': ids}}
|
||||
|
||||
# Commit transaction
|
||||
transaction.commit()
|
||||
return success_response(
|
||||
data={'deleted_count': deleted_count, 'deleted_ids': ids},
|
||||
message=f'Successfully deleted {deleted_count} room(s)'
|
||||
)
|
||||
except HTTPException:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
raise
|
||||
except IntegrityError as e:
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
logger.error(f'Database integrity error during bulk room deletion: {str(e)}')
|
||||
raise HTTPException(status_code=409, detail='Cannot delete rooms due to existing relationships (bookings, etc.)')
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
if 'transaction' in locals():
|
||||
transaction.rollback()
|
||||
logger.error(f'Error bulk deleting rooms: {str(e)}', exc_info=True)
|
||||
raise HTTPException(status_code=500, detail='An error occurred while deleting rooms')
|
||||
|
||||
@router.post('/{id}/images', dependencies=[Depends(authorize_roles('admin', 'staff'))])
|
||||
async def upload_room_images(id: int, images: List[UploadFile]=File(...), current_user: User=Depends(authorize_roles('admin', 'staff')), db: Session=Depends(get_db)):
|
||||
|
||||
Reference in New Issue
Block a user