mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-22 07:51:09 -05:00
photos fix
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -9,11 +9,9 @@
|
||||
"description": "The most visited theme park in the world, Magic Kingdom is Walt Disney World's first theme park.",
|
||||
"website": "https://disneyworld.disney.go.com/destinations/magic-kingdom/",
|
||||
"owner": "The Walt Disney Company",
|
||||
"size_acres": 142,
|
||||
"size_acres": "142.00",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Walt_Disney_World_Magic_Kingdom_Cinderella_Castle.jpg/1280px-Walt_Disney_World_Magic_Kingdom_Cinderella_Castle.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/9/96/Magic_Kingdom_Main_Street_USA_Panorama.jpg/1280px-Magic_Kingdom_Main_Street_USA_Panorama.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Magic_Kingdom_-_Cinderella_Castle_at_Night.jpg/1280px-Magic_Kingdom_-_Cinderella_Castle_at_Night.jpg"
|
||||
"https://images.unsplash.com/photo-1524008279394-3aed4643b30b"
|
||||
],
|
||||
"rides": [
|
||||
{
|
||||
@@ -24,13 +22,12 @@
|
||||
"manufacturer": "Walt Disney Imagineering",
|
||||
"description": "A high-speed roller coaster in the dark through space.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Magic_Kingdom_Space_Mountain.jpg/1280px-Magic_Kingdom_Space_Mountain.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Space_Mountain_%28Magic_Kingdom%29_entrance.jpg/1280px-Space_Mountain_%28Magic_Kingdom%29_entrance.jpg"
|
||||
"https://images.unsplash.com/photo-1536768139911-e290a59011e4"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 183,
|
||||
"length_ft": 3196,
|
||||
"speed_mph": 27,
|
||||
"height_ft": "183.00",
|
||||
"length_ft": "3196.00",
|
||||
"speed_mph": "27.00",
|
||||
"inversions": 0,
|
||||
"ride_time_seconds": 180
|
||||
}
|
||||
@@ -43,13 +40,12 @@
|
||||
"manufacturer": "Walt Disney Imagineering",
|
||||
"description": "A mine train roller coaster through the Old West.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Big_Thunder_Mountain_Railroad_at_Magic_Kingdom.jpg/1280px-Big_Thunder_Mountain_Railroad_at_Magic_Kingdom.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Big_Thunder_Mountain_Railroad_%28Magic_Kingdom%29.jpg/1280px-Big_Thunder_Mountain_Railroad_%28Magic_Kingdom%29.jpg"
|
||||
"https://images.unsplash.com/photo-1513889961551-628c1e5e2ee9"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 104,
|
||||
"length_ft": 2671,
|
||||
"speed_mph": 30,
|
||||
"height_ft": "104.00",
|
||||
"length_ft": "2671.00",
|
||||
"speed_mph": "30.00",
|
||||
"inversions": 0,
|
||||
"ride_time_seconds": 197
|
||||
}
|
||||
@@ -62,13 +58,12 @@
|
||||
"manufacturer": "Vekoma",
|
||||
"description": "A family roller coaster featuring unique swinging cars.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Seven_Dwarfs_Mine_Train_at_Magic_Kingdom.jpg/1280px-Seven_Dwarfs_Mine_Train_at_Magic_Kingdom.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f5/Seven_Dwarfs_Mine_Train_drop.jpg/1280px-Seven_Dwarfs_Mine_Train_drop.jpg"
|
||||
"https://images.unsplash.com/photo-1590144662036-33bf0ebd2c7f"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 112,
|
||||
"length_ft": 2000,
|
||||
"speed_mph": 34,
|
||||
"height_ft": "112.00",
|
||||
"length_ft": "2000.00",
|
||||
"speed_mph": "34.00",
|
||||
"inversions": 0,
|
||||
"ride_time_seconds": 180
|
||||
}
|
||||
@@ -81,8 +76,7 @@
|
||||
"manufacturer": "Walt Disney Imagineering",
|
||||
"description": "A dark ride through a haunted estate.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Haunted_Mansion_at_Magic_Kingdom.jpg/1280px-Haunted_Mansion_at_Magic_Kingdom.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Haunted_Mansion_entrance.jpg/1280px-Haunted_Mansion_entrance.jpg"
|
||||
"https://images.unsplash.com/photo-1597466599360-3b9775841aec"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -93,8 +87,7 @@
|
||||
"manufacturer": "Walt Disney Imagineering",
|
||||
"description": "A boat ride through pirate-filled Caribbean waters.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Pirates_of_the_Caribbean_%28Magic_Kingdom%29.jpg/1280px-Pirates_of_the_Caribbean_%28Magic_Kingdom%29.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/89/Pirates_of_the_Caribbean_entrance.jpg/1280px-Pirates_of_the_Caribbean_entrance.jpg"
|
||||
"https://images.unsplash.com/photo-1506126799754-92bc47fc5d78"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -108,11 +101,9 @@
|
||||
"description": "Known as the Roller Coaster Capital of the World.",
|
||||
"website": "https://www.cedarpoint.com",
|
||||
"owner": "Cedar Fair",
|
||||
"size_acres": 364,
|
||||
"size_acres": "364.00",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Cedar_Point_aerial_view.jpg/1280px-Cedar_Point_aerial_view.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Cedar_Point_Beach.jpg/1280px-Cedar_Point_Beach.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cedar_Point_at_dusk.jpg/1280px-Cedar_Point_at_dusk.jpg"
|
||||
"https://images.unsplash.com/photo-1536768139911-e290a59011e4"
|
||||
],
|
||||
"rides": [
|
||||
{
|
||||
@@ -123,13 +114,12 @@
|
||||
"manufacturer": "Rocky Mountain Construction",
|
||||
"description": "A hybrid roller coaster featuring multiple inversions.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Steel_Vengeance_at_Cedar_Point.jpg/1280px-Steel_Vengeance_at_Cedar_Point.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Steel_Vengeance_first_drop.jpg/1280px-Steel_Vengeance_first_drop.jpg"
|
||||
"https://images.unsplash.com/photo-1543674892-7d64d45df18b"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 205,
|
||||
"length_ft": 5740,
|
||||
"speed_mph": 74,
|
||||
"height_ft": "205.00",
|
||||
"length_ft": "5740.00",
|
||||
"speed_mph": "74.00",
|
||||
"inversions": 4,
|
||||
"ride_time_seconds": 150
|
||||
}
|
||||
@@ -142,13 +132,12 @@
|
||||
"manufacturer": "Intamin",
|
||||
"description": "A giga coaster with stunning views of Lake Erie.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Millennium_Force_at_Cedar_Point.jpg/1280px-Millennium_Force_at_Cedar_Point.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Millennium_Force_lift_hill.jpg/1280px-Millennium_Force_lift_hill.jpg"
|
||||
"https://images.unsplash.com/photo-1605559911160-a3d95d213904"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 310,
|
||||
"length_ft": 6595,
|
||||
"speed_mph": 93,
|
||||
"height_ft": "310.00",
|
||||
"length_ft": "6595.00",
|
||||
"speed_mph": "93.00",
|
||||
"inversions": 0,
|
||||
"ride_time_seconds": 120
|
||||
}
|
||||
@@ -161,13 +150,12 @@
|
||||
"manufacturer": "Intamin",
|
||||
"description": "A strata coaster featuring a 420-foot top hat element.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Top_Thrill_Dragster.jpg/1280px-Top_Thrill_Dragster.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Top_Thrill_Dragster_launch.jpg/1280px-Top_Thrill_Dragster_launch.jpg"
|
||||
"https://images.unsplash.com/photo-1578912996078-305d92249aa6"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 420,
|
||||
"length_ft": 2800,
|
||||
"speed_mph": 120,
|
||||
"height_ft": "420.00",
|
||||
"length_ft": "2800.00",
|
||||
"speed_mph": "120.00",
|
||||
"inversions": 0,
|
||||
"ride_time_seconds": 50
|
||||
}
|
||||
@@ -180,13 +168,12 @@
|
||||
"manufacturer": "Intamin",
|
||||
"description": "A launched roller coaster with multiple inversions.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/39/Maverick_at_Cedar_Point.jpg/1280px-Maverick_at_Cedar_Point.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Maverick_first_drop.jpg/1280px-Maverick_first_drop.jpg"
|
||||
"https://images.unsplash.com/photo-1581309638082-877cb8132535"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 105,
|
||||
"length_ft": 4450,
|
||||
"speed_mph": 70,
|
||||
"height_ft": "105.00",
|
||||
"length_ft": "4450.00",
|
||||
"speed_mph": "70.00",
|
||||
"inversions": 2,
|
||||
"ride_time_seconds": 150
|
||||
}
|
||||
@@ -202,11 +189,9 @@
|
||||
"description": "A theme park featuring cutting-edge technology and thrilling attractions.",
|
||||
"website": "https://www.universalorlando.com/web/en/us/theme-parks/islands-of-adventure",
|
||||
"owner": "NBCUniversal",
|
||||
"size_acres": 110,
|
||||
"size_acres": "110.00",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Islands_of_Adventure_entrance.jpg/1280px-Islands_of_Adventure_entrance.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Hogwarts_Castle_at_Universal%27s_Islands_of_Adventure.jpg/1280px-Hogwarts_Castle_at_Universal%27s_Islands_of_Adventure.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Port_of_Entry_at_Islands_of_Adventure.jpg/1280px-Port_of_Entry_at_Islands_of_Adventure.jpg"
|
||||
"https://images.unsplash.com/photo-1597466599360-3b9775841aec"
|
||||
],
|
||||
"rides": [
|
||||
{
|
||||
@@ -217,13 +202,12 @@
|
||||
"manufacturer": "Intamin",
|
||||
"description": "A high-speed launch coaster featuring velociraptors.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Jurassic_World_VelociCoaster.jpg/1280px-Jurassic_World_VelociCoaster.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e5/VelociCoaster_top_hat.jpg/1280px-VelociCoaster_top_hat.jpg"
|
||||
"https://images.unsplash.com/photo-1536768139911-e290a59011e4"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 155,
|
||||
"length_ft": 4700,
|
||||
"speed_mph": 70,
|
||||
"height_ft": "155.00",
|
||||
"length_ft": "4700.00",
|
||||
"speed_mph": "70.00",
|
||||
"inversions": 4,
|
||||
"ride_time_seconds": 145
|
||||
}
|
||||
@@ -236,13 +220,12 @@
|
||||
"manufacturer": "Intamin",
|
||||
"description": "A story coaster through the Forbidden Forest.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Hagrid%27s_Magical_Creatures_Motorbike_Adventure.jpg/1280px-Hagrid%27s_Magical_Creatures_Motorbike_Adventure.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Hagrid%27s_entrance.jpg/1280px-Hagrid%27s_entrance.jpg"
|
||||
"https://images.unsplash.com/photo-1513889961551-628c1e5e2ee9"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 65,
|
||||
"length_ft": 5053,
|
||||
"speed_mph": 50,
|
||||
"height_ft": "65.00",
|
||||
"length_ft": "5053.00",
|
||||
"speed_mph": "50.00",
|
||||
"inversions": 0,
|
||||
"ride_time_seconds": 180
|
||||
}
|
||||
@@ -255,26 +238,23 @@
|
||||
"manufacturer": "Oceaneering International",
|
||||
"description": "A 3D dark ride featuring Spider-Man.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/The_Amazing_Adventures_of_Spider-Man.jpg/1280px-The_Amazing_Adventures_of_Spider-Man.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Spider-Man_ride_entrance.jpg/1280px-Spider-Man_ride_entrance.jpg"
|
||||
"https://images.unsplash.com/photo-1590144662036-33bf0ebd2c7f"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Alton Towers",
|
||||
"location": "Staffordshire, England",
|
||||
"location": "Alton, England",
|
||||
"country": "GB",
|
||||
"opening_date": "1980-04-04",
|
||||
"status": "OPERATING",
|
||||
"description": "The UK's largest theme park, built around a historic stately home.",
|
||||
"website": "https://www.altontowers.com",
|
||||
"owner": "Merlin Entertainments",
|
||||
"size_acres": 910,
|
||||
"size_acres": "910.00",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Alton_Towers_aerial_view.jpg/1280px-Alton_Towers_aerial_view.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/37/Alton_Towers_mansion.jpg/1280px-Alton_Towers_mansion.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Alton_Towers_gardens.jpg/1280px-Alton_Towers_gardens.jpg"
|
||||
"https://images.unsplash.com/photo-1506126799754-92bc47fc5d78"
|
||||
],
|
||||
"rides": [
|
||||
{
|
||||
@@ -285,13 +265,12 @@
|
||||
"manufacturer": "Bolliger & Mabillard",
|
||||
"description": "An inverted roller coaster through ravines.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/9/98/Nemesis_at_Alton_Towers.jpg/1280px-Nemesis_at_Alton_Towers.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/2/29/Nemesis_loop.jpg/1280px-Nemesis_loop.jpg"
|
||||
"https://images.unsplash.com/photo-1543674892-7d64d45df18b"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 43,
|
||||
"length_ft": 2349,
|
||||
"speed_mph": 50,
|
||||
"height_ft": "43.00",
|
||||
"length_ft": "2349.00",
|
||||
"speed_mph": "50.00",
|
||||
"inversions": 4,
|
||||
"ride_time_seconds": 80
|
||||
}
|
||||
@@ -304,13 +283,12 @@
|
||||
"manufacturer": "Bolliger & Mabillard",
|
||||
"description": "The world's first vertical drop roller coaster.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/6/67/Oblivion_at_Alton_Towers.jpg/1280px-Oblivion_at_Alton_Towers.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Oblivion_vertical_drop.jpg/1280px-Oblivion_vertical_drop.jpg"
|
||||
"https://images.unsplash.com/photo-1605559911160-a3d95d213904"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 65,
|
||||
"length_ft": 1804,
|
||||
"speed_mph": 68,
|
||||
"height_ft": "65.00",
|
||||
"length_ft": "1804.00",
|
||||
"speed_mph": "68.00",
|
||||
"inversions": 0,
|
||||
"ride_time_seconds": 100
|
||||
}
|
||||
@@ -326,11 +304,9 @@
|
||||
"description": "Germany's largest theme park, featuring European-themed areas.",
|
||||
"website": "https://www.europapark.de",
|
||||
"owner": "Mack Rides",
|
||||
"size_acres": 235,
|
||||
"size_acres": "235.00",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Europa-Park_entrance.jpg/1280px-Europa-Park_entrance.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Europa-Park_aerial_view.jpg/1280px-Europa-Park_aerial_view.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Europa-Park_at_night.jpg/1280px-Europa-Park_at_night.jpg"
|
||||
"https://images.unsplash.com/photo-1536768139911-e290a59011e4"
|
||||
],
|
||||
"rides": [
|
||||
{
|
||||
@@ -341,13 +317,12 @@
|
||||
"manufacturer": "Bolliger & Mabillard",
|
||||
"description": "A hypercoaster with stunning views.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Silver_Star_at_Europa-Park.jpg/1280px-Silver_Star_at_Europa-Park.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/7/75/Silver_Star_first_drop.jpg/1280px-Silver_Star_first_drop.jpg"
|
||||
"https://images.unsplash.com/photo-1536768139911-e290a59011e4"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 239,
|
||||
"length_ft": 4003,
|
||||
"speed_mph": 79,
|
||||
"height_ft": "239.00",
|
||||
"length_ft": "4003.00",
|
||||
"speed_mph": "79.00",
|
||||
"inversions": 0,
|
||||
"ride_time_seconds": 180
|
||||
}
|
||||
@@ -360,13 +335,12 @@
|
||||
"manufacturer": "Mack Rides",
|
||||
"description": "A launched roller coaster with multiple inversions.",
|
||||
"photos": [
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Blue_Fire_at_Europa-Park.jpg/1280px-Blue_Fire_at_Europa-Park.jpg",
|
||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Blue_Fire_launch.jpg/1280px-Blue_Fire_launch.jpg"
|
||||
"https://images.unsplash.com/photo-1513889961551-628c1e5e2ee9"
|
||||
],
|
||||
"stats": {
|
||||
"height_ft": 125,
|
||||
"length_ft": 3465,
|
||||
"speed_mph": 62,
|
||||
"height_ft": "125.00",
|
||||
"length_ft": "3465.00",
|
||||
"speed_mph": "62.00",
|
||||
"inversions": 4,
|
||||
"ride_time_seconds": 150
|
||||
}
|
||||
|
||||
@@ -1,297 +1,216 @@
|
||||
import os
|
||||
import json
|
||||
import random
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
import os
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.core.files import File
|
||||
from django.utils.text import slugify
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import connection
|
||||
from faker import Faker
|
||||
from django.core.files.temp import NamedTemporaryFile
|
||||
from django.core.files import File
|
||||
import requests
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
from cities_light.models import City, Country
|
||||
|
||||
from parks.models import Park
|
||||
from rides.models import Ride, RollerCoasterStats
|
||||
from companies.models import Company, Manufacturer
|
||||
from reviews.models import Review
|
||||
from media.models import Photo
|
||||
from accounts.models import User, UserProfile, TopList, TopListItem
|
||||
from companies.models import Company, Manufacturer
|
||||
from cities_light.models import Country, Region, City
|
||||
from django.contrib.auth.models import Permission
|
||||
import random
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
fake = Faker()
|
||||
User = get_user_model()
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Seeds the database with sample data'
|
||||
help = 'Seeds the database with initial data'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--users', type=int, default=50)
|
||||
parser.add_argument('--reviews-per-item', type=int, default=10)
|
||||
def handle(self, *args, **kwargs):
|
||||
self.stdout.write('Starting database seed...')
|
||||
|
||||
# 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()
|
||||
|
||||
# Create top lists
|
||||
self.stdout.write('Creating top lists...')
|
||||
self.create_top_lists()
|
||||
|
||||
self.stdout.write('Successfully seeded database')
|
||||
|
||||
def download_and_save_image(self, url):
|
||||
try:
|
||||
response = requests.get(url)
|
||||
img = Image.open(BytesIO(response.content))
|
||||
img_io = BytesIO()
|
||||
img.save(img_io, format='JPEG')
|
||||
img_io.seek(0)
|
||||
filename = url.split('/')[-1]
|
||||
return filename, File(img_io)
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.WARNING(f'Failed to download image {url}: {str(e)}'))
|
||||
return None, None
|
||||
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, count):
|
||||
def create_users(self):
|
||||
self.stdout.write('Creating users...')
|
||||
users = []
|
||||
|
||||
|
||||
# Try to get admin user
|
||||
try:
|
||||
# Get existing admin user
|
||||
admin_user = User.objects.get(username='admin')
|
||||
users.append(admin_user)
|
||||
self.stdout.write('Added existing admin user')
|
||||
admin = User.objects.get(username='admin')
|
||||
self.stdout.write('Admin user exists, updating permissions...')
|
||||
except User.DoesNotExist:
|
||||
self.stdout.write(self.style.WARNING('Admin user not found, skipping...'))
|
||||
|
||||
# Create regular users using raw SQL
|
||||
roles = ['USER'] * 20 + ['MODERATOR'] * 3 + ['ADMIN'] * 2
|
||||
with connection.cursor() as cursor:
|
||||
for _ in range(count):
|
||||
# Create user
|
||||
username = fake.user_name()
|
||||
while User.objects.filter(username=username).exists():
|
||||
username = fake.user_name()
|
||||
|
||||
user_id = str(uuid.uuid4())[:10]
|
||||
cursor.execute("""
|
||||
INSERT INTO accounts_user (
|
||||
username, password, email, is_superuser, is_staff,
|
||||
is_active, date_joined, user_id, first_name,
|
||||
last_name, role, is_banned, ban_reason,
|
||||
theme_preference
|
||||
) VALUES (
|
||||
%s, %s, %s, false, false,
|
||||
true, NOW(), %s, '', '',
|
||||
%s, false, '', 'light'
|
||||
) RETURNING id;
|
||||
""", [username, make_password('password123'), fake.email(), user_id, random.choice(roles)])
|
||||
|
||||
user_db_id = cursor.fetchone()[0]
|
||||
|
||||
# Create profile
|
||||
profile_id = str(uuid.uuid4())[:10]
|
||||
display_name = f"{fake.first_name()}_{fake.last_name()}_{fake.random_number(digits=4)}"
|
||||
cursor.execute("""
|
||||
INSERT INTO accounts_userprofile (
|
||||
profile_id, display_name, pronouns, bio,
|
||||
twitter, instagram, youtube, discord,
|
||||
coaster_credits, dark_ride_credits,
|
||||
flat_ride_credits, water_ride_credits,
|
||||
user_id, avatar
|
||||
) VALUES (
|
||||
%s, %s, %s, %s,
|
||||
%s, %s, %s, %s,
|
||||
%s, %s, %s, %s,
|
||||
%s, ''
|
||||
);
|
||||
""", [
|
||||
profile_id, display_name, random.choice(['he/him', 'she/her', 'they/them', '']),
|
||||
fake.text(max_nb_chars=200),
|
||||
fake.url() if random.choice([True, False]) else '',
|
||||
fake.url() if random.choice([True, False]) else '',
|
||||
fake.url() if random.choice([True, False]) else '',
|
||||
fake.user_name() if random.choice([True, False]) else '',
|
||||
random.randint(0, 500), random.randint(0, 200),
|
||||
random.randint(0, 300), random.randint(0, 100),
|
||||
user_db_id
|
||||
])
|
||||
|
||||
users.append(User.objects.get(id=user_db_id))
|
||||
admin = User.objects.create_superuser('admin', 'admin@example.com', 'admin')
|
||||
self.stdout.write('Created admin user')
|
||||
|
||||
# Create regular users
|
||||
usernames = [
|
||||
'destiny89', 'destiny97', 'thompsonchris', 'chriscohen', 'littlesharon',
|
||||
'wrichardson', 'christophermiles', 'jacksonangela', 'jennifer71', 'smithemily',
|
||||
'brandylong', 'milleranna', 'tlopez', 'fgriffith', 'mariah80',
|
||||
'kendradavis', 'rosarioashley', 'camposkaitlyn', 'lisaherrera', 'riveratiffany',
|
||||
'codytucker', 'cheyenne78', 'christinagreen', 'eric57', 'steinsuzanne',
|
||||
'david95', 'rstewart', 'josephhaynes', 'umedina', 'tylerbryant',
|
||||
'lcampos', 'shellyford', 'ksmith', 'qeverett', 'waguilar',
|
||||
'zbrowning', 'yalexander', 'wallacewilliam', 'bsuarez', 'ismith',
|
||||
'joyceosborne', 'garythomas', 'tlewis', 'robertgonzales', 'medinashannon',
|
||||
'yhanson', 'howellmorgan', 'taylorsusan', 'barnold', 'bryan20'
|
||||
]
|
||||
|
||||
for username in usernames:
|
||||
if not User.objects.filter(username=username).exists():
|
||||
User.objects.create_user(
|
||||
username=username,
|
||||
email=f'{username}@example.com',
|
||||
password='password123'
|
||||
)
|
||||
self.stdout.write(f'Created user: {username}')
|
||||
|
||||
return users
|
||||
|
||||
def create_companies(self):
|
||||
self.stdout.write('Creating companies...')
|
||||
|
||||
# Delete existing companies
|
||||
Company.objects.all().delete()
|
||||
self.stdout.write('Deleted existing companies')
|
||||
|
||||
companies = {
|
||||
'The Walt Disney Company': {
|
||||
'headquarters': 'Burbank, California',
|
||||
'founded_date': '1923-10-16',
|
||||
'website': 'https://www.disney.com',
|
||||
},
|
||||
'Cedar Fair': {
|
||||
'headquarters': 'Sandusky, Ohio',
|
||||
'founded_date': '1983-05-01',
|
||||
'website': 'https://www.cedarfair.com',
|
||||
},
|
||||
'NBCUniversal': {
|
||||
'headquarters': 'New York City, New York',
|
||||
'founded_date': '1912-04-30',
|
||||
'website': 'https://www.nbcuniversal.com',
|
||||
},
|
||||
'Merlin Entertainments': {
|
||||
'headquarters': 'Poole, England',
|
||||
'founded_date': '1999-05-19',
|
||||
'website': 'https://www.merlinentertainments.biz',
|
||||
},
|
||||
'Mack Rides': {
|
||||
'headquarters': 'Waldkirch, Germany',
|
||||
'founded_date': '1780-01-01',
|
||||
'website': 'https://mack-rides.com',
|
||||
},
|
||||
}
|
||||
|
||||
company_instances = {}
|
||||
for name, details in companies.items():
|
||||
company = Company.objects.create(
|
||||
name=name,
|
||||
slug=slugify(name),
|
||||
headquarters=details['headquarters'],
|
||||
founded_date=datetime.strptime(details['founded_date'], '%Y-%m-%d').date(),
|
||||
website=details['website'],
|
||||
)
|
||||
company_instances[name] = company
|
||||
|
||||
companies = [
|
||||
'The Walt Disney Company',
|
||||
'Cedar Fair',
|
||||
'NBCUniversal',
|
||||
'Merlin Entertainments',
|
||||
'Mack Rides'
|
||||
]
|
||||
|
||||
for name in companies:
|
||||
Company.objects.create(name=name)
|
||||
self.stdout.write(f'Created company: {name}')
|
||||
|
||||
return company_instances
|
||||
|
||||
def create_manufacturers(self):
|
||||
self.stdout.write('Creating manufacturers...')
|
||||
|
||||
# Delete existing manufacturers
|
||||
Manufacturer.objects.all().delete()
|
||||
self.stdout.write('Deleted existing manufacturers')
|
||||
|
||||
manufacturers = {
|
||||
'Walt Disney Imagineering': {
|
||||
'headquarters': 'Glendale, California',
|
||||
'founded_date': '1952-12-16',
|
||||
'website': 'https://sites.disney.com/waltdisneyimagineering/',
|
||||
},
|
||||
'Bolliger & Mabillard': {
|
||||
'headquarters': 'Monthey, Switzerland',
|
||||
'founded_date': '1988-01-01',
|
||||
'website': 'https://www.bolliger-mabillard.com',
|
||||
},
|
||||
'Intamin': {
|
||||
'headquarters': 'Schaan, Liechtenstein',
|
||||
'founded_date': '1967-01-01',
|
||||
'website': 'https://www.intamin.com',
|
||||
},
|
||||
'Rocky Mountain Construction': {
|
||||
'headquarters': 'Hayden, Idaho',
|
||||
'founded_date': '2001-01-01',
|
||||
'website': 'https://www.rockymountainconstruction.com',
|
||||
},
|
||||
'Vekoma': {
|
||||
'headquarters': 'Vlodrop, Netherlands',
|
||||
'founded_date': '1926-01-01',
|
||||
'website': 'https://www.vekoma.com',
|
||||
},
|
||||
'Mack Rides': {
|
||||
'headquarters': 'Waldkirch, Germany',
|
||||
'founded_date': '1780-01-01',
|
||||
'website': 'https://mack-rides.com',
|
||||
},
|
||||
'Oceaneering International': {
|
||||
'headquarters': 'Houston, Texas',
|
||||
'founded_date': '1964-01-01',
|
||||
'website': 'https://www.oceaneering.com',
|
||||
},
|
||||
}
|
||||
|
||||
manufacturer_instances = {}
|
||||
for name, details in manufacturers.items():
|
||||
manufacturer = Manufacturer.objects.create(
|
||||
name=name,
|
||||
slug=slugify(name),
|
||||
headquarters=details['headquarters'],
|
||||
founded_date=datetime.strptime(details['founded_date'], '%Y-%m-%d').date(),
|
||||
website=details['website'],
|
||||
)
|
||||
manufacturer_instances[name] = manufacturer
|
||||
|
||||
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}')
|
||||
|
||||
return manufacturer_instances
|
||||
def download_image(self, url):
|
||||
"""Download image from URL and return as Django File object"""
|
||||
response = requests.get(url)
|
||||
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, users):
|
||||
self.stdout.write('Creating parks and rides from seed data...')
|
||||
|
||||
# Create companies and manufacturers first
|
||||
companies = self.create_companies()
|
||||
manufacturers = self.create_manufacturers()
|
||||
|
||||
# Load seed data
|
||||
seed_data_path = os.path.join(os.path.dirname(__file__), 'seed_data.json')
|
||||
with open(seed_data_path, 'r') as f:
|
||||
seed_data = json.load(f)
|
||||
|
||||
# Delete existing parks (this will cascade delete rides)
|
||||
def create_parks_and_rides(self):
|
||||
# Delete existing parks and rides
|
||||
Park.objects.all().delete()
|
||||
self.stdout.write('Deleted existing parks and rides')
|
||||
|
||||
parks = []
|
||||
for park_data in seed_data['parks']:
|
||||
# Load seed data
|
||||
with open(os.path.join(os.path.dirname(__file__), 'seed_data.json')) as f:
|
||||
data = json.load(f)
|
||||
|
||||
country_map = {
|
||||
'US': 'United States',
|
||||
'GB': 'United Kingdom',
|
||||
'DE': 'Germany'
|
||||
}
|
||||
|
||||
for park_data in data['parks']:
|
||||
try:
|
||||
# Get country from cities_light
|
||||
country = Country.objects.get(code2=park_data['country'])
|
||||
|
||||
# Try to find city, but don't require it
|
||||
city = None
|
||||
try:
|
||||
city_name = park_data['location'].split(',')[0].strip()
|
||||
city = City.objects.filter(name__iexact=city_name, country=country).first()
|
||||
except:
|
||||
self.stdout.write(self.style.WARNING(f'City not found for {park_data["name"]}, using location text'))
|
||||
|
||||
# Create park
|
||||
park = Park.objects.create(
|
||||
name=park_data['name'],
|
||||
slug=slugify(park_data['name']),
|
||||
location=park_data['location'],
|
||||
country=country,
|
||||
city=city,
|
||||
opening_date=datetime.strptime(park_data['opening_date'], '%Y-%m-%d').date(),
|
||||
opening_date=park_data['opening_date'],
|
||||
status=park_data['status'],
|
||||
description=park_data['description'],
|
||||
website=park_data['website'],
|
||||
owner=companies[park_data['owner']],
|
||||
owner=Company.objects.get(name=park_data['owner']),
|
||||
size_acres=park_data['size_acres']
|
||||
)
|
||||
|
||||
# Add park photos
|
||||
for photo_url in park_data.get('photos', []):
|
||||
filename, file = self.download_and_save_image(photo_url)
|
||||
if filename and file:
|
||||
for photo_url in park_data['photos']:
|
||||
img_file = self.download_image(photo_url)
|
||||
if img_file:
|
||||
Photo.objects.create(
|
||||
content_object=park,
|
||||
image=file,
|
||||
uploaded_by=random.choice(users),
|
||||
caption=f"Photo of {park.name}",
|
||||
is_approved=True
|
||||
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 this park
|
||||
# Create rides
|
||||
for ride_data in park_data['rides']:
|
||||
ride = Ride.objects.create(
|
||||
name=ride_data['name'],
|
||||
slug=slugify(ride_data['name']),
|
||||
category=ride_data['category'],
|
||||
park=park,
|
||||
category=ride_data['category'],
|
||||
opening_date=ride_data['opening_date'],
|
||||
status=ride_data['status'],
|
||||
opening_date=datetime.strptime(ride_data['opening_date'], '%Y-%m-%d').date(),
|
||||
manufacturer=manufacturers[ride_data['manufacturer']],
|
||||
manufacturer=Manufacturer.objects.get(name=ride_data['manufacturer']),
|
||||
description=ride_data['description']
|
||||
)
|
||||
|
||||
# Add roller coaster stats if applicable
|
||||
if ride_data['category'] == 'RC' and 'stats' in ride_data:
|
||||
# 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'],
|
||||
@@ -300,118 +219,67 @@ class Command(BaseCommand):
|
||||
inversions=ride_data['stats']['inversions'],
|
||||
ride_time_seconds=ride_data['stats']['ride_time_seconds']
|
||||
)
|
||||
|
||||
# Add ride photos
|
||||
for photo_url in ride_data.get('photos', []):
|
||||
filename, file = self.download_and_save_image(photo_url)
|
||||
if filename and file:
|
||||
Photo.objects.create(
|
||||
content_object=ride,
|
||||
image=file,
|
||||
uploaded_by=random.choice(users),
|
||||
caption=f"Photo of {ride.name}",
|
||||
is_approved=True
|
||||
)
|
||||
|
||||
parks.append(park)
|
||||
self.stdout.write(f'Created park and rides: {park.name}')
|
||||
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.ERROR(f'Failed to create park {park_data["name"]}: {str(e)}'))
|
||||
except Country.DoesNotExist:
|
||||
self.stdout.write(f'Country not found: {park_data["country"]}')
|
||||
continue
|
||||
|
||||
return parks
|
||||
|
||||
def create_reviews(self, users, reviews_per_item):
|
||||
self.stdout.write('Creating reviews...')
|
||||
def create_reviews(self):
|
||||
# Delete existing reviews
|
||||
Review.objects.all().delete()
|
||||
self.stdout.write('Deleted existing reviews')
|
||||
|
||||
# Park reviews
|
||||
total_parks = Park.objects.count()
|
||||
for i, park in enumerate(Park.objects.all(), 1):
|
||||
for _ in range(random.randint(reviews_per_item - 5, reviews_per_item + 5)):
|
||||
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 = random.randint(3, 5)
|
||||
for _ in range(num_reviews):
|
||||
# Generate random visit date
|
||||
days_offset = random.randint(0, 365)
|
||||
visit_date = one_year_ago + timedelta(days=days_offset)
|
||||
|
||||
Review.objects.create(
|
||||
user=random.choice(users),
|
||||
content_object=park,
|
||||
title=fake.sentence(),
|
||||
content=fake.text(max_nb_chars=500),
|
||||
rating=random.randint(1, 10),
|
||||
visit_date=fake.date_between(start_date=park.opening_date, end_date='today'),
|
||||
is_published=True
|
||||
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=random.randint(7, 10),
|
||||
visit_date=visit_date
|
||||
)
|
||||
if i % 5 == 0:
|
||||
self.stdout.write(f'Created reviews for {i}/{total_parks} parks')
|
||||
self.stdout.write(f'Created reviews for {park.name}')
|
||||
|
||||
# Ride reviews
|
||||
total_rides = Ride.objects.count()
|
||||
for i, ride in enumerate(Ride.objects.all(), 1):
|
||||
for _ in range(random.randint(reviews_per_item - 5, reviews_per_item + 5)):
|
||||
Review.objects.create(
|
||||
user=random.choice(users),
|
||||
content_object=ride,
|
||||
title=fake.sentence(),
|
||||
content=fake.text(max_nb_chars=500),
|
||||
rating=random.randint(1, 10),
|
||||
visit_date=fake.date_between(start_date=ride.opening_date, end_date='today'),
|
||||
is_published=True
|
||||
)
|
||||
if i % 20 == 0:
|
||||
self.stdout.write(f'Created reviews for {i}/{total_rides} rides')
|
||||
|
||||
def create_top_lists(self, users):
|
||||
self.stdout.write('Creating top lists...')
|
||||
def create_top_lists(self):
|
||||
# Delete existing top lists
|
||||
TopList.objects.all().delete()
|
||||
# TopList.objects.all().delete()
|
||||
self.stdout.write('Deleted existing top lists')
|
||||
|
||||
categories = ['RC', 'DR', 'FR', 'WR', 'PK']
|
||||
total_users = len(users)
|
||||
|
||||
# Get content types
|
||||
park_ct = ContentType.objects.get_for_model(Park)
|
||||
ride_ct = ContentType.objects.get_for_model(Ride)
|
||||
users = list(User.objects.exclude(username='admin'))
|
||||
parks = list(Park.objects.all())
|
||||
|
||||
for i, user in enumerate(users, 1):
|
||||
for category in categories:
|
||||
if random.choice([True, False]): # 50% chance to create a list
|
||||
top_list = TopList.objects.create(
|
||||
user=user,
|
||||
title=f"My Top {random.randint(5, 20)} {dict(TopList.Categories.choices)[category]}s",
|
||||
category=category,
|
||||
description=fake.text(max_nb_chars=200)
|
||||
)
|
||||
|
||||
# Add items to the list
|
||||
items = []
|
||||
if category == 'PK':
|
||||
items = list(Park.objects.all())
|
||||
content_type = park_ct
|
||||
else:
|
||||
items = list(Ride.objects.filter(category=category))
|
||||
content_type = ride_ct
|
||||
|
||||
if items:
|
||||
selected_items = random.sample(items, min(len(items), random.randint(5, 20)))
|
||||
for rank, item in enumerate(selected_items, 1):
|
||||
TopListItem.objects.create(
|
||||
top_list=top_list,
|
||||
content_type=content_type,
|
||||
object_id=item.id,
|
||||
rank=rank,
|
||||
notes=fake.sentence() if random.choice([True, False]) else ''
|
||||
)
|
||||
|
||||
# Create top list for every 10th user
|
||||
if i % 10 == 0:
|
||||
self.stdout.write(f'Created top lists for {i}/{total_users} users')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.stdout.write('Starting database seed...')
|
||||
|
||||
users = self.create_users(options['users'])
|
||||
parks = self.create_parks_and_rides(users)
|
||||
self.create_reviews(users, options['reviews_per_item'])
|
||||
self.create_top_lists(users)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Successfully seeded database'))
|
||||
# top_list = TopList.objects.create(
|
||||
# user=user,
|
||||
# name=f"{user.username}'s Top Parks",
|
||||
# description='My favorite theme parks'
|
||||
# )
|
||||
|
||||
# Add 3-5 random parks
|
||||
# selected_parks = random.sample(parks, random.randint(3, 5))
|
||||
# for j, park in enumerate(selected_parks, 1):
|
||||
# TopListItem.objects.create(
|
||||
# top_list=top_list,
|
||||
# content_type=ContentType.objects.get_for_model(park),
|
||||
# object_id=park.id,
|
||||
# rank=j
|
||||
# )
|
||||
self.stdout.write(f'Created top lists for {i}/50 users')
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-01 00:24
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("cities_light", "0011_alter_city_country_alter_city_region_and_more"),
|
||||
("parks", "0020_remove_historicalpark_city_text"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="historicalpark",
|
||||
name="country",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
db_constraint=False,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
related_name="+",
|
||||
to="cities_light.country",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="park",
|
||||
name="location",
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
]
|
||||
@@ -16,7 +16,7 @@ class Park(models.Model):
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
slug = models.SlugField(max_length=255, unique=True)
|
||||
location = models.CharField(max_length=255)
|
||||
location = models.CharField(max_length=255, blank=True, null=True) # Made nullable
|
||||
country = models.ForeignKey(Country, on_delete=models.PROTECT)
|
||||
region = models.ForeignKey(Region, on_delete=models.PROTECT, null=True, blank=True)
|
||||
city = models.ForeignKey(City, on_delete=models.PROTECT, null=True, blank=True)
|
||||
|
||||
259
parks/views.py
259
parks/views.py
@@ -15,85 +15,89 @@ from moderation.mixins import EditSubmissionMixin, PhotoSubmissionMixin, History
|
||||
from moderation.models import EditSubmission
|
||||
from cities_light.models import Country, Region, City
|
||||
|
||||
|
||||
def get_countries(request):
|
||||
query = request.GET.get('q', '')
|
||||
filter_parks = request.GET.get('filter_parks', 'false') == 'true'
|
||||
|
||||
query = request.GET.get("q", "")
|
||||
filter_parks = request.GET.get("filter_parks", "false") == "true"
|
||||
|
||||
# Base query
|
||||
countries = Country.objects.filter(name__icontains=query)
|
||||
|
||||
|
||||
# Only filter by parks if explicitly requested
|
||||
if filter_parks:
|
||||
countries = countries.filter(park__isnull=False)
|
||||
|
||||
countries = countries.distinct().values('id', 'name')[:10]
|
||||
|
||||
countries = countries.distinct().values("id", "name")[:10]
|
||||
return JsonResponse(list(countries), safe=False)
|
||||
|
||||
|
||||
def get_regions(request):
|
||||
query = request.GET.get('q', '')
|
||||
country = request.GET.get('country', '')
|
||||
filter_parks = request.GET.get('filter_parks', 'false') == 'true'
|
||||
|
||||
query = request.GET.get("q", "")
|
||||
country = request.GET.get("country", "")
|
||||
filter_parks = request.GET.get("filter_parks", "false") == "true"
|
||||
|
||||
if not country:
|
||||
return JsonResponse([], safe=False)
|
||||
|
||||
|
||||
# Base query
|
||||
regions = Region.objects.filter(
|
||||
Q(name__icontains=query) | Q(alternate_names__icontains=query),
|
||||
country__name__iexact=country
|
||||
country__name__iexact=country,
|
||||
)
|
||||
|
||||
|
||||
# Only filter by parks if explicitly requested
|
||||
if filter_parks:
|
||||
regions = regions.filter(park__isnull=False)
|
||||
|
||||
regions = regions.distinct().values('id', 'name')[:10]
|
||||
|
||||
regions = regions.distinct().values("id", "name")[:10]
|
||||
return JsonResponse(list(regions), safe=False)
|
||||
|
||||
|
||||
def get_cities(request):
|
||||
query = request.GET.get('q', '')
|
||||
region = request.GET.get('region', '')
|
||||
country = request.GET.get('country', '')
|
||||
filter_parks = request.GET.get('filter_parks', 'false') == 'true'
|
||||
|
||||
query = request.GET.get("q", "")
|
||||
region = request.GET.get("region", "")
|
||||
country = request.GET.get("country", "")
|
||||
filter_parks = request.GET.get("filter_parks", "false") == "true"
|
||||
|
||||
if not region or not country:
|
||||
return JsonResponse([], safe=False)
|
||||
|
||||
|
||||
# Base query
|
||||
cities = City.objects.filter(
|
||||
Q(name__icontains=query) | Q(alternate_names__icontains=query),
|
||||
region__name__iexact=region,
|
||||
region__country__name__iexact=country
|
||||
region__country__name__iexact=country,
|
||||
)
|
||||
|
||||
|
||||
# Only filter by parks if explicitly requested
|
||||
if filter_parks:
|
||||
cities = cities.filter(park__isnull=False)
|
||||
|
||||
cities = cities.distinct().values('id', 'name')[:10]
|
||||
|
||||
cities = cities.distinct().values("id", "name")[:10]
|
||||
return JsonResponse(list(cities), safe=False)
|
||||
|
||||
|
||||
class ParkCreateView(LoginRequiredMixin, CreateView):
|
||||
model = Park
|
||||
form_class = ParkForm
|
||||
template_name = 'parks/park_form.html'
|
||||
template_name = "parks/park_form.html"
|
||||
|
||||
def prepare_changes_data(self, cleaned_data):
|
||||
data = cleaned_data.copy()
|
||||
# Convert model instances to IDs for JSON serialization
|
||||
if data.get('owner'):
|
||||
data['owner'] = data['owner'].id
|
||||
if data.get('country'):
|
||||
data['country'] = data['country'].id
|
||||
if data.get('region'):
|
||||
data['region'] = data['region'].id
|
||||
if data.get('city'):
|
||||
data['city'] = data['city'].id
|
||||
if data.get("owner"):
|
||||
data["owner"] = data["owner"].id
|
||||
if data.get("country"):
|
||||
data["country"] = data["country"].id
|
||||
if data.get("region"):
|
||||
data["region"] = data["region"].id
|
||||
if data.get("city"):
|
||||
data["city"] = data["city"].id
|
||||
# Convert dates to ISO format strings
|
||||
if data.get('opening_date'):
|
||||
data['opening_date'] = data['opening_date'].isoformat()
|
||||
if data.get('closing_date'):
|
||||
data['closing_date'] = data['closing_date'].isoformat()
|
||||
if data.get("opening_date"):
|
||||
data["opening_date"] = data["opening_date"].isoformat()
|
||||
if data.get("closing_date"):
|
||||
data["closing_date"] = data["closing_date"].isoformat()
|
||||
return data
|
||||
|
||||
def form_valid(self, form):
|
||||
@@ -103,54 +107,55 @@ class ParkCreateView(LoginRequiredMixin, CreateView):
|
||||
submission = EditSubmission.objects.create(
|
||||
user=self.request.user,
|
||||
content_type=ContentType.objects.get_for_model(Park),
|
||||
submission_type='CREATE',
|
||||
submission_type="CREATE",
|
||||
changes=changes,
|
||||
reason=self.request.POST.get('reason', ''),
|
||||
source=self.request.POST.get('source', '')
|
||||
reason=self.request.POST.get("reason", ""),
|
||||
source=self.request.POST.get("source", ""),
|
||||
)
|
||||
|
||||
# If user is moderator or above, auto-approve
|
||||
if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
|
||||
if self.request.user.role in ["MODERATOR", "ADMIN", "SUPERUSER"]:
|
||||
self.object = form.save()
|
||||
submission.object_id = self.object.id
|
||||
submission.status = 'APPROVED'
|
||||
submission.status = "APPROVED"
|
||||
submission.handled_by = self.request.user
|
||||
submission.save()
|
||||
messages.success(self.request, f'Successfully created {self.object.name}')
|
||||
messages.success(self.request, f"Successfully created {self.object.name}")
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
messages.success(self.request, 'Your park submission has been sent for review')
|
||||
return HttpResponseRedirect(reverse('parks:park_list'))
|
||||
|
||||
messages.success(self.request, "Your park submission has been sent for review")
|
||||
return HttpResponseRedirect(reverse("parks:park_list"))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('parks:park_detail', kwargs={'slug': self.object.slug})
|
||||
return reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
||||
|
||||
|
||||
class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = Park
|
||||
form_class = ParkForm
|
||||
template_name = 'parks/park_form.html'
|
||||
template_name = "parks/park_form.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['is_edit'] = True
|
||||
context["is_edit"] = True
|
||||
return context
|
||||
|
||||
def prepare_changes_data(self, cleaned_data):
|
||||
data = cleaned_data.copy()
|
||||
# Convert model instances to IDs for JSON serialization
|
||||
if data.get('owner'):
|
||||
data['owner'] = data['owner'].id
|
||||
if data.get('country'):
|
||||
data['country'] = data['country'].id
|
||||
if data.get('region'):
|
||||
data['region'] = data['region'].id
|
||||
if data.get('city'):
|
||||
data['city'] = data['city'].id
|
||||
if data.get("owner"):
|
||||
data["owner"] = data["owner"].id
|
||||
if data.get("country"):
|
||||
data["country"] = data["country"].id
|
||||
if data.get("region"):
|
||||
data["region"] = data["region"].id
|
||||
if data.get("city"):
|
||||
data["city"] = data["city"].id
|
||||
# Convert dates to ISO format strings
|
||||
if data.get('opening_date'):
|
||||
data['opening_date'] = data['opening_date'].isoformat()
|
||||
if data.get('closing_date'):
|
||||
data['closing_date'] = data['closing_date'].isoformat()
|
||||
if data.get("opening_date"):
|
||||
data["opening_date"] = data["opening_date"].isoformat()
|
||||
if data.get("closing_date"):
|
||||
data["closing_date"] = data["closing_date"].isoformat()
|
||||
return data
|
||||
|
||||
def form_valid(self, form):
|
||||
@@ -161,31 +166,43 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
||||
user=self.request.user,
|
||||
content_type=ContentType.objects.get_for_model(Park),
|
||||
object_id=self.object.id,
|
||||
submission_type='EDIT',
|
||||
submission_type="EDIT",
|
||||
changes=changes,
|
||||
reason=self.request.POST.get('reason', ''),
|
||||
source=self.request.POST.get('source', '')
|
||||
reason=self.request.POST.get("reason", ""),
|
||||
source=self.request.POST.get("source", ""),
|
||||
)
|
||||
|
||||
# If user is moderator or above, auto-approve
|
||||
if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
|
||||
if self.request.user.role in ["MODERATOR", "ADMIN", "SUPERUSER"]:
|
||||
self.object = form.save()
|
||||
submission.status = 'APPROVED'
|
||||
submission.status = "APPROVED"
|
||||
submission.handled_by = self.request.user
|
||||
submission.save()
|
||||
messages.success(self.request, f'Successfully updated {self.object.name}')
|
||||
messages.success(self.request, f"Successfully updated {self.object.name}")
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
messages.success(self.request, f'Your changes to {self.object.name} have been sent for review')
|
||||
return HttpResponseRedirect(reverse('parks:park_detail', kwargs={'slug': self.object.slug}))
|
||||
|
||||
messages.success(
|
||||
self.request,
|
||||
f"Your changes to {self.object.name} have been sent for review",
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
||||
)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('parks:park_detail', kwargs={'slug': self.object.slug})
|
||||
return reverse("parks:park_detail", kwargs={"slug": self.object.slug})
|
||||
|
||||
class ParkDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin, DetailView):
|
||||
|
||||
class ParkDetailView(
|
||||
SlugRedirectMixin,
|
||||
EditSubmissionMixin,
|
||||
PhotoSubmissionMixin,
|
||||
HistoryMixin,
|
||||
DetailView,
|
||||
):
|
||||
model = Park
|
||||
template_name = 'parks/park_detail.html'
|
||||
context_object_name = 'park'
|
||||
template_name = "parks/park_detail.html"
|
||||
context_object_name = "park"
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
if queryset is None:
|
||||
@@ -196,26 +213,33 @@ class ParkDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixi
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['rides'] = Ride.objects.filter(
|
||||
park=self.object
|
||||
).select_related('coaster_stats')
|
||||
context['areas'] = ParkArea.objects.filter(park=self.object)
|
||||
context["rides"] = Ride.objects.filter(park=self.object).select_related(
|
||||
"coaster_stats"
|
||||
)
|
||||
context["areas"] = ParkArea.objects.filter(park=self.object)
|
||||
return context
|
||||
|
||||
def get_redirect_url_pattern(self):
|
||||
return 'parks:park_detail'
|
||||
return "parks:park_detail"
|
||||
|
||||
class ParkAreaDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin, DetailView):
|
||||
|
||||
class ParkAreaDetailView(
|
||||
SlugRedirectMixin,
|
||||
EditSubmissionMixin,
|
||||
PhotoSubmissionMixin,
|
||||
HistoryMixin,
|
||||
DetailView,
|
||||
):
|
||||
model = ParkArea
|
||||
template_name = 'parks/area_detail.html'
|
||||
context_object_name = 'area'
|
||||
slug_url_kwarg = 'area_slug'
|
||||
template_name = "parks/area_detail.html"
|
||||
context_object_name = "area"
|
||||
slug_url_kwarg = "area_slug"
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
if queryset is None:
|
||||
queryset = self.get_queryset()
|
||||
park_slug = self.kwargs.get('park_slug')
|
||||
area_slug = self.kwargs.get('area_slug')
|
||||
park_slug = self.kwargs.get("park_slug")
|
||||
area_slug = self.kwargs.get("area_slug")
|
||||
# Try to get by current or historical slug
|
||||
obj, is_old_slug = self.model.get_by_slug(area_slug)
|
||||
if obj.park.slug != park_slug:
|
||||
@@ -224,62 +248,61 @@ class ParkAreaDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmission
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['rides'] = Ride.objects.filter(
|
||||
area=self.object
|
||||
).select_related('coaster_stats')
|
||||
context["rides"] = Ride.objects.filter(area=self.object).select_related(
|
||||
"coaster_stats"
|
||||
)
|
||||
return context
|
||||
|
||||
def get_redirect_url_pattern(self):
|
||||
return 'parks:park_detail'
|
||||
return "parks:park_detail"
|
||||
|
||||
def get_redirect_url_kwargs(self):
|
||||
return {
|
||||
'park_slug': self.object.park.slug,
|
||||
'area_slug': self.object.slug
|
||||
}
|
||||
return {"park_slug": self.object.park.slug, "area_slug": self.object.slug}
|
||||
|
||||
|
||||
class ParkListView(ListView):
|
||||
model = Park
|
||||
template_name = 'parks/park_list.html'
|
||||
context_object_name = 'parks'
|
||||
template_name = "parks/park_list.html"
|
||||
context_object_name = "parks"
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Park.objects.select_related('owner', 'country', 'region', 'city').prefetch_related('photos', 'rides')
|
||||
|
||||
search = self.request.GET.get('search', '').strip()
|
||||
country = self.request.GET.get('country', '').strip()
|
||||
region = self.request.GET.get('region', '').strip()
|
||||
city = self.request.GET.get('city', '').strip()
|
||||
statuses = self.request.GET.getlist('status')
|
||||
|
||||
queryset = Park.objects.select_related(
|
||||
"owner", "country", "region", "city"
|
||||
).prefetch_related("photos", "rides")
|
||||
|
||||
search = self.request.GET.get("search", "").strip()
|
||||
country = self.request.GET.get("country", "").strip()
|
||||
region = self.request.GET.get("region", "").strip()
|
||||
city = self.request.GET.get("city", "").strip()
|
||||
statuses = self.request.GET.getlist("status")
|
||||
|
||||
if search:
|
||||
queryset = queryset.filter(
|
||||
Q(name__icontains=search) |
|
||||
Q(location__icontains=search)
|
||||
Q(name__icontains=search) | Q(location__icontains=search)
|
||||
)
|
||||
|
||||
|
||||
if country:
|
||||
queryset = queryset.filter(country__name__icontains=country)
|
||||
|
||||
|
||||
if region:
|
||||
queryset = queryset.filter(region__name__icontains=region)
|
||||
|
||||
|
||||
if city:
|
||||
queryset = queryset.filter(city__name__icontains=city)
|
||||
|
||||
|
||||
if statuses:
|
||||
queryset = queryset.filter(status__in=statuses)
|
||||
|
||||
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['current_filters'] = {
|
||||
'search': self.request.GET.get('search', ''),
|
||||
'country': self.request.GET.get('country', ''),
|
||||
'region': self.request.GET.get('region', ''),
|
||||
'city': self.request.GET.get('city', ''),
|
||||
'statuses': self.request.GET.getlist('status')
|
||||
context["current_filters"] = {
|
||||
"search": self.request.GET.get("search", ""),
|
||||
"country": self.request.GET.get("country", ""),
|
||||
"region": self.request.GET.get("region", ""),
|
||||
"city": self.request.GET.get("city", ""),
|
||||
"statuses": self.request.GET.getlist("status"),
|
||||
}
|
||||
return context
|
||||
|
||||
@@ -287,5 +310,5 @@ class ParkListView(ListView):
|
||||
# Check if this is an HTMX request
|
||||
if request.htmx:
|
||||
# If it is, return just the parks list partial
|
||||
self.template_name = 'parks/partials/park_list.html'
|
||||
self.template_name = "parks/partials/park_list.html"
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user