This commit is contained in:
Iliyan Angelov
2025-12-03 01:31:34 +02:00
parent e32527ae8c
commit 5fb50983a9
37 changed files with 5844 additions and 201 deletions

View File

@@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""
Master script to seed all test data for the hotel booking system.
This script runs all necessary seed scripts in the correct order.
"""
import sys
import os
from pathlib import Path
# Add the Backend directory to the path
backend_dir = Path(__file__).parent.parent
sys.path.insert(0, str(backend_dir))
# Also add the seeds_data directory like other seed scripts
sys.path.insert(0, str(Path(__file__).parent))
import bcrypt
def ensure_housekeeping_role(db):
"""Ensure housekeeping role exists"""
from src.models.role import Role
housekeeping_role = db.query(Role).filter(Role.name == 'housekeeping').first()
if not housekeeping_role:
print('Creating housekeeping role...')
housekeeping_role = Role(name='housekeeping', description='Housekeeping staff role')
db.add(housekeeping_role)
db.commit()
db.refresh(housekeeping_role)
print('✓ Housekeeping role created')
return housekeeping_role
def ensure_housekeeping_users(db):
"""Ensure housekeeping users exist"""
from src.models.role import Role
from src.models.user import User
housekeeping_role = db.query(Role).filter(Role.name == 'housekeeping').first()
if not housekeeping_role:
print('❌ Housekeeping role not found!')
return
housekeeping_users = [
{
'email': 'housekeeping@gnxsoft.com',
'password': 'housekeeping123',
'full_name': 'Housekeeping Staff',
'phone': '+1 (555) 999-0000'
},
{
'email': 'housekeeping2@gnxsoft.com',
'password': 'housekeeping123',
'full_name': 'Housekeeping Staff 2',
'phone': '+1 (555) 999-0001'
}
]
for user_data in housekeeping_users:
existing = db.query(User).filter(User.email == user_data['email']).first()
if not existing:
password_bytes = user_data['password'].encode('utf-8')
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password_bytes, salt).decode('utf-8')
user = User(
email=user_data['email'],
password=hashed_password,
full_name=user_data['full_name'],
phone=user_data['phone'],
role_id=housekeeping_role.id,
currency='EUR',
is_active=True
)
db.add(user)
print(f' ✓ Created housekeeping user: {user_data["email"]} - Password: {user_data["password"]}')
else:
print(f' ⚠️ Housekeeping user "{user_data["email"]}" already exists')
db.commit()
def main():
print('=' * 80)
print('SEEDING ALL TEST DATA FOR HOTEL BOOKING SYSTEM')
print('=' * 80)
print()
from src.shared.config.database import SessionLocal
db = SessionLocal()
try:
# Step 1: Ensure housekeeping role exists
print('Step 1: Ensuring housekeeping role exists...')
ensure_housekeeping_role(db)
print()
# Step 2: Ensure housekeeping users exist
print('Step 2: Ensuring housekeeping users exist...')
ensure_housekeeping_users(db)
print()
# Step 3: Import and run seed scripts
print('Step 3: Running seed scripts...')
print()
# Import seed modules
from seeds_data.seed_initial_data import seed_roles, seed_room_types, seed_admin_user
from seeds_data.seed_users import seed_users
from seeds_data.seed_rooms import seed_rooms
from seeds_data.seed_bookings import seed_bookings
# Run seed scripts
print('Running seed_initial_data...')
seed_initial_data.seed_roles(db)
seed_initial_data.seed_room_types(db)
seed_initial_data.seed_admin_user(db)
print()
print('Running seed_users...')
seed_users_module.seed_users(db)
print()
print('Running seed_rooms...')
seed_rooms_module.seed_rooms(db)
print()
print('Running seed_bookings...')
seed_bookings_module.seed_bookings(db)
print()
print('=' * 80)
print('✅ ALL TEST DATA SEEDED SUCCESSFULLY!')
print('=' * 80)
print()
print('📋 Test Accounts:')
print(' Staff: staff@gnxsoft.com / staff123')
print(' Housekeeping: housekeeping@gnxsoft.com / housekeeping123')
print(' Housekeeping 2: housekeeping2@gnxsoft.com / housekeeping123')
print(' Customer: customer@gnxsoft.com / customer123')
print()
print('🧪 To test notifications:')
print(' 1. Log in as staff (staff@gnxsoft.com)')
print(' 2. Go to Bookings and mark a checked_in booking as checked_out')
print(' 3. Log in as housekeeping user in another browser/tab')
print(' 4. You should receive a real-time notification about the room needing cleaning')
print('=' * 80)
except Exception as e:
print(f'\n❌ Error: {e}')
import traceback
traceback.print_exc()
db.rollback()
finally:
db.close()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,329 @@
#!/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()

View File

@@ -1,12 +1,18 @@
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 src.shared.config.database import SessionLocal
from src.models.role import Role
from src.models.room_type import RoomType
from src.models.user import User
# Import all models needed for SQLAlchemy relationship setup
from src.auth.models.role import Role
from src.auth.models.user import User
from src.rooms.models.room_type import RoomType
from src.rooms.models.room import Room
from src.rooms.models.rate_plan import RatePlan # For RoomType relationship
from src.bookings.models.booking import Booking # For Room relationship
import bcrypt
from datetime import datetime

View File

@@ -1,11 +1,13 @@
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 src.shared.config.database import SessionLocal, engine
from src.models.room import Room, RoomStatus
from src.models.room_type import RoomType
from src.rooms.models.room import Room, RoomStatus
from src.rooms.models.room_type import RoomType
from datetime import datetime
import json
import random
@@ -28,9 +30,9 @@ def seed_rooms(db: Session):
print(f'\n✓ Found {len(room_types)} room type(s)')
for rt in room_types:
print(f' - {rt.name} (ID: {rt.id}, Base Price: {rt.base_price})')
from src.models.booking import Booking
from src.models.review import Review
from src.models.favorite import Favorite
from src.bookings.models.booking import Booking
from src.reviews.models.review import Review
from src.reviews.models.favorite import Favorite
existing_rooms = db.query(Room).all()
if existing_rooms:
print(f'\n🗑️ Deleting {len(existing_rooms)} existing room(s)...')

View File

@@ -1,11 +1,13 @@
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 src.shared.config.database import SessionLocal
from src.models.role import Role
from src.models.user import User
from src.auth.models.role import Role
from src.auth.models.user import User
import bcrypt
from datetime import datetime
@@ -25,6 +27,7 @@ def seed_users(db: Session):
admin_role = db.query(Role).filter(Role.name == 'admin').first()
staff_role = db.query(Role).filter(Role.name == 'staff').first()
customer_role = db.query(Role).filter(Role.name == 'customer').first()
housekeeping_role = db.query(Role).filter(Role.name == 'housekeeping').first()
if not admin_role or not staff_role or not customer_role:
print(' ❌ Roles not found! Please seed roles first.')
@@ -105,12 +108,38 @@ def seed_users(db: Session):
}
]
# Add housekeeping users if role exists
if housekeeping_role:
users_data.extend([
{
'email': 'housekeeping@gnxsoft.com',
'password': 'housekeeping123',
'full_name': 'Housekeeping Staff',
'phone': '+1 (555) 999-0000',
'role': 'housekeeping',
'currency': 'EUR',
'is_active': True
},
{
'email': 'housekeeping2@gnxsoft.com',
'password': 'housekeeping123',
'full_name': 'Housekeeping Staff 2',
'phone': '+1 (555) 999-0001',
'role': 'housekeeping',
'currency': 'EUR',
'is_active': True
}
])
role_map = {
'admin': admin_role.id,
'staff': staff_role.id,
'customer': customer_role.id
}
if housekeeping_role:
role_map['housekeeping'] = housekeeping_role.id
created_count = 0
skipped_count = 0