major changes, including tailwind v4

This commit is contained in:
pacnpal
2025-08-15 12:24:20 -04:00
parent f6c8e0e25c
commit da7c7e3381
261 changed files with 22783 additions and 10465 deletions

View File

@@ -1,306 +0,0 @@
import json
import os
import shutil
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.files.temp import NamedTemporaryFile
from django.core.files import File
import requests
from parks.models import Park
from rides.models import Ride, RollerCoasterStats
from operators.models import Operator
from manufacturers.models import Manufacturer
from reviews.models import Review
from media.models import Photo
from django.contrib.auth.models import Permission
from datetime import datetime, timedelta
import secrets
User = get_user_model()
# Park coordinates mapping
PARK_COORDINATES = {
"Walt Disney World Magic Kingdom": {
"latitude": "28.418778",
"longitude": "-81.581212",
"street_address": "1180 Seven Seas Dr",
"city": "Orlando",
"state": "Florida",
"postal_code": "32836"
},
"Cedar Point": {
"latitude": "41.482207",
"longitude": "-82.683523",
"street_address": "1 Cedar Point Dr",
"city": "Sandusky",
"state": "Ohio",
"postal_code": "44870"
},
"Universal's Islands of Adventure": {
"latitude": "28.470891",
"longitude": "-81.471756",
"street_address": "6000 Universal Blvd",
"city": "Orlando",
"state": "Florida",
"postal_code": "32819"
},
"Alton Towers": {
"latitude": "52.988889",
"longitude": "-1.892778",
"street_address": "Farley Ln",
"city": "Alton",
"state": "Staffordshire",
"postal_code": "ST10 4DB"
},
"Europa-Park": {
"latitude": "48.266031",
"longitude": "7.722044",
"street_address": "Europa-Park-Straße 2",
"city": "Rust",
"state": "Baden-Württemberg",
"postal_code": "77977"
}
}
class Command(BaseCommand):
help = "Seeds the database with initial data"
def handle(self, *args, **kwargs):
self.stdout.write("Starting database seed...")
# Clean up media directory
self.stdout.write("Cleaning up media directory...")
media_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'media')
if os.path.exists(media_dir):
for item in os.listdir(media_dir):
if item != '__init__.py': # Preserve __init__.py
item_path = os.path.join(media_dir, item)
if os.path.isdir(item_path):
shutil.rmtree(item_path)
else:
os.remove(item_path)
# Delete all existing data
self.stdout.write("Deleting existing data...")
User.objects.exclude(username='admin').delete() # Delete all users except admin
Park.objects.all().delete()
Ride.objects.all().delete()
Operator.objects.all().delete()
Manufacturer.objects.all().delete()
Review.objects.all().delete()
Photo.objects.all().delete()
# Create users and set permissions
self.create_users()
self.setup_permissions()
# Create parks and rides
self.stdout.write("Creating parks and rides from seed data...")
self.create_companies()
self.create_manufacturers()
self.create_parks_and_rides()
# Create reviews
self.stdout.write("Creating reviews...")
self.create_reviews()
self.stdout.write("Successfully seeded database")
def setup_permissions(self):
"""Set up photo permissions for all users"""
self.stdout.write("Setting up photo permissions...")
# Get photo permissions
photo_content_type = ContentType.objects.get_for_model(Photo)
photo_permissions = Permission.objects.filter(content_type=photo_content_type)
# Update all users
users = User.objects.all()
for user in users:
for perm in photo_permissions:
user.user_permissions.add(perm)
user.save()
self.stdout.write(f"Updated permissions for user: {user.username}")
def create_users(self):
self.stdout.write("Creating users...")
# Try to get admin user
try:
admin = User.objects.get(username="admin")
self.stdout.write("Admin user exists, updating permissions...")
except User.DoesNotExist:
admin = User.objects.create_superuser("admin", "admin@example.com", "admin")
self.stdout.write("Created admin user")
# Create 10 regular users
usernames = [
"thrillseeker1",
"coasterrider2",
"parkfan3",
"adventurer4",
"funseeker5",
"parkexplorer6",
"ridetester7",
"themepark8",
"coaster9",
"parkvisitor10"
]
for username in usernames:
User.objects.create_user(
username=username,
email=f"{username}@example.com",
[PASSWORD-REMOVED]",
)
self.stdout.write(f"Created user: {username}")
def create_companies(self):
self.stdout.write("Creating companies...")
companies = [
"The Walt Disney Company",
"Cedar Fair",
"NBCUniversal",
"Merlin Entertainments",
"Mack Rides",
]
for name in companies:
Operator.objects.create(name=name)
self.stdout.write(f"Created company: {name}")
def create_manufacturers(self):
self.stdout.write("Creating manufacturers...")
manufacturers = [
"Walt Disney Imagineering",
"Bolliger & Mabillard",
"Intamin",
"Rocky Mountain Construction",
"Vekoma",
"Mack Rides",
"Oceaneering International",
]
for name in manufacturers:
Manufacturer.objects.create(name=name)
self.stdout.write(f"Created manufacturer: {name}")
def download_image(self, url):
"""Download image from URL and return as Django File object"""
response = requests.get(url, timeout=60)
if response.status_code == 200:
img_temp = NamedTemporaryFile(delete=True)
img_temp.write(response.content)
img_temp.flush()
return File(img_temp)
return None
def create_parks_and_rides(self):
# Load seed data
with open(os.path.join(os.path.dirname(__file__), "seed_data.json")) as f:
data = json.load(f)
for park_data in data["parks"]:
try:
# Create park with location data
park_coords = PARK_COORDINATES[park_data["name"]]
park = Park.objects.create(
name=park_data["name"],
country=park_data["country"],
opening_date=park_data["opening_date"],
status=park_data["status"],
description=park_data["description"],
website=park_data["website"],
operator=Operator.objects.get(name=park_data["owner"]),
size_acres=park_data["size_acres"],
# Add location fields
latitude=park_coords["latitude"],
longitude=park_coords["longitude"],
street_address=park_coords["street_address"],
city=park_coords["city"],
state=park_coords["state"],
postal_code=park_coords["postal_code"]
)
# Add park photos
for photo_url in park_data["photos"]:
img_file = self.download_image(photo_url)
if img_file:
Photo.objects.create(
image=img_file,
content_type=ContentType.objects.get_for_model(park),
object_id=park.id,
is_primary=True, # First photo is primary
)
# Create rides
for ride_data in park_data["rides"]:
ride = Ride.objects.create(
name=ride_data["name"],
park=park,
category=ride_data["category"],
opening_date=ride_data["opening_date"],
status=ride_data["status"],
manufacturer=Manufacturer.objects.get(
name=ride_data["manufacturer"]
),
description=ride_data["description"],
)
# Add ride photos
for photo_url in ride_data["photos"]:
img_file = self.download_image(photo_url)
if img_file:
Photo.objects.create(
image=img_file,
content_type=ContentType.objects.get_for_model(ride),
object_id=ride.id,
is_primary=True, # First photo is primary
)
# Add coaster stats if present
if "stats" in ride_data:
RollerCoasterStats.objects.create(
ride=ride,
height_ft=ride_data["stats"]["height_ft"],
length_ft=ride_data["stats"]["length_ft"],
speed_mph=ride_data["stats"]["speed_mph"],
inversions=ride_data["stats"]["inversions"],
ride_time_seconds=ride_data["stats"]["ride_time_seconds"],
)
self.stdout.write(f"Created park and rides: {park.name}")
except Exception as e:
self.stdout.write(self.style.ERROR(f"Error creating park {park_data['name']}: {str(e)}"))
continue
def create_reviews(self):
users = list(User.objects.exclude(username="admin"))
parks = list(Park.objects.all())
# Generate random dates within the last year
today = datetime.now().date()
one_year_ago = today - timedelta(days=365)
for park in parks:
# Create 3-5 reviews per park
num_reviews = secrets.SystemRandom().randint(3, 5)
for _ in range(num_reviews):
# Generate random visit date
days_offset = secrets.SystemRandom().randint(0, 365)
visit_date = one_year_ago + timedelta(days=days_offset)
Review.objects.create(
user=secrets.choice(users),
content_type=ContentType.objects.get_for_model(park),
object_id=park.id,
title=f"Great experience at {park.name}",
content="Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
rating=secrets.SystemRandom().randint(7, 10),
visit_date=visit_date,
)
self.stdout.write(f"Created reviews for {park.name}")

View File

@@ -1,9 +1,8 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from operators.models import Operator
from parks.models.companies import Operator
from parks.models import Park, ParkArea
from location.models import Location
from django.contrib.contenttypes.models import ContentType
from parks.models.location import ParkLocation
class Command(BaseCommand):
help = 'Seeds initial park data with major theme parks worldwide'
@@ -218,18 +217,20 @@ class Command(BaseCommand):
# Create location for park
if created:
loc_data = park_data['location']
park_content_type = ContentType.objects.get_for_model(Park)
Location.objects.create(
content_type=park_content_type,
object_id=park.id,
park_location = ParkLocation.objects.create(
park=park,
street_address=loc_data['street_address'],
city=loc_data['city'],
state=loc_data['state'],
country=loc_data['country'],
postal_code=loc_data['postal_code'],
latitude=loc_data['latitude'],
longitude=loc_data['longitude']
postal_code=loc_data['postal_code']
)
# Set coordinates using the helper method
park_location.set_coordinates(
loc_data['latitude'],
loc_data['longitude']
)
park_location.save()
# Create areas for park
for area_data in park_data['areas']:

View File

@@ -1,321 +0,0 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from manufacturers.models import Manufacturer
from parks.models import Park
from rides.models import Ride, RollerCoasterStats
from decimal import Decimal
class Command(BaseCommand):
help = 'Seeds ride data for parks'
def handle(self, *args, **options):
# Create major ride manufacturers
manufacturers_data = [
{
'name': 'Bolliger & Mabillard',
'website': 'https://www.bolligermabillard.com/',
'headquarters': 'Monthey, Switzerland',
'description': 'Known for their smooth steel roller coasters.'
},
{
'name': 'Rocky Mountain Construction',
'website': 'https://www.rockymtnconstruction.com/',
'headquarters': 'Hayden, Idaho, USA',
'description': 'Specialists in hybrid and steel roller coasters.'
},
{
'name': 'Intamin',
'website': 'https://www.intamin.com/',
'headquarters': 'Schaan, Liechtenstein',
'description': 'Creators of record-breaking roller coasters and rides.'
},
{
'name': 'Vekoma',
'website': 'https://www.vekoma.com/',
'headquarters': 'Vlodrop, Netherlands',
'description': 'Manufacturers of various roller coaster types.'
},
{
'name': 'Mack Rides',
'website': 'https://mack-rides.com/',
'headquarters': 'Waldkirch, Germany',
'description': 'Family-owned manufacturer of roller coasters and attractions.'
},
{
'name': 'Sally Dark Rides',
'website': 'https://sallydarkrides.com/',
'headquarters': 'Jacksonville, Florida, USA',
'description': 'Specialists in dark rides and interactive attractions.'
},
{
'name': 'Zamperla',
'website': 'https://www.zamperla.com/',
'headquarters': 'Vicenza, Italy',
'description': 'Manufacturer of family rides and thrill attractions.'
}
]
manufacturers = {}
for mfg_data in manufacturers_data:
manufacturer, created = Manufacturer.objects.get_or_create(
name=mfg_data['name'],
defaults=mfg_data
)
manufacturers[manufacturer.name] = manufacturer
self.stdout.write(f'{"Created" if created else "Found"} manufacturer: {manufacturer.name}')
# Create rides for each park
rides_data = [
# Silver Dollar City Rides
{
'park_name': 'Silver Dollar City',
'rides': [
{
'name': 'Time Traveler',
'manufacturer': 'Mack Rides',
'description': 'The world\'s fastest, steepest, and tallest spinning roller coaster.',
'category': 'RC',
'opening_date': '2018-03-14',
'stats': {
'height_ft': 100,
'length_ft': 3020,
'speed_mph': 50.3,
'inversions': 3,
'track_material': 'STEEL',
'roller_coaster_type': 'SPINNING',
'launch_type': 'LSM'
}
},
{
'name': 'Wildfire',
'manufacturer': 'Bolliger & Mabillard',
'description': 'A multi-looping roller coaster with a 155-foot drop.',
'category': 'RC',
'opening_date': '2001-04-01',
'stats': {
'height_ft': 155,
'length_ft': 3073,
'speed_mph': 66,
'inversions': 5,
'track_material': 'STEEL',
'roller_coaster_type': 'SITDOWN',
'launch_type': 'CHAIN'
}
},
{
'name': 'Fire In The Hole',
'manufacturer': 'Sally Dark Rides',
'description': 'Indoor coaster featuring special effects and storytelling.',
'category': 'DR',
'opening_date': '1972-01-01'
},
{
'name': 'American Plunge',
'manufacturer': 'Intamin',
'description': 'Log flume ride with a 50-foot splashdown.',
'category': 'WR',
'opening_date': '1981-01-01'
}
]
},
# Magic Kingdom Rides
{
'park_name': 'Magic Kingdom',
'rides': [
{
'name': 'Space Mountain',
'manufacturer': 'Vekoma',
'description': 'An indoor roller coaster through space.',
'category': 'RC',
'opening_date': '1975-01-15',
'stats': {
'height_ft': 180,
'length_ft': 3196,
'speed_mph': 27,
'inversions': 0,
'track_material': 'STEEL',
'roller_coaster_type': 'SITDOWN',
'launch_type': 'CHAIN'
}
},
{
'name': 'Haunted Mansion',
'manufacturer': 'Sally Dark Rides',
'description': 'Classic dark ride through a haunted estate.',
'category': 'DR',
'opening_date': '1971-10-01'
},
{
'name': 'Mad Tea Party',
'manufacturer': 'Zamperla',
'description': 'Spinning teacup ride based on Alice in Wonderland.',
'category': 'FR',
'opening_date': '1971-10-01'
},
{
'name': 'Splash Mountain',
'manufacturer': 'Intamin',
'description': 'Log flume ride with multiple drops and animatronics.',
'category': 'WR',
'opening_date': '1992-10-02'
}
]
},
# Cedar Point Rides
{
'park_name': 'Cedar Point',
'rides': [
{
'name': 'Millennium Force',
'manufacturer': 'Intamin',
'description': 'Former world\'s tallest and fastest complete-circuit roller coaster.',
'category': 'RC',
'opening_date': '2000-05-13',
'stats': {
'height_ft': 310,
'length_ft': 6595,
'speed_mph': 93,
'inversions': 0,
'track_material': 'STEEL',
'roller_coaster_type': 'SITDOWN',
'launch_type': 'CABLE'
}
},
{
'name': 'Cedar Downs Racing Derby',
'manufacturer': 'Zamperla',
'description': 'High-speed carousel with racing horses.',
'category': 'FR',
'opening_date': '1967-01-01'
},
{
'name': 'Snake River Falls',
'manufacturer': 'Intamin',
'description': 'Shoot-the-Chutes water ride with an 82-foot drop.',
'category': 'WR',
'opening_date': '1993-05-01'
}
]
},
# Universal Studios Florida Rides
{
'park_name': 'Universal Studios Florida',
'rides': [
{
'name': 'Harry Potter and the Escape from Gringotts',
'manufacturer': 'Intamin',
'description': 'Indoor steel roller coaster with 3D effects.',
'category': 'RC',
'opening_date': '2014-07-08',
'stats': {
'height_ft': 65,
'length_ft': 2000,
'speed_mph': 50,
'inversions': 0,
'track_material': 'STEEL',
'roller_coaster_type': 'SITDOWN',
'launch_type': 'LSM'
}
},
{
'name': 'The Amazing Adventures of Spider-Man',
'manufacturer': 'Sally Dark Rides',
'description': 'groundbreaking 3D dark ride.',
'category': 'DR',
'opening_date': '1999-05-28'
},
{
'name': 'Jurassic World VelociCoaster',
'manufacturer': 'Intamin',
'description': 'Florida\'s fastest and tallest launch coaster.',
'category': 'RC',
'opening_date': '2021-06-10',
'stats': {
'height_ft': 155,
'length_ft': 4700,
'speed_mph': 70,
'inversions': 4,
'track_material': 'STEEL',
'roller_coaster_type': 'SITDOWN',
'launch_type': 'LSM'
}
}
]
},
# SeaWorld Orlando Rides
{
'park_name': 'SeaWorld Orlando',
'rides': [
{
'name': 'Mako',
'manufacturer': 'Bolliger & Mabillard',
'description': 'Orlando\'s tallest, fastest and longest roller coaster.',
'category': 'RC',
'opening_date': '2016-06-10',
'stats': {
'height_ft': 200,
'length_ft': 4760,
'speed_mph': 73,
'inversions': 0,
'track_material': 'STEEL',
'roller_coaster_type': 'SITDOWN',
'launch_type': 'CHAIN'
}
},
{
'name': 'Journey to Atlantis',
'manufacturer': 'Mack Rides',
'description': 'Water coaster combining dark ride elements with splashes.',
'category': 'WR',
'opening_date': '1998-03-01'
},
{
'name': 'Sky Tower',
'manufacturer': 'Intamin',
'description': 'Rotating observation tower providing views of Orlando.',
'category': 'TR',
'opening_date': '1973-12-15'
}
]
}
]
# Create rides and their stats
for park_data in rides_data:
try:
park = Park.objects.get(name=park_data['park_name'])
for ride_data in park_data['rides']:
manufacturer = manufacturers[ride_data['manufacturer']]
ride, created = Ride.objects.get_or_create(
name=ride_data['name'],
park=park,
defaults={
'description': ride_data['description'],
'category': ride_data['category'],
'manufacturer': manufacturer,
'opening_date': ride_data['opening_date'],
'status': 'OPERATING'
}
)
self.stdout.write(f'{"Created" if created else "Found"} ride: {ride.name}')
if created and ride_data.get('stats'):
stats = ride_data['stats']
RollerCoasterStats.objects.create(
ride=ride,
height_ft=stats['height_ft'],
length_ft=stats['length_ft'],
speed_mph=stats['speed_mph'],
inversions=stats['inversions'],
track_material=stats['track_material'],
roller_coaster_type=stats['roller_coaster_type'],
launch_type=stats['launch_type']
)
self.stdout.write(f'Created stats for: {ride.name}')
except Park.DoesNotExist:
self.stdout.write(self.style.WARNING(f'Park not found: {park_data["park_name"]}'))
self.stdout.write(self.style.SUCCESS('Successfully seeded ride data'))

View File

@@ -0,0 +1,119 @@
from django.core.management.base import BaseCommand
from parks.models import Park, ParkLocation
from parks.models.companies import Company
class Command(BaseCommand):
help = 'Test ParkLocation model functionality'
def handle(self, *args, **options):
self.stdout.write("🧪 Testing ParkLocation Model Functionality")
self.stdout.write("=" * 50)
# Create a test company (operator)
operator, created = Company.objects.get_or_create(
name="Test Theme Parks Inc",
defaults={
'slug': 'test-theme-parks-inc',
'roles': ['OPERATOR']
}
)
self.stdout.write(f"✅ Created operator: {operator.name}")
# Create a test park
park, created = Park.objects.get_or_create(
name="Test Magic Kingdom",
defaults={
'slug': 'test-magic-kingdom',
'description': 'A test theme park for location testing',
'operator': operator
}
)
self.stdout.write(f"✅ Created park: {park.name}")
# Create a park location
location, created = ParkLocation.objects.get_or_create(
park=park,
defaults={
'street_address': '1313 Disneyland Dr',
'city': 'Anaheim',
'state': 'California',
'country': 'USA',
'postal_code': '92802',
'highway_exit': 'I-5 Exit 110B',
'parking_notes': 'Large parking structure available',
'seasonal_notes': 'Open year-round'
}
)
self.stdout.write(f"✅ Created location: {location}")
# Test coordinate setting
self.stdout.write("\n🔍 Testing coordinate functionality:")
location.set_coordinates(33.8121, -117.9190) # Disneyland coordinates
location.save()
self.stdout.write(f" Latitude: {location.latitude}")
self.stdout.write(f" Longitude: {location.longitude}")
self.stdout.write(f" Coordinates: {location.coordinates}")
self.stdout.write(f" Formatted Address: {location.formatted_address}")
# Test Park model integration
self.stdout.write("\n🔍 Testing Park model integration:")
self.stdout.write(f" Park formatted location: {park.formatted_location}")
self.stdout.write(f" Park coordinates: {park.coordinates}")
# Create another location for distance testing
operator2, created = Company.objects.get_or_create(
name="Six Flags Entertainment",
defaults={
'slug': 'six-flags-entertainment',
'roles': ['OPERATOR']
}
)
park2, created = Park.objects.get_or_create(
name="Six Flags Magic Mountain",
defaults={
'slug': 'six-flags-magic-mountain',
'description': 'Another test theme park',
'operator': operator2
}
)
location2, created = ParkLocation.objects.get_or_create(
park=park2,
defaults={
'city': 'Valencia',
'state': 'California',
'country': 'USA'
}
)
location2.set_coordinates(34.4244, -118.5971) # Six Flags Magic Mountain coordinates
location2.save()
# Test distance calculation
self.stdout.write("\n🔍 Testing distance calculation:")
distance = location.distance_to(location2)
if distance:
self.stdout.write(f" Distance between parks: {distance:.2f} km")
else:
self.stdout.write(" ❌ Distance calculation failed")
# Test spatial indexing
self.stdout.write("\n🔍 Testing spatial queries:")
try:
from django.contrib.gis.measure import D
from django.contrib.gis.geos import Point
# Find parks within 100km of a point
search_point = Point(-117.9190, 33.8121, srid=4326) # Same as Disneyland
nearby_locations = ParkLocation.objects.filter(
point__distance_lte=(search_point, D(km=100))
)
self.stdout.write(f" Found {nearby_locations.count()} parks within 100km")
for loc in nearby_locations:
self.stdout.write(f" - {loc.park.name} in {loc.city}, {loc.state}")
except Exception as e:
self.stdout.write(f" ⚠️ Spatial queries not fully functional: {e}")
self.stdout.write("\n✅ ParkLocation model tests completed successfully!")