mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 02:31:08 -05:00
1212 lines
57 KiB
Python
1212 lines
57 KiB
Python
"""
|
||
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.')
|