feat: Implement MFA authentication, add ride statistics model, and update various services, APIs, and tests across the application.

This commit is contained in:
pacnpal
2025-12-28 17:32:53 -05:00
parent aa56c46c27
commit c95f99ca10
452 changed files with 7948 additions and 6073 deletions

View File

@@ -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.')