Files
thrillwiki_django_no_react/apps/api/management/commands/seed_data.py
2025-09-21 20:19:12 -04:00

1212 lines
57 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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.')