474 lines
19 KiB
Python
474 lines
19 KiB
Python
"""
|
|
Room Seeder
|
|
Seeds the database with room types and rooms
|
|
All images are from Unsplash (free stock photos)
|
|
"""
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from datetime import datetime, timezone
|
|
from decimal import Decimal
|
|
|
|
# Add parent directory to path to import modules
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
|
|
from sqlalchemy.orm import Session
|
|
from src.shared.config.database import SessionLocal
|
|
from src.shared.config.logging_config import get_logger
|
|
from src.rooms.models.room import Room, RoomStatus
|
|
from src.rooms.models.room_type import RoomType
|
|
|
|
# Import all models to ensure relationships are loaded correctly
|
|
from src.models import *
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
def get_room_types_data():
|
|
"""Generate room types data"""
|
|
now = datetime.now(timezone.utc)
|
|
|
|
return [
|
|
{
|
|
'name': 'Standard Room',
|
|
'description': 'Comfortable and well-appointed standard rooms perfect for business travelers and couples. Features modern amenities and elegant decor.',
|
|
'base_price': Decimal('150.00'),
|
|
'capacity': 2,
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Flat-screen TV',
|
|
'Air Conditioning',
|
|
'Mini Bar',
|
|
'Work Desk',
|
|
'In-room Safe',
|
|
'Coffee Maker',
|
|
'Hair Dryer'
|
|
])
|
|
},
|
|
{
|
|
'name': 'Deluxe Room',
|
|
'description': 'Spacious deluxe rooms with enhanced amenities and premium furnishings. Ideal for guests seeking extra comfort and space.',
|
|
'base_price': Decimal('220.00'),
|
|
'capacity': 2,
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV',
|
|
'Air Conditioning',
|
|
'Premium Mini Bar',
|
|
'Work Desk',
|
|
'In-room Safe',
|
|
'Nespresso Machine',
|
|
'Hair Dryer',
|
|
'Bathrobe & Slippers',
|
|
'City View'
|
|
])
|
|
},
|
|
{
|
|
'name': 'Executive Suite',
|
|
'description': 'Luxurious suites with separate living areas, perfect for extended stays or guests who prefer extra space and privacy.',
|
|
'base_price': Decimal('350.00'),
|
|
'capacity': 3,
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV (Multiple)',
|
|
'Air Conditioning',
|
|
'Premium Mini Bar',
|
|
'Separate Living Area',
|
|
'In-room Safe',
|
|
'Nespresso Machine',
|
|
'Hair Dryer',
|
|
'Bathrobe & Slippers',
|
|
'Panoramic View',
|
|
'Premium Toiletries',
|
|
'Welcome Amenities'
|
|
])
|
|
},
|
|
{
|
|
'name': 'Presidential Suite',
|
|
'description': 'The ultimate in luxury accommodation. Spacious multi-room suite with premium amenities, private terrace, and personalized butler service.',
|
|
'base_price': Decimal('800.00'),
|
|
'capacity': 4,
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV (Multiple)',
|
|
'Air Conditioning',
|
|
'Premium Mini Bar',
|
|
'Separate Living & Dining Areas',
|
|
'In-room Safe',
|
|
'Nespresso Machine',
|
|
'Hair Dryer',
|
|
'Bathrobe & Slippers',
|
|
'Panoramic View',
|
|
'Premium Toiletries',
|
|
'Welcome Amenities',
|
|
'Private Terrace',
|
|
'Butler Service',
|
|
'Private Bar',
|
|
'Jacuzzi'
|
|
])
|
|
},
|
|
{
|
|
'name': 'Ocean View Room',
|
|
'description': 'Stunning ocean view rooms with floor-to-ceiling windows and private balconies. Perfect for romantic getaways and relaxation.',
|
|
'base_price': Decimal('280.00'),
|
|
'capacity': 2,
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV',
|
|
'Air Conditioning',
|
|
'Premium Mini Bar',
|
|
'Work Desk',
|
|
'In-room Safe',
|
|
'Nespresso Machine',
|
|
'Hair Dryer',
|
|
'Bathrobe & Slippers',
|
|
'Ocean View',
|
|
'Private Balcony',
|
|
'Premium Toiletries'
|
|
])
|
|
},
|
|
{
|
|
'name': 'Family Room',
|
|
'description': 'Spacious family-friendly rooms designed to accommodate families with children. Features extra beds and family amenities.',
|
|
'base_price': Decimal('300.00'),
|
|
'capacity': 4,
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV (Multiple)',
|
|
'Air Conditioning',
|
|
'Mini Bar',
|
|
'Extra Beds',
|
|
'In-room Safe',
|
|
'Coffee Maker',
|
|
'Hair Dryer',
|
|
'Family Amenities',
|
|
'Game Console',
|
|
'Child Safety Features'
|
|
])
|
|
}
|
|
]
|
|
|
|
|
|
def get_rooms_data(room_type_map):
|
|
"""Generate rooms data based on room types"""
|
|
now = datetime.now(timezone.utc)
|
|
|
|
rooms = []
|
|
|
|
# Standard Rooms (Floors 1-3, Rooms 101-130)
|
|
standard_type = room_type_map.get('Standard Room')
|
|
if standard_type:
|
|
for floor in range(1, 4):
|
|
for room_num in range(1, 11):
|
|
room_number = f"{floor}{room_num:02d}"
|
|
rooms.append({
|
|
'room_type_id': standard_type.id,
|
|
'room_number': room_number,
|
|
'floor': floor,
|
|
'status': RoomStatus.available,
|
|
'price': Decimal('150.00'),
|
|
'featured': room_num <= 2, # First 2 rooms per floor are featured
|
|
'capacity': 2,
|
|
'room_size': '25 sqm',
|
|
'view': 'City View',
|
|
'images': json.dumps([
|
|
'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1596394516093-501ba68a0ba6?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1618773928121-c32242e63f39?w=1200&h=800&fit=crop'
|
|
]),
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Flat-screen TV',
|
|
'Air Conditioning',
|
|
'Mini Bar',
|
|
'Work Desk',
|
|
'In-room Safe'
|
|
]),
|
|
'description': f'Comfortable standard room on floor {floor} with modern amenities and city view. Perfect for business travelers and couples.'
|
|
})
|
|
|
|
# Deluxe Rooms (Floors 4-6, Rooms 401-430)
|
|
deluxe_type = room_type_map.get('Deluxe Room')
|
|
if deluxe_type:
|
|
for floor in range(4, 7):
|
|
for room_num in range(1, 11):
|
|
room_number = f"{floor}{room_num:02d}"
|
|
rooms.append({
|
|
'room_type_id': deluxe_type.id,
|
|
'room_number': room_number,
|
|
'floor': floor,
|
|
'status': RoomStatus.available,
|
|
'price': Decimal('220.00'),
|
|
'featured': room_num <= 2,
|
|
'capacity': 2,
|
|
'room_size': '35 sqm',
|
|
'view': 'City View',
|
|
'images': json.dumps([
|
|
'https://images.unsplash.com/photo-1618773928121-c32242e63f39?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1590490360182-c33d57733427?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=1200&h=800&fit=crop'
|
|
]),
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV',
|
|
'Air Conditioning',
|
|
'Premium Mini Bar',
|
|
'Work Desk',
|
|
'Nespresso Machine',
|
|
'Bathrobe & Slippers'
|
|
]),
|
|
'description': f'Spacious deluxe room on floor {floor} with premium amenities and enhanced comfort. Ideal for guests seeking extra space.'
|
|
})
|
|
|
|
# Executive Suites (Floors 7-8, Rooms 701-720)
|
|
executive_type = room_type_map.get('Executive Suite')
|
|
if executive_type:
|
|
for floor in range(7, 9):
|
|
for room_num in range(1, 11):
|
|
room_number = f"{floor}{room_num:02d}"
|
|
rooms.append({
|
|
'room_type_id': executive_type.id,
|
|
'room_number': room_number,
|
|
'floor': floor,
|
|
'status': RoomStatus.available,
|
|
'price': Decimal('350.00'),
|
|
'featured': room_num <= 3,
|
|
'capacity': 3,
|
|
'room_size': '55 sqm',
|
|
'view': 'Panoramic View',
|
|
'images': json.dumps([
|
|
'https://images.unsplash.com/photo-1590490360182-c33d57733427?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1618773928121-c32242e63f39?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1596394516093-501ba68a0ba6?w=1200&h=800&fit=crop'
|
|
]),
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV (Multiple)',
|
|
'Separate Living Area',
|
|
'Premium Mini Bar',
|
|
'Nespresso Machine',
|
|
'Bathrobe & Slippers',
|
|
'Premium Toiletries',
|
|
'Welcome Amenities'
|
|
]),
|
|
'description': f'Luxurious executive suite on floor {floor} with separate living area and panoramic views. Perfect for extended stays.'
|
|
})
|
|
|
|
# Presidential Suites (Floor 9, Rooms 901-905)
|
|
presidential_type = room_type_map.get('Presidential Suite')
|
|
if presidential_type:
|
|
for room_num in range(1, 6):
|
|
room_number = f"9{room_num:02d}"
|
|
rooms.append({
|
|
'room_type_id': presidential_type.id,
|
|
'room_number': room_number,
|
|
'floor': 9,
|
|
'status': RoomStatus.available,
|
|
'price': Decimal('800.00'),
|
|
'featured': True, # All presidential suites are featured
|
|
'capacity': 4,
|
|
'room_size': '120 sqm',
|
|
'view': 'Panoramic View',
|
|
'images': json.dumps([
|
|
'https://images.unsplash.com/photo-1596394516093-501ba68a0ba6?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1618773928121-c32242e63f39?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1590490360182-c33d57733427?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1564501049412-61c2a3083791?w=1200&h=800&fit=crop'
|
|
]),
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV (Multiple)',
|
|
'Separate Living & Dining Areas',
|
|
'Private Terrace',
|
|
'Butler Service',
|
|
'Premium Mini Bar',
|
|
'Private Bar',
|
|
'Jacuzzi',
|
|
'Premium Toiletries',
|
|
'Welcome Amenities'
|
|
]),
|
|
'description': f'Ultimate luxury in our exclusive presidential suite. Features grand living room, formal dining, private terrace, and personal butler service. Suite {room_num}.'
|
|
})
|
|
|
|
# Ocean View Rooms (Floor 10, Rooms 1001-1020)
|
|
ocean_type = room_type_map.get('Ocean View Room')
|
|
if ocean_type:
|
|
for room_num in range(1, 21):
|
|
room_number = f"10{room_num:02d}"
|
|
rooms.append({
|
|
'room_type_id': ocean_type.id,
|
|
'room_number': room_number,
|
|
'floor': 10,
|
|
'status': RoomStatus.available,
|
|
'price': Decimal('280.00'),
|
|
'featured': room_num <= 5,
|
|
'capacity': 2,
|
|
'room_size': '30 sqm',
|
|
'view': 'Ocean View',
|
|
'images': json.dumps([
|
|
'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1587925358603-dc217c8a64f8?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1564501049412-61c2a3083791?w=1200&h=800&fit=crop'
|
|
]),
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV',
|
|
'Air Conditioning',
|
|
'Premium Mini Bar',
|
|
'Private Balcony',
|
|
'Nespresso Machine',
|
|
'Bathrobe & Slippers',
|
|
'Ocean View'
|
|
]),
|
|
'description': f'Stunning ocean view room with floor-to-ceiling windows and private balcony. Perfect for romantic getaways and relaxation.'
|
|
})
|
|
|
|
# Family Rooms (Floor 1, Rooms 111-120)
|
|
family_type = room_type_map.get('Family Room')
|
|
if family_type:
|
|
for room_num in range(11, 21):
|
|
room_number = f"1{room_num:02d}"
|
|
rooms.append({
|
|
'room_type_id': family_type.id,
|
|
'room_number': room_number,
|
|
'floor': 1,
|
|
'status': RoomStatus.available,
|
|
'price': Decimal('300.00'),
|
|
'featured': room_num <= 13,
|
|
'capacity': 4,
|
|
'room_size': '45 sqm',
|
|
'view': 'City View',
|
|
'images': json.dumps([
|
|
'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1618773928121-c32242e63f39?w=1200&h=800&fit=crop',
|
|
'https://images.unsplash.com/photo-1590490360182-c33d57733427?w=1200&h=800&fit=crop'
|
|
]),
|
|
'amenities': json.dumps([
|
|
'Free WiFi',
|
|
'Smart TV (Multiple)',
|
|
'Air Conditioning',
|
|
'Mini Bar',
|
|
'Extra Beds',
|
|
'Game Console',
|
|
'Child Safety Features',
|
|
'Family Amenities'
|
|
]),
|
|
'description': f'Spacious family-friendly room on floor 1 designed to accommodate families with children. Features extra beds and family amenities.'
|
|
})
|
|
|
|
return rooms
|
|
|
|
|
|
def seed_room_types(db: Session):
|
|
"""Seed room types into the database"""
|
|
try:
|
|
room_types_data = get_room_types_data()
|
|
room_type_map = {}
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
for room_type_data in room_types_data:
|
|
existing = db.query(RoomType).filter(RoomType.name == room_type_data['name']).first()
|
|
|
|
if existing:
|
|
logger.info(f"Room type '{room_type_data['name']}' already exists. Updating...")
|
|
for key, value in room_type_data.items():
|
|
setattr(existing, key, value)
|
|
existing.updated_at = now
|
|
room_type_map[existing.name] = existing
|
|
else:
|
|
logger.info(f"Creating room type: {room_type_data['name']}")
|
|
room_type = RoomType(
|
|
**room_type_data,
|
|
created_at=now,
|
|
updated_at=now
|
|
)
|
|
db.add(room_type)
|
|
db.flush() # Flush to get the ID
|
|
room_type_map[room_type.name] = room_type
|
|
|
|
db.commit()
|
|
logger.info(f'Successfully seeded {len(room_type_map)} room types!')
|
|
return room_type_map
|
|
except Exception as e:
|
|
db.rollback()
|
|
logger.error(f'Error seeding room types: {str(e)}', exc_info=True)
|
|
raise
|
|
|
|
|
|
def seed_rooms(db: Session, room_type_map: dict, clear_existing: bool = False):
|
|
"""Seed rooms into the database"""
|
|
try:
|
|
if clear_existing:
|
|
logger.info('Clearing existing rooms...')
|
|
db.query(Room).delete()
|
|
db.commit()
|
|
logger.info('Existing rooms cleared.')
|
|
|
|
rooms_data = get_rooms_data(room_type_map)
|
|
now = datetime.now(timezone.utc)
|
|
|
|
created_count = 0
|
|
updated_count = 0
|
|
|
|
for room_data in rooms_data:
|
|
existing = db.query(Room).filter(Room.room_number == room_data['room_number']).first()
|
|
|
|
if existing:
|
|
logger.debug(f"Room '{room_data['room_number']}' already exists. Updating...")
|
|
for key, value in room_data.items():
|
|
setattr(existing, key, value)
|
|
existing.updated_at = now
|
|
updated_count += 1
|
|
else:
|
|
logger.debug(f"Creating room: {room_data['room_number']}")
|
|
room = Room(
|
|
**room_data,
|
|
created_at=now,
|
|
updated_at=now
|
|
)
|
|
db.add(room)
|
|
created_count += 1
|
|
|
|
db.commit()
|
|
logger.info(f'Successfully seeded rooms! Created: {created_count}, Updated: {updated_count}, Total: {len(rooms_data)}')
|
|
except Exception as e:
|
|
db.rollback()
|
|
logger.error(f'Error seeding rooms: {str(e)}', exc_info=True)
|
|
raise
|
|
|
|
|
|
def seed_rooms_and_types(db: Session, clear_existing: bool = False):
|
|
"""Seed both room types and rooms"""
|
|
room_type_map = seed_room_types(db)
|
|
seed_rooms(db, room_type_map, clear_existing=clear_existing)
|
|
return room_type_map
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='Seed room types and rooms')
|
|
parser.add_argument(
|
|
'--clear-rooms',
|
|
action='store_true',
|
|
help='Clear existing rooms before seeding (room types are updated, not cleared)'
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
seed_rooms_and_types(db, clear_existing=args.clear_rooms)
|
|
except Exception as e:
|
|
logger.error(f'Failed to seed rooms: {str(e)}', exc_info=True)
|
|
sys.exit(1)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|