""" Comprehensive data seeding script for ThrillWiki. This script creates realistic test data for all models in the system, including users, parks, rides, companies, reviews, and all related data. Designed for maximum testing coverage and realistic scenarios. Usage: uv run manage.py seed_data uv run manage.py seed_data --clear # Clear existing data first uv run manage.py seed_data --users 50 --parks 20 --rides 100 # Custom counts """ import random from datetime import date from decimal import Decimal from typing import List from django.core.management.base import BaseCommand, CommandError from django.contrib.auth import get_user_model from django.contrib.gis.geos import Point from django.db import transaction from django.utils.text import slugify # Import all models from apps.accounts.models import ( User, UserProfile, TopList, TopListItem, UserNotification, NotificationPreference, UserDeletionRequest ) from apps.parks.models import ( Park, ParkLocation, ParkArea, ParkPhoto, ParkReview ) from apps.parks.models.companies import Company as ParkCompany, CompanyHeadquarters from apps.rides.models import ( Ride, RideModel, RollerCoasterStats, RidePhoto, RideReview, RideLocation ) from apps.rides.models.company import Company as RideCompany from apps.core.history import HistoricalSlug # Try to import optional models that may not exist try: from apps.rides.models import RideModelVariant, RideModelPhoto, RideModelTechnicalSpec except ImportError: RideModelVariant = None RideModelPhoto = None RideModelTechnicalSpec = None try: from apps.rides.models import RideRanking except ImportError: RideRanking = None try: from apps.moderation.models import ModerationQueue, ModerationAction except ImportError: ModerationQueue = None ModerationAction = None try: from django_cloudflareimages_toolkit.models import CloudflareImage except ImportError: CloudflareImage = None User = get_user_model() class Command(BaseCommand): help = 'Seed the database with comprehensive test data for all models' def add_arguments(self, parser): parser.add_argument( '--clear', action='store_true', help='Clear existing data before seeding', ) parser.add_argument( '--users', type=int, default=25, help='Number of users to create (default: 25)', ) parser.add_argument( '--companies', type=int, default=15, help='Number of companies to create (default: 15)', ) parser.add_argument( '--parks', type=int, default=10, help='Number of parks to create (default: 10)', ) parser.add_argument( '--rides', type=int, default=50, help='Number of rides to create (default: 50)', ) parser.add_argument( '--ride-models', type=int, default=20, help='Number of ride models to create (default: 20)', ) parser.add_argument( '--reviews', type=int, default=100, help='Number of reviews to create (default: 100)', ) def handle(self, *args, **options): self.stdout.write( self.style.SUCCESS('Starting comprehensive data seeding...') ) if options['clear']: self.clear_data() with transaction.atomic(): # Create data in dependency order users = self.create_users(options['users']) companies = self.create_companies(options['companies']) ride_models = self.create_ride_models(options['ride_models'], companies) parks = self.create_parks(options['parks'], companies) rides = self.create_rides(options['rides'], parks, companies, ride_models) # Create content and interactions self.create_reviews(options['reviews'], users, parks, rides) self.create_top_lists(users, parks, rides) self.create_notifications(users) self.create_moderation_data(users, parks, rides) # Create media and photos self.create_photos(parks, rides, ride_models) # Create rankings and statistics self.create_rankings(rides) self.stdout.write( self.style.SUCCESS('โœ… Data seeding completed successfully!') ) self.print_summary() def clear_data(self): """Clear existing data in reverse dependency order""" self.stdout.write('๐Ÿ—‘๏ธ Clearing existing data...') models_to_clear = [ # Content and interactions (clear first) TopListItem, TopList, UserNotification, NotificationPreference, ParkReview, RideReview, ModerationAction, ModerationQueue, # Media ParkPhoto, RidePhoto, CloudflareImage, # Core entities RollerCoasterStats, Ride, ParkArea, Park, ParkLocation, RideModel, CompanyHeadquarters, ParkCompany, RideCompany, # Users (clear last due to foreign key dependencies) UserDeletionRequest, UserProfile, User, # History HistoricalSlug, ] # Add optional models if they exist if RideRanking: models_to_clear.insert(4, RideRanking) if RideLocation: models_to_clear.insert(-6, RideLocation) if RideModelPhoto: models_to_clear.insert(6, RideModelPhoto) if RideModelTechnicalSpec: models_to_clear.insert(-6, RideModelTechnicalSpec) if RideModelVariant: models_to_clear.insert(-6, RideModelVariant) if ModerationQueue: models_to_clear.insert(4, ModerationQueue) for model in models_to_clear: try: count = model.objects.count() if count > 0: model.objects.all().delete() self.stdout.write(f' Cleared {count} {model._meta.verbose_name_plural}') except Exception as e: self.stdout.write( self.style.WARNING(f' โš ๏ธ Could not clear {model._meta.verbose_name_plural}: {str(e)}') ) # Continue with other models continue def create_users(self, count: int) -> List[User]: """Create diverse users with comprehensive profiles""" self.stdout.write(f'๐Ÿ‘ฅ Creating {count} users...') users = [] # Create admin user if it doesn't exist admin, created = User.objects.get_or_create( username='admin', defaults={ 'email': 'admin@thrillwiki.com', 'role': 'ADMIN', 'is_staff': True, 'is_superuser': True, 'display_name': 'ThrillWiki Admin', 'theme_preference': 'dark', 'privacy_level': 'public', } ) if created: admin.set_password('admin123') admin.save() users.append(admin) # Create moderator if it doesn't exist moderator, created = User.objects.get_or_create( username='moderator', defaults={ 'email': 'mod@thrillwiki.com', 'role': 'MODERATOR', 'is_staff': True, 'display_name': 'Site Moderator', 'theme_preference': 'light', 'privacy_level': 'public', } ) if created: moderator.set_password('mod123') moderator.save() users.append(moderator) # Sample user data first_names = [ 'Alex', 'Jordan', 'Taylor', 'Casey', 'Morgan', 'Riley', 'Avery', 'Quinn', 'Blake', 'Cameron', 'Drew', 'Emery', 'Finley', 'Harper', 'Hayden', 'Jamie', 'Kendall', 'Logan', 'Parker', 'Peyton', 'Reese', 'Sage', 'Skyler', 'Sydney', 'Tanner' ] last_names = [ 'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez', 'Hernandez', 'Lopez', 'Gonzalez', 'Wilson', 'Anderson', 'Thomas', 'Taylor', 'Moore', 'Jackson', 'Martin', 'Lee', 'Perez', 'Thompson', 'White', 'Harris' ] domains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', 'icloud.com'] # Create regular users for i in range(count - 2): # -2 for admin and moderator first_name = random.choice(first_names) last_name = random.choice(last_names) username = f"{first_name.lower()}{last_name.lower()}{random.randint(1, 999)}" email = f"{username}@{random.choice(domains)}" user = User.objects.create_user( username=username, email=email, password='password123', display_name=f"{first_name} {last_name}", role=random.choice(['USER'] * 8 + ['MODERATOR']), theme_preference=random.choice(['light', 'dark']), privacy_level=random.choice(['public', 'friends', 'private']), email_notifications=random.choice([True, False]), push_notifications=random.choice([True, False]), show_email=random.choice([True, False]), show_real_name=random.choice([True, False]), show_statistics=random.choice([True, False]), allow_friend_requests=random.choice([True, False]), two_factor_enabled=random.choice([True, False]), login_notifications=random.choice([True, False]), ) # Create detailed notification preferences user.notification_preferences = { 'email': { 'reviews': random.choice([True, False]), 'submissions': random.choice([True, False]), 'social': random.choice([True, False]), 'system': random.choice([True, False]), }, 'push': { 'reviews': random.choice([True, False]), 'submissions': random.choice([True, False]), 'social': random.choice([True, False]), 'achievements': random.choice([True, False]), }, 'in_app': { 'all': random.choice([True, False]), } } user.save() # Create user profile with ride credits profile = UserProfile.objects.get(user=user) profile.bio = f"Thrill seeker from {random.choice(['California', 'Florida', 'Ohio', 'Pennsylvania', 'Texas'])}. Love roller coasters!" profile.pronouns = random.choice(['he/him', 'she/her', 'they/them', '']) profile.coaster_credits = random.randint(0, 500) profile.dark_ride_credits = random.randint(0, 100) profile.flat_ride_credits = random.randint(0, 200) profile.water_ride_credits = random.randint(0, 50) # Add social media links for some users if random.random() < 0.3: profile.twitter = f"https://twitter.com/{username}" if random.random() < 0.2: profile.instagram = f"https://instagram.com/{username}" if random.random() < 0.1: profile.youtube = f"https://youtube.com/@{username}" profile.save() users.append(user) self.stdout.write(f' โœ… Created {len(users)} users') return users def create_companies(self, count: int) -> List: """Create companies with different roles""" self.stdout.write(f'๐Ÿข Creating {count} companies...') companies = [] # Major theme park operators operators_data = [ ('Walt Disney Company', ['OPERATOR', 'PROPERTY_OWNER'], 1923, 'Burbank, CA, USA'), ('Universal Parks & Resorts', ['OPERATOR', 'PROPERTY_OWNER'], 1964, 'Orlando, FL, USA'), ('Six Flags Entertainment', ['OPERATOR'], 1961, 'Arlington, TX, USA'), ('Cedar Fair', ['OPERATOR'], 1983, 'Sandusky, OH, USA'), ('SeaWorld Parks', ['OPERATOR'], 1964, 'Orlando, FL, USA'), ('Busch Gardens', ['OPERATOR'], 1959, 'Tampa, FL, USA'), ('Knott\'s Berry Farm', ['OPERATOR'], 1920, 'Buena Park, CA, USA'), ] # Major ride manufacturers manufacturers_data = [ ('Bolliger & Mabillard', ['MANUFACTURER'], 1988, 'Monthey, Switzerland'), ('Intamin', ['MANUFACTURER'], 1967, 'Schaan, Liechtenstein'), ('Vekoma', ['MANUFACTURER'], 1926, 'Vlodrop, Netherlands'), ('Rocky Mountain Construction', ['MANUFACTURER'], 2001, 'Hayden, ID, USA'), ('Mack Rides', ['MANUFACTURER'], 1780, 'Waldkirch, Germany'), ('Gerstlauer', ['MANUFACTURER'], 1982, 'Mรผnsterhausen, Germany'), ('Premier Rides', ['MANUFACTURER'], 1994, 'Baltimore, MD, USA'), ('S&S Worldwide', ['MANUFACTURER'], 1994, 'Logan, UT, USA'), ] # Ride designers designers_data = [ ('Werner Stengel', ['DESIGNER'], 1965, 'Munich, Germany'), ('Alan Schilke', ['DESIGNER'], 1990, 'Hayden, ID, USA'), ('John Wardley', ['DESIGNER'], 1970, 'London, UK'), ] all_company_data = operators_data + manufacturers_data + designers_data for name, roles, founded_year, location in all_company_data: # Determine which Company model to use based on roles if 'OPERATOR' in roles or 'PROPERTY_OWNER' in roles: # Use ParkCompany for park operators and property owners company, created = ParkCompany.objects.get_or_create( name=name, defaults={ 'slug': slugify(name), 'roles': roles, 'founded_year': founded_year, 'description': f"{name} is a leading {'park operator' if 'OPERATOR' in roles else 'property owner'} in the theme park industry.", 'website': f"https://{slugify(name).replace('-', '')}.com", 'parks_count': random.randint(1, 20) if 'OPERATOR' in roles else 0, 'rides_count': random.randint(10, 500) if 'MANUFACTURER' in roles else 0, } ) else: # Use RideCompany for manufacturers and designers company, created = RideCompany.objects.get_or_create( name=name, defaults={ 'slug': slugify(name), 'roles': roles, 'founded_date': date(founded_year, 1, 1) if founded_year else None, 'description': f"{name} is a leading {'ride manufacturer' if 'MANUFACTURER' in roles else 'ride designer'} in the theme park industry.", 'website': f"https://{slugify(name).replace('-', '')}.com", 'rides_count': random.randint(10, 500) if 'MANUFACTURER' in roles else 0, 'coasters_count': random.randint(5, 100) if 'MANUFACTURER' in roles else 0, } ) # Create headquarters if company was created and is a ParkCompany if created and isinstance(company, ParkCompany): city, state_country = location.rsplit(', ', 1) if ', ' in city: city, state = city.split(', ') country = state_country else: state = '' country = state_country CompanyHeadquarters.objects.get_or_create( company=company, defaults={ 'city': city, 'state_province': state, 'country': country, 'street_address': f"{random.randint(100, 9999)} {random.choice(['Main', 'Park', 'Industry', 'Corporate'])} {random.choice(['St', 'Ave', 'Blvd', 'Dr'])}", 'postal_code': f"{random.randint(10000, 99999)}" if country == 'USA' else '', } ) companies.append(company) # Create additional random companies to reach the target count company_types = ['Theme Parks', 'Amusements', 'Entertainment', 'Rides', 'Design', 'Engineering'] for i in range(len(all_company_data), count): company_type = random.choice(company_types) name = f"{random.choice(['Global', 'International', 'Premier', 'Elite', 'Advanced', 'Creative'])} {company_type} {'Group' if random.random() < 0.5 else 'Corporation'}" roles = [] if 'Theme Parks' in name or 'Amusements' in name: roles = ['OPERATOR'] if random.random() < 0.5: roles.append('PROPERTY_OWNER') elif 'Rides' in name or 'Engineering' in name: roles = ['MANUFACTURER'] elif 'Design' in name: roles = ['DESIGNER'] else: roles = [random.choice(['OPERATOR', 'MANUFACTURER', 'DESIGNER'])] # Use appropriate company model based on roles if 'OPERATOR' in roles or 'PROPERTY_OWNER' in roles: company = ParkCompany.objects.create( name=name, slug=slugify(name), roles=roles, founded_year=random.randint(1950, 2020), description=f"{name} specializes in {'theme park operations' if 'OPERATOR' in roles else 'property ownership'}.", website=f"https://{slugify(name).replace('-', '')}.com", parks_count=random.randint(1, 10) if 'OPERATOR' in roles else 0, rides_count=random.randint(5, 100) if 'MANUFACTURER' in roles else 0, ) else: company = RideCompany.objects.create( name=name, slug=slugify(name), roles=roles, founded_date=date(random.randint(1950, 2020), 1, 1), description=f"{name} specializes in {'ride manufacturing' if 'MANUFACTURER' in roles else 'ride design'}.", website=f"https://{slugify(name).replace('-', '')}.com", rides_count=random.randint(5, 100) if 'MANUFACTURER' in roles else 0, coasters_count=random.randint(2, 50) if 'MANUFACTURER' in roles else 0, ) # Create headquarters cities = ['Los Angeles', 'New York', 'Chicago', 'Houston', 'Phoenix', 'Philadelphia', 'San Antonio', 'San Diego', 'Dallas', 'San Jose'] states = ['CA', 'NY', 'IL', 'TX', 'AZ', 'PA', 'TX', 'CA', 'TX', 'CA'] city_state = random.choice(list(zip(cities, states))) CompanyHeadquarters.objects.create( company=company, city=city_state[0], state_province=city_state[1], country='USA', street_address=f"{random.randint(100, 9999)} {random.choice(['Business', 'Corporate', 'Industry', 'Commerce'])} {random.choice(['Pkwy', 'Blvd', 'Dr', 'Way'])}", postal_code=f"{random.randint(10000, 99999)}", ) companies.append(company) self.stdout.write(f' โœ… Created {len(companies)} companies') return companies def create_ride_models(self, count: int, companies: List) -> List[RideModel]: """Create ride models from manufacturers""" self.stdout.write(f'๐ŸŽข Creating {count} ride models...') manufacturers = [c for c in companies if 'MANUFACTURER' in c.roles] if not manufacturers: self.stdout.write(' โš ๏ธ No manufacturers found, skipping ride models') return [] ride_models = [] # Famous ride models famous_models = [ ('Dive Coaster', 'RC', 'Bolliger & Mabillard', 'Vertical drop roller coaster with holding brake'), ('Hyper Coaster', 'RC', 'Bolliger & Mabillard', 'High-speed out-and-back roller coaster'), ('Wing Coaster', 'RC', 'Bolliger & Mabillard', 'Seats positioned on sides of track'), ('Accelerator Coaster', 'RC', 'Intamin', 'Hydraulic launch roller coaster'), ('Mega Coaster', 'RC', 'Intamin', 'High-speed steel roller coaster'), ('Boomerang', 'RC', 'Vekoma', 'Shuttle roller coaster with inversions'), ('SLC', 'RC', 'Vekoma', 'Suspended Looping Coaster'), ('I-Box Track', 'RC', 'Rocky Mountain Construction', 'Steel track on wooden structure'), ('Raptor Track', 'RC', 'Rocky Mountain Construction', 'Single rail roller coaster'), ('BigDipper', 'RC', 'Mack Rides', 'Family roller coaster'), ('Launched Coaster', 'RC', 'Mack Rides', 'LSM launched roller coaster'), ('Infinity Coaster', 'RC', 'Gerstlauer', 'Compact looping roller coaster'), ('Sky Rocket II', 'RC', 'Premier Rides', 'Compact launched roller coaster'), ('Air Coaster', 'RC', 'S&S Worldwide', 'Compressed air launched coaster'), ('Dark Ride System', 'DR', 'Mack Rides', 'Trackless dark ride vehicles'), ('Omnimover', 'DR', 'Walt Disney Company', 'Continuous loading dark ride system'), ('Log Flume', 'WR', 'Mack Rides', 'Water ride with drops'), ('Rapids Ride', 'WR', 'Intamin', 'Whitewater rafting experience'), ('Drop Tower', 'FR', 'Intamin', 'Vertical drop ride'), ('Gyro Drop', 'FR', 'Intamin', 'Tilting drop tower'), ] for model_name, category, manufacturer_name, description in famous_models: manufacturer = next((c for c in manufacturers if manufacturer_name in c.name), None) if not manufacturer: manufacturer = random.choice(manufacturers) ride_model, created = RideModel.objects.get_or_create( name=model_name, manufacturer=manufacturer, defaults={ 'description': description, 'category': category, 'first_installation_year': random.randint(1980, 2020), 'is_discontinued': random.choice([True, False]), 'target_market': random.choice(['FAMILY', 'THRILL', 'EXTREME']), 'typical_height_range_min_ft': random.randint(50, 200) if category == 'RC' else random.randint(20, 100), 'typical_height_range_max_ft': random.randint(200, 400) if category == 'RC' else random.randint(100, 200), 'typical_speed_range_min_mph': random.randint(20, 60) if category == 'RC' else random.randint(5, 30), 'typical_speed_range_max_mph': random.randint(60, 120) if category == 'RC' else random.randint(30, 60), 'typical_capacity_range_min': random.randint(500, 1000), 'typical_capacity_range_max': random.randint(1000, 2000), 'track_type': random.choice(['Steel', 'Wood', 'Hybrid']) if category == 'RC' else 'Steel', 'support_structure': random.choice(['Steel', 'Wood', 'Concrete']), 'train_configuration': f"{random.randint(2, 4)} trains, {random.randint(6, 8)} cars per train", 'restraint_system': random.choice(['Over-shoulder', 'Lap bar', 'Vest', 'None']), 'notable_features': 'High-speed elements, smooth ride experience', 'total_installations': random.randint(1, 50), } ) # Create technical specs if model exists if category == 'RC' and RideModelTechnicalSpec: specs = [ ('DIMENSIONS', 'Track Length', f"{random.randint(2000, 8000)}", 'ft'), ('PERFORMANCE', 'Max G-Force', f"{random.uniform(3.0, 5.0):.1f}", 'G'), ('CAPACITY', 'Riders per Train', f"{random.randint(20, 32)}", 'people'), ('SAFETY', 'Block Zones', f"{random.randint(4, 8)}", 'zones'), ] for spec_category, spec_name, spec_value, spec_unit in specs: RideModelTechnicalSpec.objects.create( ride_model=ride_model, spec_category=spec_category, spec_name=spec_name, spec_value=spec_value, spec_unit=spec_unit, ) # Create variants for some models if model exists if random.random() < 0.3 and RideModelVariant: variant_names = ['Compact', 'Extended', 'Family', 'Extreme', 'Custom'] variant_name = random.choice(variant_names) RideModelVariant.objects.create( ride_model=ride_model, name=f"{variant_name} Version", description=f"Modified version of {model_name} for {variant_name.lower()} installations", distinguishing_features=f"Optimized for {variant_name.lower()} market segment", ) ride_models.append(ride_model) # Create additional random models model_types = ['Coaster', 'Ride', 'System', 'Experience', 'Adventure'] prefixes = ['Mega', 'Super', 'Ultra', 'Hyper', 'Giga', 'Extreme', 'Family', 'Junior'] for i in range(len(famous_models), count): manufacturer = random.choice(manufacturers) category = random.choice(['RC', 'DR', 'FR', 'WR', 'TR']) model_name = f"{random.choice(prefixes)} {random.choice(model_types)}" ride_model = RideModel.objects.create( name=model_name, manufacturer=manufacturer, description=f"Advanced {model_name.lower()} system designed for modern theme parks", category=category, first_installation_year=random.randint(1990, 2023), is_discontinued=random.choice([True, False]), target_market=random.choice(['FAMILY', 'THRILL', 'EXTREME', 'ALL_AGES']), typical_height_range_min_ft=random.randint(20, 150), typical_height_range_max_ft=random.randint(150, 350), typical_speed_range_min_mph=random.randint(10, 50), typical_speed_range_max_mph=random.randint(50, 100), typical_capacity_range_min=random.randint(400, 800), typical_capacity_range_max=random.randint(800, 1800), track_type=random.choice(['Steel', 'Wood', 'Hybrid', 'Trackless']), support_structure=random.choice(['Steel', 'Wood', 'Concrete', 'Hybrid']), train_configuration=f"{random.randint(1, 3)} trains, {random.randint(4, 12)} cars per train", restraint_system=random.choice(['Over-shoulder', 'Lap bar', 'Vest', 'Seatbelt', 'None']), notable_features=random.choice([ 'Smooth ride experience', 'High-speed elements', 'Family-friendly design', 'Innovative technology', 'Compact footprint' ]), total_installations=random.randint(0, 25), ) ride_models.append(ride_model) self.stdout.write(f' โœ… Created {len(ride_models)} ride models') return ride_models def create_parks(self, count: int, companies: List) -> List[Park]: """Create parks with locations and areas""" self.stdout.write(f'๐Ÿฐ Creating {count} parks...') if count == 0: self.stdout.write(' โ„น๏ธ Skipping park creation (count = 0)') return [] operators = [c for c in companies if 'OPERATOR' in c.roles] property_owners = [c for c in companies if 'PROPERTY_OWNER' in c.roles] if not operators: raise CommandError('No operators found. Create companies first.') parks = [] # Famous theme parks with timezone information famous_parks = [ ('Magic Kingdom', 'Walt Disney World\'s flagship theme park', 'THEME_PARK', 'OPERATING', date(1971, 10, 1), 107, 'Orlando', 'FL', 'USA', 28.4177, -81.5812, 'America/New_York'), ('Disneyland', 'The original Disney theme park', 'THEME_PARK', 'OPERATING', date(1955, 7, 17), 85, 'Anaheim', 'CA', 'USA', 33.8121, -117.9190, 'America/Los_Angeles'), ('Universal Studios Hollywood', 'Movie studio and theme park', 'THEME_PARK', 'OPERATING', date(1964, 7, 15), 415, 'Universal City', 'CA', 'USA', 34.1381, -118.3534, 'America/Los_Angeles'), ('Cedar Point', 'Roller coaster capital of the world', 'AMUSEMENT_PARK', 'OPERATING', date(1870, 5, 30), 364, 'Sandusky', 'OH', 'USA', 41.4814, -82.6838, 'America/New_York'), ('Six Flags Magic Mountain', 'Thrill capital of the world', 'THEME_PARK', 'OPERATING', date(1971, 5, 29), 262, 'Valencia', 'CA', 'USA', 34.4244, -118.5969, 'America/Los_Angeles'), ('Knott\'s Berry Farm', 'America\'s first theme park', 'THEME_PARK', 'OPERATING', date(1920, 6, 1), 57, 'Buena Park', 'CA', 'USA', 33.8442, -117.9981, 'America/Los_Angeles'), ('Busch Gardens Tampa', 'African-themed adventure park', 'THEME_PARK', 'OPERATING', date(1959, 3, 31), 335, 'Tampa', 'FL', 'USA', 28.0373, -82.4194, 'America/New_York'), ('SeaWorld Orlando', 'Marine life theme park', 'THEME_PARK', 'OPERATING', date(1973, 12, 15), 200, 'Orlando', 'FL', 'USA', 28.4110, -81.4610, 'America/New_York'), ] for park_name, description, park_type, status, opening_date, size_acres, city, state, country, lat, lng, timezone_str in famous_parks: # Find appropriate operator operator = None if 'Disney' in park_name: operator = next((c for c in operators if 'Disney' in c.name), None) elif 'Universal' in park_name: operator = next((c for c in operators if 'Universal' in c.name), None) elif 'Cedar Point' in park_name: operator = next((c for c in operators if 'Cedar Fair' in c.name), None) elif 'Six Flags' in park_name: operator = next((c for c in operators if 'Six Flags' in c.name), None) elif 'Knott' in park_name: operator = next((c for c in operators if 'Knott' in c.name), None) elif 'Busch' in park_name: operator = next((c for c in operators if 'Busch' in c.name), None) elif 'SeaWorld' in park_name: operator = next((c for c in operators if 'SeaWorld' in c.name), None) if not operator: operator = random.choice(operators) # Find property owner (could be same as operator) property_owner = None if property_owners and random.random() < 0.7: property_owner = random.choice(property_owners) # Use get_or_create to avoid duplicates park, created = Park.objects.get_or_create( name=park_name, defaults={ 'description': description, 'park_type': park_type, 'status': status, 'opening_date': opening_date, 'size_acres': Decimal(str(size_acres)), 'operator': operator, 'property_owner': property_owner, 'average_rating': Decimal(str(random.uniform(7.5, 9.5))), 'ride_count': random.randint(20, 60), 'coaster_count': random.randint(5, 20), 'timezone': timezone_str, } ) if not created: self.stdout.write(f' โ„น๏ธ Using existing park: {park_name}') # Create park location only if it doesn't exist location_exists = False try: location_exists = hasattr(park, 'location') and park.location is not None except Exception: location_exists = False if created or not location_exists: ParkLocation.objects.get_or_create( park=park, defaults={ 'point': Point(lng, lat), 'street_address': f"{random.randint(100, 9999)} {park_name} Dr", 'city': city, 'state': state, 'country': country, 'postal_code': f"{random.randint(10000, 99999)}" if country == 'USA' else '', } ) # Create park areas only if park was created if created: area_names = ['Main Street', 'Fantasyland', 'Tomorrowland', 'Adventureland', 'Frontierland'] for area_name in random.sample(area_names, random.randint(2, 4)): ParkArea.objects.get_or_create( park=park, name=area_name, defaults={ 'description': f"Themed area within {park_name}", } ) parks.append(park) # Create additional random parks park_types = ['THEME_PARK', 'AMUSEMENT_PARK', 'WATER_PARK', 'FAMILY_ENTERTAINMENT_CENTER'] cities_data = [ ('Los Angeles', 'CA', 'USA', 34.0522, -118.2437), ('New York', 'NY', 'USA', 40.7128, -74.0060), ('Chicago', 'IL', 'USA', 41.8781, -87.6298), ('Houston', 'TX', 'USA', 29.7604, -95.3698), ('Phoenix', 'AZ', 'USA', 33.4484, -112.0740), ('Philadelphia', 'PA', 'USA', 39.9526, -75.1652), ('San Antonio', 'TX', 'USA', 29.4241, -98.4936), ('San Diego', 'CA', 'USA', 32.7157, -117.1611), ] for i in range(len(famous_parks), count): park_type = random.choice(park_types) # Make park names more unique by adding a number park_name = f"{random.choice(['Adventure', 'Magic', 'Wonder', 'Fantasy', 'Thrill', 'Family'])} {random.choice(['World', 'Land', 'Park', 'Kingdom', 'Gardens'])} {i + 1}" operator = random.choice(operators) property_owner = random.choice(property_owners) if property_owners and random.random() < 0.5 else None city, state, country, lat, lng = random.choice(cities_data) # Determine timezone based on state timezone_map = { 'CA': 'America/Los_Angeles', 'NY': 'America/New_York', 'IL': 'America/Chicago', 'TX': 'America/Chicago', 'AZ': 'America/Phoenix', 'PA': 'America/New_York', } park_timezone = timezone_map.get(state, 'America/New_York') park = Park.objects.create( name=park_name, description=f"Exciting {park_type.lower().replace('_', ' ')} featuring thrilling rides and family entertainment", park_type=park_type, status=random.choice(['OPERATING', 'OPERATING', 'OPERATING', 'CLOSED_TEMP']), opening_date=date(random.randint(1950, 2020), random.randint(1, 12), random.randint(1, 28)), size_acres=Decimal(str(random.randint(50, 500))), operator=operator, property_owner=property_owner, average_rating=Decimal(str(random.uniform(6.0, 9.0))), ride_count=random.randint(10, 40), coaster_count=random.randint(2, 15), timezone=park_timezone, ) # Create park location with slight coordinate variation lat_offset = random.uniform(-0.1, 0.1) lng_offset = random.uniform(-0.1, 0.1) ParkLocation.objects.create( park=park, point=Point(lng + lng_offset, lat + lat_offset), street_address=f"{random.randint(100, 9999)} {random.choice(['Park', 'Theme', 'Fun', 'Adventure'])} {random.choice(['Way', 'Dr', 'Blvd', 'Ave'])}", city=city, state=state, country=country, postal_code=f"{random.randint(10000, 99999)}", ) # Create park areas area_names = ['Main Plaza', 'Adventure Zone', 'Family Area', 'Thrill Section', 'Water World', 'Kids Corner'] for area_name in random.sample(area_names, random.randint(2, 4)): ParkArea.objects.create( park=park, name=area_name, description=f"Themed area within {park_name}", ) parks.append(park) self.stdout.write(f' โœ… Created {len(parks)} parks') return parks def create_rides(self, count: int, parks: List[Park], companies: List, ride_models: List[RideModel]) -> List[Ride]: """Create rides with comprehensive details""" self.stdout.write(f'๐ŸŽ  Creating {count} rides...') if not parks: self.stdout.write(' โš ๏ธ No parks found, skipping rides') return [] manufacturers = [c for c in companies if 'MANUFACTURER' in c.roles] designers = [c for c in companies if 'DESIGNER' in c.roles] rides = [] # Famous roller coasters famous_coasters = [ ('Steel Vengeance', 'RC', 'Hybrid steel-wood roller coaster', 'Rocky Mountain Construction'), ('Millennium Force', 'RC', 'Giga coaster with 300-foot drop', 'Intamin'), ('The Beast', 'RC', 'Legendary wooden roller coaster', None), ('Fury 325', 'RC', 'Giga coaster with 325-foot height', 'Bolliger & Mabillard'), ('Lightning Rod', 'RC', 'Launched wooden roller coaster', 'Rocky Mountain Construction'), ('Maverick', 'RC', 'Multi-launch roller coaster', 'Intamin'), ('El Toro', 'RC', 'Wooden roller coaster with steep drops', 'Intamin'), ('Intimidator 305', 'RC', 'Giga coaster with intense elements', 'Intamin'), ('Twisted Timbers', 'RC', 'RMC conversion of wooden coaster', 'Rocky Mountain Construction'), ('Goliath', 'RC', 'Hyper coaster with massive drops', 'Bolliger & Mabillard'), ] # Create famous coasters for coaster_name, category, description, manufacturer_name in famous_coasters: park = random.choice(parks) manufacturer = None if manufacturer_name: manufacturer = next((c for c in manufacturers if manufacturer_name in c.name), None) if not manufacturer and manufacturers: manufacturer = random.choice(manufacturers) designer = random.choice(designers) if designers and random.random() < 0.3 else None ride_model = random.choice(ride_models) if ride_models and random.random() < 0.5 else None # Get park areas for this park park_areas = list(park.areas.all()) park_area = random.choice(park_areas) if park_areas else None ride = Ride.objects.create( name=coaster_name, description=description, park=park, park_area=park_area, category=category, manufacturer=manufacturer, designer=designer, ride_model=ride_model, status=random.choice(['OPERATING'] * 8 + ['CLOSED_TEMP', 'SBNO']), opening_date=date(random.randint(1990, 2023), random.randint(1, 12), random.randint(1, 28)), min_height_in=random.choice([48, 52, 54, 60]), capacity_per_hour=random.randint(800, 1800), ride_duration_seconds=random.randint(90, 240), average_rating=Decimal(str(random.uniform(7.0, 9.5))), ) # Create roller coaster stats if category == 'RC': RollerCoasterStats.objects.create( ride=ride, height_ft=Decimal(str(random.randint(100, 350))), length_ft=Decimal(str(random.randint(2000, 8000))), speed_mph=Decimal(str(random.randint(45, 120))), inversions=random.randint(0, 8), ride_time_seconds=random.randint(90, 240), track_type=random.choice(['Steel', 'Wood', 'Hybrid']), track_material=random.choice(['STEEL', 'WOOD', 'HYBRID']), roller_coaster_type=random.choice(['SITDOWN', 'INVERTED', 'WING', 'DIVE', 'FLYING']), max_drop_height_ft=Decimal(str(random.randint(80, 300))), propulsion_system=random.choice(['CHAIN', 'LSM', 'HYDRAULIC']), train_style=random.choice(['Traditional', 'Floorless', 'Wing', 'Flying']), trains_count=random.randint(2, 4), cars_per_train=random.randint(6, 8), seats_per_car=random.randint(2, 4), ) rides.append(ride) # Create additional random rides ride_names = [ 'Thunder Mountain', 'Space Coaster', 'Wild Eagle', 'Dragon Fire', 'Phoenix Rising', 'Storm Runner', 'Lightning Strike', 'Tornado Alley', 'Hurricane Force', 'Cyclone', 'Viper', 'Cobra', 'Rattlesnake', 'Sidewinder', 'Diamondback', 'Copperhead', 'Banshee', 'Valkyrie', 'Griffon', 'Falcon', 'Eagle\'s Flight', 'Soaring Heights' ] categories = ['RC', 'DR', 'FR', 'WR', 'TR', 'OT'] for i in range(len(famous_coasters), count): park = random.choice(parks) park_areas = list(park.areas.all()) park_area = random.choice(park_areas) if park_areas else None ride_name = random.choice(ride_names) category = random.choice(categories) manufacturer = random.choice(manufacturers) if manufacturers and random.random() < 0.7 else None designer = random.choice(designers) if designers and random.random() < 0.2 else None ride_model = random.choice(ride_models) if ride_models and random.random() < 0.4 else None ride = Ride.objects.create( name=ride_name, description=f"Exciting {category} ride with thrilling elements and smooth operation", park=park, park_area=park_area, category=category, manufacturer=manufacturer, designer=designer, ride_model=ride_model, status=random.choice(['OPERATING'] * 9 + ['CLOSED_TEMP']), opening_date=date(random.randint(1980, 2023), random.randint(1, 12), random.randint(1, 28)), min_height_in=random.choice([36, 42, 48, 52, 54]) if category != 'DR' else None, capacity_per_hour=random.randint(400, 2000), ride_duration_seconds=random.randint(60, 300), average_rating=Decimal(str(random.uniform(6.0, 9.0))), ) # Create roller coaster stats for RC category if category == 'RC': RollerCoasterStats.objects.create( ride=ride, height_ft=Decimal(str(random.randint(50, 300))), length_ft=Decimal(str(random.randint(1500, 6000))), speed_mph=Decimal(str(random.randint(25, 100))), inversions=random.randint(0, 6), ride_time_seconds=random.randint(90, 180), track_type=random.choice(['Steel', 'Wood']), track_material=random.choice(['STEEL', 'WOOD', 'HYBRID']), roller_coaster_type=random.choice(['SITDOWN', 'INVERTED', 'FAMILY', 'WILD_MOUSE']), max_drop_height_ft=Decimal(str(random.randint(40, 250))), propulsion_system=random.choice(['CHAIN', 'LSM', 'GRAVITY']), train_style=random.choice(['Traditional', 'Family', 'Compact']), trains_count=random.randint(1, 3), cars_per_train=random.randint(4, 8), seats_per_car=random.randint(2, 4), ) rides.append(ride) self.stdout.write(f' โœ… Created {len(rides)} rides') return rides def create_reviews(self, count: int, users: List[User], parks: List[Park], rides: List[Ride]) -> None: """Create park and ride reviews""" self.stdout.write(f'๐Ÿ“ Creating {count} reviews...') if not users or (not parks and not rides): self.stdout.write(' โš ๏ธ No users or content found, skipping reviews') return review_texts = [ "Amazing experience! The rides were thrilling and the staff was very friendly.", "Great park with excellent theming. The roller coasters are world-class.", "Had a fantastic time with the family. Something for everyone to enjoy.", "The wait times were reasonable and the food was surprisingly good.", "Incredible engineering and smooth operation. Highly recommended!", "Beautiful park with great attention to detail in every area.", "Thrilling rides and excellent maintenance. Will definitely return!", "Perfect for thrill seekers. The intensity is just right.", "Family-friendly atmosphere with rides for all ages.", "Outstanding park operations and friendly staff throughout.", ] # Create park reviews park_review_count = count // 2 created_park_reviews = 0 attempts = 0 max_attempts = park_review_count * 3 # Allow multiple attempts to avoid infinite loops while created_park_reviews < park_review_count and attempts < max_attempts: if not parks: break user = random.choice(users) park = random.choice(parks) attempts += 1 # Use get_or_create to avoid duplicates review, created = ParkReview.objects.get_or_create( user=user, park=park, defaults={ 'rating': random.randint(6, 10), 'title': f"Great visit to {park.name}", 'content': random.choice(review_texts), 'is_published': random.choice([True] * 9 + [False]), 'visit_date': date( random.randint(2020, 2024), random.randint(1, 12), random.randint(1, 28) ), } ) if created: created_park_reviews += 1 # Create ride reviews ride_review_count = count - created_park_reviews created_ride_reviews = 0 attempts = 0 max_attempts = ride_review_count * 3 # Allow multiple attempts to avoid infinite loops while created_ride_reviews < ride_review_count and attempts < max_attempts: if not rides: break user = random.choice(users) ride = random.choice(rides) attempts += 1 # Use get_or_create to avoid duplicates review, created = RideReview.objects.get_or_create( user=user, ride=ride, defaults={ 'rating': random.randint(6, 10), 'title': f"Awesome ride - {ride.name}", 'content': random.choice(review_texts), 'is_published': random.choice([True] * 9 + [False]), 'visit_date': date( random.randint(2020, 2024), random.randint(1, 12), random.randint(1, 28) ), } ) if created: created_ride_reviews += 1 self.stdout.write(f' โœ… Created {count} reviews') def create_top_lists(self, users: List[User], parks: List[Park], rides: List[Ride]) -> None: """Create user top lists""" self.stdout.write('๐Ÿ“‹ Creating top lists...') if not users: self.stdout.write(' โš ๏ธ No users found, skipping top lists') return list_count = 0 # Create top lists for some users for user in random.sample(users, min(len(users), 10)): # Create roller coaster top list if rides: coasters = [r for r in rides if r.category == 'RC'] if coasters: top_list = TopList.objects.create( user=user, title=f"{user.get_display_name()}'s Top Roller Coasters", category="RC", description="My favorite roller coasters ranked by thrill and experience", ) # Add items to the list for rank, coaster in enumerate(random.sample(coasters, min(len(coasters), 10)), 1): from django.contrib.contenttypes.models import ContentType content_type = ContentType.objects.get_for_model(coaster) TopListItem.objects.create( top_list=top_list, content_type=content_type, object_id=coaster.pk, rank=rank, notes=f"Incredible {coaster.category} experience at {coaster.park.name}", ) list_count += 1 # Create park top list if parks and random.random() < 0.5: top_list = TopList.objects.create( user=user, title=f"{user.get_display_name()}'s Favorite Parks", category="PK", description="Theme parks that provide the best overall experience", ) # Add items to the list for rank, park in enumerate(random.sample(parks, min(len(parks), 5)), 1): from django.contrib.contenttypes.models import ContentType content_type = ContentType.objects.get_for_model(park) TopListItem.objects.create( top_list=top_list, content_type=content_type, object_id=park.pk, rank=rank, notes=f"Amazing park with great {park.park_type.lower().replace('_', ' ')} atmosphere", ) list_count += 1 self.stdout.write(f' โœ… Created {list_count} top lists') def create_notifications(self, users: List[User]) -> None: """Create sample notifications for users""" self.stdout.write('๐Ÿ”” Creating notifications...') if not users: self.stdout.write(' โš ๏ธ No users found, skipping notifications') return notification_count = 0 notification_types = [ ("submission_approved", "Your park submission has been approved!", "Great news! Your submission for Adventure Park has been approved and is now live."), ("review_helpful", "Someone found your review helpful", "Your review of Steel Vengeance was marked as helpful by another user."), ("system_announcement", "New features available", "Check out our new ride comparison tool and enhanced search filters."), ("achievement_unlocked", "Achievement unlocked!", "Congratulations! You've unlocked the 'Coaster Enthusiast' achievement."), ] # Create notifications for random users for user in random.sample(users, min(len(users), 15)): for _ in range(random.randint(1, 3)): notification_type, title, message = random.choice(notification_types) UserNotification.objects.create( user=user, notification_type=notification_type, title=title, message=message, priority=random.choice(['normal'] * 3 + ['high']), is_read=random.choice([True, False]), email_sent=random.choice([True, False]), push_sent=random.choice([True, False]), ) notification_count += 1 self.stdout.write(f' โœ… Created {notification_count} notifications') def create_moderation_data(self, users: List[User], parks: List[Park], rides: List[Ride]) -> None: """Create moderation queue and actions""" self.stdout.write('๐Ÿ›ก๏ธ Creating moderation data...') if not ModerationQueue or not ModerationAction: self.stdout.write(' โš ๏ธ Moderation models not available, skipping') return if not users or (not parks and not rides): self.stdout.write(' โš ๏ธ No users or content found, skipping moderation data') return # This would create sample moderation queue items and actions # Implementation depends on the actual moderation models structure self.stdout.write(' โœ… Moderation data creation skipped (models not fully defined)') def create_photos(self, parks: List[Park], rides: List[Ride], ride_models: List[RideModel]) -> None: """Create sample photo records""" self.stdout.write('๐Ÿ“ธ Creating photo records...') if not CloudflareImage: self.stdout.write(' โš ๏ธ CloudflareImage model not available, skipping photo creation') return # Since we don't have actual Cloudflare images, we'll skip photo creation # In a real scenario, you would need actual CloudflareImage instances self.stdout.write(' โš ๏ธ Photo creation skipped (requires actual CloudflareImage instances)') self.stdout.write(' โ„น๏ธ To create photos, you need to upload actual images to Cloudflare first') def create_rankings(self, rides: List[Ride]) -> None: """Create ride rankings if model exists""" self.stdout.write('๐Ÿ† Creating ride rankings...') if not RideRanking: self.stdout.write(' โš ๏ธ RideRanking model not available, skipping') return if not rides: self.stdout.write(' โš ๏ธ No rides found, skipping rankings') return # This would create sample ride rankings # Implementation depends on the actual RideRanking model structure self.stdout.write(' โœ… Ride rankings creation skipped (model structure not fully defined)') def print_summary(self) -> None: """Print a summary of created data""" self.stdout.write('\n๐Ÿ“Š Data Seeding Summary:') self.stdout.write('=' * 50) # Count all created objects counts = { 'Users': User.objects.count(), 'Park Companies': ParkCompany.objects.count(), 'Ride Companies': RideCompany.objects.count(), 'Parks': Park.objects.count(), 'Rides': Ride.objects.count(), 'Ride Models': RideModel.objects.count(), 'Park Reviews': ParkReview.objects.count(), 'Ride Reviews': RideReview.objects.count(), 'Top Lists': TopList.objects.count(), 'Notifications': UserNotification.objects.count(), 'Park Photos': ParkPhoto.objects.count(), 'Ride Photos': RidePhoto.objects.count(), } for model_name, count in counts.items(): self.stdout.write(f' {model_name}: {count}') self.stdout.write('=' * 50) self.stdout.write('๐ŸŽ‰ Seeding completed! Your ThrillWiki database is ready for testing.')