updates
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -346,10 +346,11 @@ async def get_housekeeping_tasks(
|
||||
date: Optional[str] = Query(None),
|
||||
page: int = Query(1, ge=1),
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
include_cleaning_rooms: bool = Query(True, description='Include rooms in cleaning status even without tasks'),
|
||||
current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get housekeeping tasks with filtering"""
|
||||
"""Get housekeeping tasks with filtering. Also includes rooms in cleaning status."""
|
||||
try:
|
||||
# Check user role - housekeeping and staff users should only see their assigned tasks
|
||||
role = db.query(Role).filter(Role.id == current_user.role_id).first()
|
||||
@@ -359,8 +360,14 @@ async def get_housekeeping_tasks(
|
||||
query = db.query(HousekeepingTask)
|
||||
|
||||
# Filter by assigned_to for housekeeping and staff users (not admin)
|
||||
# But also include unassigned tasks so they can pick them up
|
||||
if is_housekeeping_or_staff:
|
||||
query = query.filter(HousekeepingTask.assigned_to == current_user.id)
|
||||
query = query.filter(
|
||||
or_(
|
||||
HousekeepingTask.assigned_to == current_user.id,
|
||||
HousekeepingTask.assigned_to.is_(None)
|
||||
)
|
||||
)
|
||||
|
||||
if room_id:
|
||||
query = query.filter(HousekeepingTask.room_id == room_id)
|
||||
@@ -379,7 +386,11 @@ async def get_housekeeping_tasks(
|
||||
tasks = query.offset(offset).limit(limit).all()
|
||||
|
||||
result = []
|
||||
task_room_ids = set()
|
||||
|
||||
# Process existing tasks
|
||||
for task in tasks:
|
||||
task_room_ids.add(task.room_id)
|
||||
result.append({
|
||||
'id': task.id,
|
||||
'room_id': task.room_id,
|
||||
@@ -396,9 +407,84 @@ async def get_housekeeping_tasks(
|
||||
'notes': task.notes,
|
||||
'quality_score': task.quality_score,
|
||||
'estimated_duration_minutes': task.estimated_duration_minutes,
|
||||
'actual_duration_minutes': task.actual_duration_minutes
|
||||
'actual_duration_minutes': task.actual_duration_minutes,
|
||||
'room_status': task.room.status.value if task.room else None
|
||||
})
|
||||
|
||||
# Include rooms in cleaning status that don't have tasks (or have unassigned tasks for housekeeping users)
|
||||
if include_cleaning_rooms:
|
||||
rooms_query = db.query(Room).filter(Room.status == RoomStatus.cleaning)
|
||||
|
||||
if room_id:
|
||||
rooms_query = rooms_query.filter(Room.id == room_id)
|
||||
|
||||
# For housekeeping/staff users, also include rooms with unassigned tasks
|
||||
if is_housekeeping_or_staff:
|
||||
# Get room IDs with unassigned tasks
|
||||
unassigned_task_rooms = db.query(HousekeepingTask.room_id).filter(
|
||||
and_(
|
||||
HousekeepingTask.assigned_to.is_(None),
|
||||
HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress])
|
||||
)
|
||||
).distinct().all()
|
||||
unassigned_room_ids = [r[0] for r in unassigned_task_rooms]
|
||||
|
||||
# Include rooms in cleaning status OR rooms with unassigned tasks
|
||||
if unassigned_room_ids:
|
||||
rooms_query = db.query(Room).filter(
|
||||
or_(
|
||||
Room.status == RoomStatus.cleaning,
|
||||
Room.id.in_(unassigned_room_ids)
|
||||
)
|
||||
)
|
||||
if room_id:
|
||||
rooms_query = rooms_query.filter(Room.id == room_id)
|
||||
|
||||
cleaning_rooms = rooms_query.all()
|
||||
|
||||
# Add rooms in cleaning status that don't have tasks in current page results
|
||||
for room in cleaning_rooms:
|
||||
if room.id not in task_room_ids:
|
||||
# Check if there are any pending tasks for this room
|
||||
pending_tasks = db.query(HousekeepingTask).filter(
|
||||
and_(
|
||||
HousekeepingTask.room_id == room.id,
|
||||
HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress])
|
||||
)
|
||||
).all()
|
||||
|
||||
# For housekeeping/staff, only show if there are unassigned tasks or if room is in cleaning
|
||||
if is_housekeeping_or_staff:
|
||||
has_unassigned = any(t.assigned_to is None for t in pending_tasks)
|
||||
if not has_unassigned and room.status != RoomStatus.cleaning:
|
||||
continue
|
||||
|
||||
# Create a virtual task entry for rooms in cleaning status
|
||||
result.append({
|
||||
'id': None, # No task ID since this is a room status entry
|
||||
'room_id': room.id,
|
||||
'room_number': room.room_number,
|
||||
'booking_id': None,
|
||||
'task_type': 'vacant', # Default task type
|
||||
'status': 'pending',
|
||||
'scheduled_time': datetime.utcnow().isoformat(),
|
||||
'started_at': None,
|
||||
'completed_at': None,
|
||||
'assigned_to': None,
|
||||
'assigned_staff_name': None,
|
||||
'checklist_items': [],
|
||||
'notes': 'Room is in cleaning mode',
|
||||
'quality_score': None,
|
||||
'estimated_duration_minutes': None,
|
||||
'actual_duration_minutes': None,
|
||||
'room_status': room.status.value,
|
||||
'is_room_status_only': True # Flag to indicate this is from room status, not a task
|
||||
})
|
||||
|
||||
# Update total count to include cleaning rooms
|
||||
if include_cleaning_rooms:
|
||||
total = len(result)
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
'data': {
|
||||
@@ -418,11 +504,16 @@ async def get_housekeeping_tasks(
|
||||
@router.post('/housekeeping')
|
||||
async def create_housekeeping_task(
|
||||
task_data: dict,
|
||||
current_user: User = Depends(authorize_roles('admin', 'staff')),
|
||||
current_user: User = Depends(authorize_roles('admin', 'staff', 'housekeeping')),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Create a new housekeeping task"""
|
||||
try:
|
||||
# Check user role - housekeeping users can only assign tasks to themselves
|
||||
role = db.query(Role).filter(Role.id == current_user.role_id).first()
|
||||
is_admin = role and role.name == 'admin'
|
||||
is_housekeeping = role and role.name == 'housekeeping'
|
||||
|
||||
room = db.query(Room).filter(Room.id == task_data.get('room_id')).first()
|
||||
if not room:
|
||||
raise HTTPException(status_code=404, detail='Room not found')
|
||||
@@ -430,6 +521,14 @@ async def create_housekeeping_task(
|
||||
scheduled_time = datetime.fromisoformat(task_data['scheduled_time'].replace('Z', '+00:00'))
|
||||
assigned_to = task_data.get('assigned_to')
|
||||
|
||||
# Housekeeping users can only assign tasks to themselves
|
||||
if is_housekeeping and assigned_to and assigned_to != current_user.id:
|
||||
raise HTTPException(status_code=403, detail='Housekeeping users can only assign tasks to themselves')
|
||||
|
||||
# If housekeeping user doesn't specify assigned_to, assign to themselves
|
||||
if is_housekeeping and not assigned_to:
|
||||
assigned_to = current_user.id
|
||||
|
||||
task = HousekeepingTask(
|
||||
room_id=task_data['room_id'],
|
||||
booking_id=task_data.get('booking_id'),
|
||||
@@ -450,8 +549,7 @@ async def create_housekeeping_task(
|
||||
# Send notification to assigned staff member if task is assigned
|
||||
if assigned_to:
|
||||
try:
|
||||
from ..routes.chat_routes import manager
|
||||
assigned_staff = db.query(User).filter(User.id == assigned_to).first()
|
||||
from ...notifications.routes.notification_routes import notification_manager
|
||||
task_data_notification = {
|
||||
'id': task.id,
|
||||
'room_id': task.room_id,
|
||||
@@ -467,13 +565,9 @@ async def create_housekeeping_task(
|
||||
'data': task_data_notification
|
||||
}
|
||||
# Send notification to the specific staff member
|
||||
if assigned_to in manager.staff_connections:
|
||||
try:
|
||||
await manager.staff_connections[assigned_to].send_json(notification_data)
|
||||
except Exception as e:
|
||||
logger.error(f'Error sending housekeeping task notification to staff {assigned_to}: {str(e)}', exc_info=True, extra={'staff_id': assigned_to})
|
||||
await notification_manager.send_to_user(assigned_to, notification_data)
|
||||
except Exception as e:
|
||||
logger.error(f'Error setting up housekeeping task notification: {str(e)}', exc_info=True)
|
||||
logger.error(f'Error sending housekeeping task notification: {str(e)}', exc_info=True)
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
@@ -504,21 +598,34 @@ async def update_housekeeping_task(
|
||||
is_housekeeping_or_staff = role and role.name in ('housekeeping', 'staff')
|
||||
|
||||
if is_housekeeping_or_staff:
|
||||
# Housekeeping and staff can only update tasks assigned to them
|
||||
if task.assigned_to != current_user.id:
|
||||
# Housekeeping and staff can start unassigned tasks (assign to themselves)
|
||||
if task.assigned_to is None:
|
||||
# Allow housekeeping users to assign unassigned tasks to themselves
|
||||
if 'status' in task_data and task_data['status'] == 'in_progress':
|
||||
task.assigned_to = current_user.id
|
||||
task_data['assigned_to'] = current_user.id
|
||||
elif task.assigned_to != current_user.id:
|
||||
# If task is assigned, only the assigned user can update it
|
||||
raise HTTPException(status_code=403, detail='You can only update tasks assigned to you')
|
||||
# Housekeeping and staff cannot change assignment
|
||||
if 'assigned_to' in task_data and task_data.get('assigned_to') != task.assigned_to:
|
||||
# Housekeeping and staff cannot change assignment of already assigned tasks
|
||||
if 'assigned_to' in task_data and task.assigned_to is not None and task_data.get('assigned_to') != task.assigned_to:
|
||||
raise HTTPException(status_code=403, detail='You cannot change task assignment')
|
||||
|
||||
old_assigned_to = task.assigned_to
|
||||
assigned_to_changed = False
|
||||
|
||||
if 'assigned_to' in task_data and is_admin:
|
||||
new_assigned_to = task_data.get('assigned_to')
|
||||
if new_assigned_to != old_assigned_to:
|
||||
task.assigned_to = new_assigned_to
|
||||
assigned_to_changed = True
|
||||
# Handle assignment - admin can assign, housekeeping can self-assign unassigned tasks
|
||||
if 'assigned_to' in task_data:
|
||||
if is_admin:
|
||||
new_assigned_to = task_data.get('assigned_to')
|
||||
if new_assigned_to != old_assigned_to:
|
||||
task.assigned_to = new_assigned_to
|
||||
assigned_to_changed = True
|
||||
elif is_housekeeping_or_staff and task.assigned_to is None:
|
||||
# Housekeeping can assign unassigned tasks to themselves when starting
|
||||
if task_data.get('assigned_to') == current_user.id:
|
||||
task.assigned_to = current_user.id
|
||||
assigned_to_changed = True
|
||||
|
||||
if 'status' in task_data:
|
||||
new_status = HousekeepingStatus(task_data['status'])
|
||||
@@ -534,6 +641,9 @@ async def update_housekeeping_task(
|
||||
|
||||
if new_status == HousekeepingStatus.in_progress and not task.started_at:
|
||||
task.started_at = datetime.utcnow()
|
||||
# If task was unassigned, assign it to the current user
|
||||
if task.assigned_to is None and is_housekeeping_or_staff:
|
||||
task.assigned_to = current_user.id
|
||||
elif new_status == HousekeepingStatus.completed and not task.completed_at:
|
||||
task.completed_at = datetime.utcnow()
|
||||
if task.started_at:
|
||||
@@ -568,8 +678,23 @@ async def update_housekeeping_task(
|
||||
# Keep room as cleaning if there are other pending tasks
|
||||
room.status = RoomStatus.cleaning
|
||||
else:
|
||||
# No pending tasks and no maintenance - room is ready
|
||||
room.status = RoomStatus.available
|
||||
# No pending tasks and no maintenance - room is ready for check-in
|
||||
# Check if there are any upcoming bookings for this room
|
||||
from ...bookings.models.booking import Booking, BookingStatus
|
||||
upcoming_booking = db.query(Booking).filter(
|
||||
and_(
|
||||
Booking.room_id == room.id,
|
||||
Booking.status == BookingStatus.confirmed,
|
||||
Booking.check_in_date <= datetime.utcnow() + timedelta(days=1)
|
||||
)
|
||||
).first()
|
||||
|
||||
if upcoming_booking:
|
||||
# Room has upcoming booking, keep as available (ready for check-in)
|
||||
room.status = RoomStatus.available
|
||||
else:
|
||||
# No upcoming bookings, room is available
|
||||
room.status = RoomStatus.available
|
||||
|
||||
if 'checklist_items' in task_data:
|
||||
task.checklist_items = task_data['checklist_items']
|
||||
@@ -591,7 +716,7 @@ async def update_housekeeping_task(
|
||||
# Send notification if assignment changed
|
||||
if assigned_to_changed and task.assigned_to:
|
||||
try:
|
||||
from ..routes.chat_routes import manager
|
||||
from ...notifications.routes.notification_routes import notification_manager
|
||||
room = db.query(Room).filter(Room.id == task.room_id).first()
|
||||
task_data_notification = {
|
||||
'id': task.id,
|
||||
@@ -608,13 +733,9 @@ async def update_housekeeping_task(
|
||||
'data': task_data_notification
|
||||
}
|
||||
# Send notification to the newly assigned staff member
|
||||
if task.assigned_to in manager.staff_connections:
|
||||
try:
|
||||
await manager.staff_connections[task.assigned_to].send_json(notification_data)
|
||||
except Exception as e:
|
||||
logger.error(f'Error sending housekeeping task notification to staff {task.assigned_to}: {str(e)}', exc_info=True, extra={'staff_id': task.assigned_to})
|
||||
await notification_manager.send_to_user(task.assigned_to, notification_data)
|
||||
except Exception as e:
|
||||
logger.error(f'Error setting up housekeeping task notification: {str(e)}', exc_info=True)
|
||||
logger.error(f'Error sending housekeeping task notification: {str(e)}', exc_info=True)
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
|
||||
@@ -14,6 +14,7 @@ from ..schemas.room import CreateRoomRequest, UpdateRoomRequest, BulkDeleteRooms
|
||||
from ...shared.utils.response_helpers import success_response
|
||||
from ...reviews.models.review import Review, ReviewStatus
|
||||
from ...bookings.models.booking import Booking, BookingStatus
|
||||
from ...hotel_services.models.housekeeping_task import HousekeepingTask, HousekeepingStatus, HousekeepingType
|
||||
from ..services.room_service import get_rooms_with_ratings, get_amenities_list, normalize_images, get_base_url
|
||||
import os
|
||||
import aiofiles
|
||||
@@ -424,8 +425,39 @@ async def update_room(id: int, room_data: UpdateRoomRequest, request: Request, c
|
||||
|
||||
if room_data.floor is not None:
|
||||
room.floor = room_data.floor
|
||||
old_status = room.status
|
||||
if room_data.status is not None:
|
||||
room.status = RoomStatus(room_data.status)
|
||||
new_status = RoomStatus(room_data.status)
|
||||
room.status = new_status
|
||||
|
||||
# If room status is changed to cleaning, create a housekeeping task if one doesn't exist
|
||||
if new_status == RoomStatus.cleaning and old_status != RoomStatus.cleaning:
|
||||
# Check if there's already a pending housekeeping task for this room
|
||||
existing_task = db.query(HousekeepingTask).filter(
|
||||
and_(
|
||||
HousekeepingTask.room_id == room.id,
|
||||
HousekeepingTask.status.in_([HousekeepingStatus.pending, HousekeepingStatus.in_progress])
|
||||
)
|
||||
).first()
|
||||
|
||||
if not existing_task:
|
||||
# Create a new housekeeping task for the cleaning room
|
||||
cleaning_task = HousekeepingTask(
|
||||
room_id=room.id,
|
||||
task_type=HousekeepingType.vacant,
|
||||
status=HousekeepingStatus.pending,
|
||||
scheduled_time=datetime.utcnow(),
|
||||
created_by=current_user.id,
|
||||
checklist_items=[
|
||||
{'item': 'Deep clean bathroom', 'completed': False, 'notes': ''},
|
||||
{'item': 'Change linens', 'completed': False, 'notes': ''},
|
||||
{'item': 'Vacuum and mop', 'completed': False, 'notes': ''},
|
||||
{'item': 'Dust surfaces', 'completed': False, 'notes': ''},
|
||||
{'item': 'Check amenities', 'completed': False, 'notes': ''}
|
||||
],
|
||||
notes='Room set to cleaning mode'
|
||||
)
|
||||
db.add(cleaning_task)
|
||||
if room_data.featured is not None:
|
||||
room.featured = room_data.featured
|
||||
if room_data.price is not None:
|
||||
@@ -545,24 +577,55 @@ async def upload_room_images(id: int, images: List[UploadFile]=File(...), curren
|
||||
room = db.query(Room).filter(Room.id == id).first()
|
||||
if not room:
|
||||
raise HTTPException(status_code=404, detail='Room not found')
|
||||
upload_dir = Path(__file__).parent.parent.parent / 'uploads' / 'rooms'
|
||||
# Calculate upload directory to match main.py (Backend/uploads/rooms)
|
||||
# From Backend/src/rooms/routes/room_routes.py -> Backend/
|
||||
upload_dir = Path(__file__).parent.parent.parent.parent / 'uploads' / 'rooms'
|
||||
upload_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Import validation and optimization utilities
|
||||
from ...shared.config.settings import settings
|
||||
from ...shared.utils.file_validation import validate_uploaded_image
|
||||
from ...shared.utils.image_optimization import optimize_image_async, ImageType
|
||||
|
||||
image_urls = []
|
||||
for image in images:
|
||||
if not image.content_type or not image.content_type.startswith('image/'):
|
||||
continue
|
||||
if not image.filename:
|
||||
continue
|
||||
import uuid
|
||||
ext = Path(image.filename).suffix or '.jpg'
|
||||
filename = f'room-{uuid.uuid4()}{ext}'
|
||||
file_path = upload_dir / filename
|
||||
async with aiofiles.open(file_path, 'wb') as f:
|
||||
content = await image.read()
|
||||
if not content:
|
||||
|
||||
try:
|
||||
# Validate the image
|
||||
content = await validate_uploaded_image(image, settings.MAX_UPLOAD_SIZE)
|
||||
|
||||
# Optimize image before saving
|
||||
optimized_content, optimized_ext = await optimize_image_async(content, ImageType.ROOM)
|
||||
|
||||
import uuid
|
||||
ext = Path(image.filename).suffix or '.jpg'
|
||||
# Update extension if format changed
|
||||
if optimized_ext:
|
||||
ext = optimized_ext
|
||||
filename = f'room-{uuid.uuid4()}{ext}'
|
||||
file_path = upload_dir / filename
|
||||
|
||||
async with aiofiles.open(file_path, 'wb') as f:
|
||||
await f.write(optimized_content)
|
||||
|
||||
# Verify file was saved
|
||||
if not file_path.exists():
|
||||
logger.error(f'File was not saved: {file_path}')
|
||||
continue
|
||||
await f.write(content)
|
||||
image_urls.append(f'/uploads/rooms/{filename}')
|
||||
|
||||
logger.info(f'Successfully uploaded and optimized image: {filename} ({len(optimized_content)} bytes)')
|
||||
image_urls.append(f'/uploads/rooms/{filename}')
|
||||
except HTTPException:
|
||||
# Skip invalid images and continue with others
|
||||
logger.warning(f'Skipping invalid image: {image.filename}')
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f'Error processing image {image.filename}: {str(e)}', exc_info=True)
|
||||
continue
|
||||
|
||||
# Handle existing_images - it might be a list, a JSON string, or None
|
||||
existing_images = room.images or []
|
||||
@@ -595,20 +658,44 @@ async def delete_room_images(id: int, image_url: str=Query(..., description='Ima
|
||||
room = db.query(Room).filter(Room.id == id).first()
|
||||
if not room:
|
||||
raise HTTPException(status_code=404, detail='Room not found')
|
||||
# For external URLs, keep the full URL for matching
|
||||
# For local files, normalize to path
|
||||
|
||||
# Normalize the input URL to extract the path part
|
||||
# The frontend may send a full URL like "http://localhost:8000/uploads/rooms/image.webp"
|
||||
# but the database stores relative paths like "/uploads/rooms/image.webp"
|
||||
is_external_url = image_url.startswith('http://') or image_url.startswith('https://')
|
||||
normalized_url = image_url
|
||||
normalized_path = image_url
|
||||
filename = None
|
||||
|
||||
if is_external_url:
|
||||
# For external URLs, use the full URL as-is for matching
|
||||
normalized_url = image_url
|
||||
# Extract the path from the full URL
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
parsed_url = urlparse(image_url)
|
||||
normalized_path = parsed_url.path # Extract path like "/uploads/rooms/image.webp"
|
||||
|
||||
# Check if it's a local uploads path (not an external image service)
|
||||
if normalized_path.startswith('/uploads/'):
|
||||
is_external_url = False # It's a local file with full URL
|
||||
filename = Path(normalized_path).name
|
||||
else:
|
||||
# Truly external URL (like Unsplash)
|
||||
normalized_path = image_url
|
||||
except Exception as e:
|
||||
logger.warning(f'Error parsing URL {image_url}: {str(e)}')
|
||||
# Fallback: try to extract path manually
|
||||
if '/uploads/' in image_url:
|
||||
match = image_url.split('/uploads/', 1)
|
||||
if len(match) == 2:
|
||||
normalized_path = f'/uploads/{match[1]}'
|
||||
is_external_url = False
|
||||
filename = Path(normalized_path).name
|
||||
else:
|
||||
# For local files, normalize the path
|
||||
if not normalized_url.startswith('/'):
|
||||
normalized_url = f'/{normalized_url}'
|
||||
filename = Path(normalized_url).name
|
||||
# Local file path - normalize it
|
||||
if not normalized_path.startswith('/'):
|
||||
normalized_path = f'/{normalized_path}'
|
||||
filename = Path(normalized_path).name
|
||||
|
||||
logger.info(f'Deleting image: original={image_url}, normalized_path={normalized_path}, filename={filename}, is_external={is_external_url}')
|
||||
|
||||
# Handle existing_images - it might be a list, a JSON string, or None
|
||||
existing_images = room.images or []
|
||||
@@ -626,24 +713,52 @@ async def delete_room_images(id: int, image_url: str=Query(..., description='Ima
|
||||
|
||||
updated_images = []
|
||||
for img in existing_images:
|
||||
# For external URLs, match by full URL (keep images that don't match)
|
||||
if is_external_url:
|
||||
# Keep the image if it doesn't match the URL we're deleting
|
||||
if img != normalized_url:
|
||||
# Convert stored image to string for comparison
|
||||
img_str = str(img).strip()
|
||||
if not img_str:
|
||||
continue
|
||||
|
||||
# Normalize stored image path
|
||||
stored_path = img_str
|
||||
stored_is_external = stored_path.startswith('http://') or stored_path.startswith('https://')
|
||||
|
||||
if stored_is_external and is_external_url:
|
||||
# Both are external URLs - match exactly
|
||||
if img_str != image_url:
|
||||
updated_images.append(img)
|
||||
elif stored_is_external and not is_external_url:
|
||||
# Stored is external, deleting is local - keep it
|
||||
updated_images.append(img)
|
||||
elif not stored_is_external and is_external_url:
|
||||
# Stored is local, deleting is external - keep it
|
||||
updated_images.append(img)
|
||||
else:
|
||||
# For local files, match by path or filename (keep images that don't match)
|
||||
stored_path = img if img.startswith('/') else f'/{img}'
|
||||
stored_filename = Path(stored_path).name if '/' in str(stored_path) else stored_path
|
||||
# Keep the image if it doesn't match any of the comparison criteria
|
||||
if img != normalized_url and stored_path != normalized_url and (not filename or stored_filename != filename):
|
||||
# Both are local paths - normalize both for comparison
|
||||
stored_normalized = stored_path if stored_path.startswith('/') else f'/{stored_path}'
|
||||
stored_filename = Path(stored_normalized).name if '/' in stored_normalized else stored_path
|
||||
|
||||
# Match by full path or by filename
|
||||
path_matches = (stored_normalized == normalized_path or stored_path == normalized_path)
|
||||
filename_matches = (filename and stored_filename == filename)
|
||||
|
||||
if not (path_matches or filename_matches):
|
||||
# Keep images that don't match
|
||||
updated_images.append(img)
|
||||
|
||||
# Only try to delete the file if it's a local file (filename exists)
|
||||
logger.info(f'Images before: {len(existing_images)}, after: {len(updated_images)}')
|
||||
|
||||
# Only try to delete the physical file if it's a local file (filename exists)
|
||||
if filename:
|
||||
file_path = Path(__file__).parent.parent.parent / 'uploads' / 'rooms' / filename
|
||||
file_path = Path(__file__).parent.parent.parent.parent / 'uploads' / 'rooms' / filename
|
||||
if file_path.exists():
|
||||
file_path.unlink()
|
||||
try:
|
||||
file_path.unlink()
|
||||
logger.info(f'Deleted file: {file_path}')
|
||||
except Exception as e:
|
||||
logger.warning(f'Could not delete file {file_path}: {str(e)}')
|
||||
else:
|
||||
logger.warning(f'File does not exist: {file_path}')
|
||||
|
||||
room.images = updated_images
|
||||
db.commit()
|
||||
return {'status': 'success', 'message': 'Image deleted successfully', 'data': {'images': updated_images}}
|
||||
@@ -651,6 +766,7 @@ async def delete_room_images(id: int, image_url: str=Query(..., description='Ima
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f'Error deleting room image: {str(e)}', exc_info=True, extra={'room_id': id, 'image_url': image_url})
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get('/{id}/booked-dates')
|
||||
|
||||
Reference in New Issue
Block a user