mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-30 17:27:16 -05:00
feat: Implement MFA authentication, add ride statistics model, and update various services, APIs, and tests across the application.
This commit is contained in:
@@ -14,32 +14,25 @@ Usage:
|
||||
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.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction
|
||||
from django.utils.text import slugify
|
||||
|
||||
# Import all models
|
||||
from apps.accounts.models import (
|
||||
User, UserProfile, 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.accounts.models import NotificationPreference, UserDeletionRequest, UserNotification, UserProfile
|
||||
from apps.core.history import HistoricalSlug
|
||||
from apps.parks.models import Park, ParkArea, ParkLocation, ParkPhoto, ParkReview
|
||||
from apps.parks.models.companies import Company as ParkCompany
|
||||
from apps.parks.models.companies import CompanyHeadquarters
|
||||
from apps.rides.models import Ride, RideLocation, RideModel, RidePhoto, RideReview, RollerCoasterStats
|
||||
from apps.rides.models.company import Company as RideCompany
|
||||
|
||||
# Try to import optional models that may not exist
|
||||
try:
|
||||
from apps.rides.models import RideModelVariant, RideModelPhoto, RideModelTechnicalSpec
|
||||
from apps.rides.models import RideModelPhoto, RideModelTechnicalSpec, RideModelVariant
|
||||
except ImportError:
|
||||
RideModelVariant = None
|
||||
RideModelPhoto = None
|
||||
@@ -51,7 +44,7 @@ except ImportError:
|
||||
RideRanking = None
|
||||
|
||||
try:
|
||||
from apps.moderation.models import ModerationQueue, ModerationAction
|
||||
from apps.moderation.models import ModerationAction, ModerationQueue
|
||||
except ImportError:
|
||||
ModerationQueue = None
|
||||
ModerationAction = None
|
||||
@@ -125,16 +118,16 @@ class Command(BaseCommand):
|
||||
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_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)
|
||||
|
||||
@@ -146,26 +139,26 @@ class Command(BaseCommand):
|
||||
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)
|
||||
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)
|
||||
@@ -179,7 +172,7 @@ class Command(BaseCommand):
|
||||
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()
|
||||
@@ -193,12 +186,12 @@ class Command(BaseCommand):
|
||||
# Continue with other models
|
||||
continue
|
||||
|
||||
def create_users(self, count: int) -> List[User]:
|
||||
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',
|
||||
@@ -216,7 +209,7 @@ class Command(BaseCommand):
|
||||
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',
|
||||
@@ -233,7 +226,7 @@ class Command(BaseCommand):
|
||||
moderator.set_password('mod123')
|
||||
moderator.save()
|
||||
users.append(moderator)
|
||||
|
||||
|
||||
# Sample user data
|
||||
first_names = [
|
||||
'Alex', 'Jordan', 'Taylor', 'Casey', 'Morgan', 'Riley', 'Avery', 'Quinn',
|
||||
@@ -241,23 +234,23 @@ class Command(BaseCommand):
|
||||
'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
|
||||
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,
|
||||
@@ -275,7 +268,7 @@ class Command(BaseCommand):
|
||||
two_factor_enabled=random.choice([True, False]),
|
||||
login_notifications=random.choice([True, False]),
|
||||
)
|
||||
|
||||
|
||||
# Create detailed notification preferences
|
||||
user.notification_preferences = {
|
||||
'email': {
|
||||
@@ -295,7 +288,7 @@ class Command(BaseCommand):
|
||||
}
|
||||
}
|
||||
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!"
|
||||
@@ -304,7 +297,7 @@ class Command(BaseCommand):
|
||||
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}"
|
||||
@@ -312,19 +305,19 @@ class Command(BaseCommand):
|
||||
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:
|
||||
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'),
|
||||
@@ -335,7 +328,7 @@ class Command(BaseCommand):
|
||||
('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'),
|
||||
@@ -347,16 +340,16 @@ class Command(BaseCommand):
|
||||
('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:
|
||||
@@ -387,7 +380,7 @@ class Command(BaseCommand):
|
||||
'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)
|
||||
@@ -397,7 +390,7 @@ class Command(BaseCommand):
|
||||
else:
|
||||
state = ''
|
||||
country = state_country
|
||||
|
||||
|
||||
CompanyHeadquarters.objects.get_or_create(
|
||||
company=company,
|
||||
defaults={
|
||||
@@ -408,16 +401,16 @@ class Command(BaseCommand):
|
||||
'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):
|
||||
|
||||
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']
|
||||
@@ -429,7 +422,7 @@ class Command(BaseCommand):
|
||||
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(
|
||||
@@ -453,12 +446,12 @@ class Command(BaseCommand):
|
||||
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)))
|
||||
|
||||
city_state = random.choice(list(zip(cities, states, strict=False)))
|
||||
|
||||
CompanyHeadquarters.objects.create(
|
||||
company=company,
|
||||
city=city_state[0],
|
||||
@@ -467,23 +460,23 @@ class Command(BaseCommand):
|
||||
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]:
|
||||
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'),
|
||||
@@ -507,12 +500,12 @@ class Command(BaseCommand):
|
||||
('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,
|
||||
@@ -536,7 +529,7 @@ class Command(BaseCommand):
|
||||
'total_installations': random.randint(1, 50),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# Create technical specs if model exists
|
||||
if category == 'RC' and RideModelTechnicalSpec:
|
||||
specs = [
|
||||
@@ -545,7 +538,7 @@ class Command(BaseCommand):
|
||||
('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,
|
||||
@@ -554,31 +547,31 @@ class Command(BaseCommand):
|
||||
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):
|
||||
|
||||
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,
|
||||
@@ -606,31 +599,31 @@ class Command(BaseCommand):
|
||||
]),
|
||||
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]:
|
||||
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',
|
||||
('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'),
|
||||
@@ -647,7 +640,7 @@ class Command(BaseCommand):
|
||||
('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
|
||||
@@ -665,15 +658,15 @@ class Command(BaseCommand):
|
||||
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,
|
||||
@@ -693,14 +686,14 @@ class Command(BaseCommand):
|
||||
)
|
||||
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,
|
||||
@@ -713,7 +706,7 @@ class Command(BaseCommand):
|
||||
'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']
|
||||
@@ -725,9 +718,9 @@ class Command(BaseCommand):
|
||||
'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 = [
|
||||
@@ -740,28 +733,28 @@ class Command(BaseCommand):
|
||||
('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',
|
||||
'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",
|
||||
@@ -776,11 +769,11 @@ class Command(BaseCommand):
|
||||
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),
|
||||
@@ -790,7 +783,7 @@ class Command(BaseCommand):
|
||||
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)):
|
||||
@@ -799,25 +792,25 @@ class Command(BaseCommand):
|
||||
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]:
|
||||
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'),
|
||||
@@ -831,7 +824,7 @@ class Command(BaseCommand):
|
||||
('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)
|
||||
@@ -840,14 +833,14 @@ class Command(BaseCommand):
|
||||
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,
|
||||
@@ -864,7 +857,7 @@ class Command(BaseCommand):
|
||||
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(
|
||||
@@ -884,9 +877,9 @@ class Command(BaseCommand):
|
||||
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',
|
||||
@@ -894,21 +887,21 @@ class Command(BaseCommand):
|
||||
'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):
|
||||
|
||||
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",
|
||||
@@ -925,7 +918,7 @@ class Command(BaseCommand):
|
||||
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(
|
||||
@@ -945,20 +938,20 @@ class Command(BaseCommand):
|
||||
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:
|
||||
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.",
|
||||
@@ -971,21 +964,21 @@ class Command(BaseCommand):
|
||||
"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,
|
||||
@@ -1002,24 +995,24 @@ class Command(BaseCommand):
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
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,
|
||||
@@ -1036,36 +1029,36 @@ class Command(BaseCommand):
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if created:
|
||||
created_ride_reviews += 1
|
||||
|
||||
|
||||
self.stdout.write(f' ✅ Created {count} reviews')
|
||||
|
||||
|
||||
|
||||
def create_notifications(self, users: List[User]) -> None:
|
||||
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,
|
||||
@@ -1077,50 +1070,50 @@ class Command(BaseCommand):
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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)')
|
||||
@@ -1129,7 +1122,7 @@ class Command(BaseCommand):
|
||||
"""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(),
|
||||
@@ -1145,9 +1138,9 @@ class Command(BaseCommand):
|
||||
'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.')
|
||||
|
||||
Reference in New Issue
Block a user