#!/usr/bin/env python3 """ Script to seed sample bookings for testing check-in/check-out and housekeeping notifications. """ import sys import os from pathlib import Path # Add both the seeds_data directory and the Backend directory to the path sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent.parent)) from sqlalchemy.orm import Session from sqlalchemy import and_ from src.shared.config.database import SessionLocal # Import all models needed for SQLAlchemy relationship setup # Import base models first from src.auth.models.role import Role from src.auth.models.user import User from src.rooms.models.room import Room, RoomStatus from src.rooms.models.room_type import RoomType # For Room relationship from src.rooms.models.rate_plan import RatePlan # For RoomType relationship from src.rooms.models.room_maintenance import RoomMaintenance # For Room relationship from src.rooms.models.room_inspection import RoomInspection # For Room relationship from src.rooms.models.room_attribute import RoomAttribute # For Room relationship from src.hotel_services.models.housekeeping_task import HousekeepingTask # For Room relationship from src.bookings.models.booking import Booking, BookingStatus from src.bookings.models.group_booking import GroupBooking # For Booking relationship # Import all related models to satisfy SQLAlchemy relationships # This is necessary because SQLAlchemy needs all related models loaded try: from src.auth.models.refresh_token import RefreshToken from src.reviews.models.review import Review from src.reviews.models.favorite import Favorite from src.hotel_services.models.service import Service # For ServiceBooking relationship from src.hotel_services.models.service_booking import ServiceBooking from src.hotel_services.models.service_usage import ServiceUsage # For Booking relationship from src.bookings.models.checkin_checkout import CheckInCheckOut from src.payments.models.payment import Payment from src.payments.models.invoice import Invoice from src.ai.models.chat import Chat from src.loyalty.models.user_loyalty import UserLoyalty from src.loyalty.models.loyalty_tier import LoyaltyTier # For UserLoyalty relationship from src.loyalty.models.loyalty_point_transaction import LoyaltyPointTransaction # For UserLoyalty relationship from src.loyalty.models.referral import Referral from src.loyalty.models.package import Package # For RoomType relationship from src.guest_management.models.guest_preference import GuestPreference from src.guest_management.models.guest_note import GuestNote from src.guest_management.models.guest_communication import GuestCommunication from src.guest_management.models.guest_tag import GuestTag, guest_tag_association from src.guest_management.models.guest_segment import GuestSegment, guest_segment_association # Import any other models that might be related except ImportError: pass # Some models might not exist, that's okay from datetime import datetime, timedelta import random import secrets def get_db(): db = SessionLocal() try: return db finally: pass def generate_booking_number() -> str: """Generate a unique booking number""" prefix = 'BK' ts = int(datetime.utcnow().timestamp() * 1000) rand = secrets.randbelow(9000) + 1000 return f'{prefix}-{ts}-{rand}' def seed_bookings(db: Session): print('=' * 80) print('SEEDING SAMPLE BOOKINGS FOR TESTING') print('=' * 80) # Get roles customer_role = db.query(Role).filter(Role.name == 'customer').first() staff_role = db.query(Role).filter(Role.name == 'staff').first() admin_role = db.query(Role).filter(Role.name == 'admin').first() if not customer_role: print(' āŒ Customer role not found! Please seed roles first.') return # Get customers customers = db.query(User).filter(User.role_id == customer_role.id).all() if not customers: print(' āŒ No customer users found! Please seed users first.') return # Get available rooms rooms = db.query(Room).all() if not rooms: print(' āŒ No rooms found! Please seed rooms first.') return print(f'\nāœ“ Found {len(customers)} customer(s) and {len(rooms)} room(s)') # Delete existing bookings (optional - comment out if you want to keep existing bookings) existing_bookings = db.query(Booking).all() if existing_bookings: print(f'\nšŸ—‘ļø Deleting {len(existing_bookings)} existing booking(s)...') booking_ids = [b.id for b in existing_bookings] # Delete related records first to avoid foreign key constraints try: from src.guest_management.models.guest_complaint import GuestComplaint, ComplaintUpdate complaints = db.query(GuestComplaint).filter(GuestComplaint.booking_id.in_(booking_ids)).all() if complaints: complaint_ids = [c.id for c in complaints] # Delete complaint updates first updates = db.query(ComplaintUpdate).filter(ComplaintUpdate.complaint_id.in_(complaint_ids)).all() if updates: for update in updates: db.delete(update) print(f' āœ“ Deleted {len(updates)} complaint update(s)') # Then delete complaints for complaint in complaints: db.delete(complaint) print(f' āœ“ Deleted {len(complaints)} guest complaint(s)') except ImportError: pass for booking in existing_bookings: db.delete(booking) db.commit() print(f'āœ“ Deleted {len(existing_bookings)} booking(s)') # Create sample bookings with different statuses now = datetime.utcnow() bookings_data = [] # 1. Past bookings (checked out) - These should trigger cleaning notifications print('\nšŸ“… Creating past bookings (checked out)...') for i in range(3): room = random.choice(rooms) customer = random.choice(customers) # Check-out was 1-3 days ago days_ago = random.randint(1, 3) check_out_date = now - timedelta(days=days_ago) check_in_date = check_out_date - timedelta(days=random.randint(1, 5)) # Calculate price base_price = float(room.price) if room.price else 100.0 num_nights = (check_out_date - check_in_date).days total_price = base_price * num_nights booking = Booking( booking_number=generate_booking_number(), user_id=customer.id, room_id=room.id, check_in_date=check_in_date, check_out_date=check_out_date, num_guests=random.randint(1, min(room.capacity, 4)), total_price=total_price, status=BookingStatus.checked_out, deposit_paid=True, requires_deposit=False, special_requests=f'Sample booking for testing - Checked out {days_ago} day(s) ago' ) db.add(booking) db.flush() # Flush to get booking ID # Set room status to cleaning for checked out bookings # Check if there's active maintenance first try: from src.rooms.models.room_maintenance import RoomMaintenance, MaintenanceStatus active_maintenance = db.query(RoomMaintenance).filter( and_( RoomMaintenance.room_id == room.id, RoomMaintenance.blocks_room == True, RoomMaintenance.status.in_([MaintenanceStatus.scheduled, MaintenanceStatus.in_progress]) ) ).first() if not active_maintenance: room.status = RoomStatus.cleaning # Create housekeeping task for checkout cleaning try: from src.hotel_services.models.housekeeping_task import HousekeepingTask, HousekeepingStatus, HousekeepingType checkout_checklist = [ {'item': 'Bathroom cleaned', 'completed': False, 'notes': ''}, {'item': 'Beds made with fresh linens', 'completed': False, 'notes': ''}, {'item': 'Trash emptied', 'completed': False, 'notes': ''}, {'item': 'Towels replaced', 'completed': False, 'notes': ''}, {'item': 'Amenities restocked', 'completed': False, 'notes': ''}, {'item': 'Floor vacuumed and mopped', 'completed': False, 'notes': ''}, {'item': 'Surfaces dusted', 'completed': False, 'notes': ''}, {'item': 'Windows and mirrors cleaned', 'completed': False, 'notes': ''}, ] housekeeping_task = HousekeepingTask( room_id=room.id, booking_id=booking.id, task_type=HousekeepingType.checkout, status=HousekeepingStatus.pending, scheduled_time=datetime.utcnow(), created_by=admin_role.id if admin_role else (staff_role.id if staff_role else None), checklist_items=checkout_checklist, notes=f'Auto-created on checkout for booking {booking.booking_number}', estimated_duration_minutes=45 ) db.add(housekeeping_task) except ImportError: pass # If housekeeping models not available, skip task creation except ImportError: # If maintenance model not available, just set to cleaning room.status = RoomStatus.cleaning bookings_data.append({ 'number': booking.booking_number, 'room': room.room_number, 'status': 'checked_out', 'check_out': check_out_date.strftime('%Y-%m-%d') }) print(f' āœ“ Created booking {booking.booking_number} - Room {room.room_number}, Checked out {days_ago} day(s) ago') # 2. Current bookings (checked in) - Guests currently staying print('\nšŸ“… Creating current bookings (checked in)...') for i in range(2): room = random.choice(rooms) customer = random.choice(customers) # Checked in 1-3 days ago, checking out in 1-3 days days_ago = random.randint(1, 3) check_in_date = now - timedelta(days=days_ago) check_out_date = now + timedelta(days=random.randint(1, 3)) base_price = float(room.price) if room.price else 100.0 num_nights = (check_out_date - check_in_date).days total_price = base_price * num_nights booking = Booking( booking_number=generate_booking_number(), user_id=customer.id, room_id=room.id, check_in_date=check_in_date, check_out_date=check_out_date, num_guests=random.randint(1, min(room.capacity, 4)), total_price=total_price, status=BookingStatus.checked_in, deposit_paid=True, requires_deposit=False, special_requests=f'Sample booking for testing - Currently checked in' ) db.add(booking) db.flush() # Flush to get booking ID # Set room status to occupied for checked in bookings room.status = RoomStatus.occupied bookings_data.append({ 'number': booking.booking_number, 'room': room.room_number, 'status': 'checked_in', 'check_out': check_out_date.strftime('%Y-%m-%d') }) print(f' āœ“ Created booking {booking.booking_number} - Room {room.room_number}, Checked in {days_ago} day(s) ago') # 3. Future bookings (confirmed) - Upcoming reservations print('\nšŸ“… Creating future bookings (confirmed)...') for i in range(3): room = random.choice(rooms) customer = random.choice(customers) # Check-in in 1-7 days, staying for 2-5 days days_ahead = random.randint(1, 7) check_in_date = now + timedelta(days=days_ahead) check_out_date = check_in_date + timedelta(days=random.randint(2, 5)) base_price = float(room.price) if room.price else 100.0 num_nights = (check_out_date - check_in_date).days total_price = base_price * num_nights booking = Booking( booking_number=generate_booking_number(), user_id=customer.id, room_id=room.id, check_in_date=check_in_date, check_out_date=check_out_date, num_guests=random.randint(1, min(room.capacity, 4)), total_price=total_price, status=BookingStatus.confirmed, deposit_paid=random.choice([True, False]), requires_deposit=random.choice([True, False]), special_requests=f'Sample booking for testing - Future reservation' ) db.add(booking) bookings_data.append({ 'number': booking.booking_number, 'room': room.room_number, 'status': 'confirmed', 'check_in': check_in_date.strftime('%Y-%m-%d') }) print(f' āœ“ Created booking {booking.booking_number} - Room {room.room_number}, Check-in in {days_ahead} day(s)') db.commit() print(f'\nāœ… Successfully created {len(bookings_data)} sample bookings!') print(f'\nšŸ“Š Summary:') checked_out = sum(1 for b in bookings_data if b['status'] == 'checked_out') checked_in = sum(1 for b in bookings_data if b['status'] == 'checked_in') confirmed = sum(1 for b in bookings_data if b['status'] == 'confirmed') print(f' - Checked out: {checked_out} (rooms should be in cleaning status)') print(f' - Checked in: {checked_in} (guests currently staying)') print(f' - Confirmed: {confirmed} (upcoming reservations)') print('\nšŸ’” To test notifications:') print(' 1. Log in as staff user (staff@gnxsoft.com / staff123)') print(' 2. Go to Bookings and mark a checked_in booking as checked_out') print(' 3. Log in as housekeeping user (housekeeping@gnxsoft.com / P4eli240453.)') print(' 4. You should receive a notification about the room needing cleaning') print('=' * 80) def main(): db = get_db() try: seed_bookings(db) except Exception as e: print(f'\nāŒ Error: {e}') import traceback traceback.print_exc() db.rollback() finally: db.close() if __name__ == '__main__': main()