feat: complete monorepo structure with frontend and shared resources

- Add complete backend/ directory with full Django application
- Add frontend/ directory with Vite + TypeScript setup ready for Next.js
- Add comprehensive shared/ directory with:
  - Complete documentation and memory-bank archives
  - Media files and avatars (letters, park/ride images)
  - Deployment scripts and automation tools
  - Shared types and utilities
- Add architecture/ directory with migration guides
- Configure pnpm workspace for monorepo development
- Update .gitignore to exclude .django_tailwind_cli/ build artifacts
- Preserve all historical documentation in shared/docs/memory-bank/
- Set up proper structure for full-stack development with shared resources
This commit is contained in:
pacnpal
2025-08-23 18:40:07 -04:00
parent b0e0678590
commit d504d41de2
762 changed files with 142636 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
import requests
from django.core.management.base import BaseCommand
from media.models import Photo
from parks.models import Park
from rides.models import Ride
from django.contrib.contenttypes.models import ContentType
import json
from django.core.files.base import ContentFile
class Command(BaseCommand):
help = "Download photos from seed data URLs"
def handle(self, *args, **kwargs):
self.stdout.write("Downloading photos from seed data...")
# Read seed data
with open("parks/management/commands/seed_data.json", "r") as f:
seed_data = json.load(f)
park_content_type = ContentType.objects.get_for_model(Park)
ride_content_type = ContentType.objects.get_for_model(Ride)
# Process parks and their photos
for park_data in seed_data["parks"]:
try:
park = Park.objects.get(name=park_data["name"])
# Download park photos
for idx, photo_url in enumerate(park_data["photos"], 1):
try:
# Download image
self.stdout.write(f"Downloading from URL: {photo_url}")
response = requests.get(photo_url, timeout=60)
if response.status_code == 200:
# Delete any existing photos for this park
Photo.objects.filter(
content_type=park_content_type,
object_id=park.id,
).delete()
# Create new photo record
photo = Photo(
content_type=park_content_type,
object_id=park.id,
is_primary=idx == 1,
)
# Save image content
photo.image.save(
f"{park.slug}_{idx}.jpg",
ContentFile(response.content),
save=False,
)
photo.save()
self.stdout.write(
f"Downloaded photo for {
park.name}: {
photo.image.name}"
)
self.stdout.write(
f"Database record created with ID: {photo.id}"
)
else:
self.stdout.write(
f"Error downloading image. Status code: {
response.status_code}"
)
except Exception as e:
self.stdout.write(
f"Error downloading park photo: {
str(e)}"
)
# Process rides and their photos
for ride_data in park_data["rides"]:
try:
ride = Ride.objects.get(name=ride_data["name"], park=park)
# Download ride photos
for idx, photo_url in enumerate(ride_data["photos"], 1):
try:
# Download image
self.stdout.write(f"Downloading from URL: {photo_url}")
response = requests.get(photo_url, timeout=60)
if response.status_code == 200:
# Delete any existing photos for this ride
Photo.objects.filter(
content_type=ride_content_type,
object_id=ride.id,
).delete()
# Create new photo record
photo = Photo(
content_type=ride_content_type,
object_id=ride.id,
is_primary=idx == 1,
)
# Save image content
photo.image.save(
f"{ride.slug}_{idx}.jpg",
ContentFile(response.content),
save=False,
)
photo.save()
self.stdout.write(
f"Downloaded photo for {
ride.name}: {
photo.image.name}"
)
self.stdout.write(
f"Database record created with ID: {
photo.id}"
)
else:
self.stdout.write(
f"Error downloading image. Status code: {
response.status_code}"
)
except Exception as e:
self.stdout.write(
f"Error downloading ride photo: {str(e)}"
)
except Ride.DoesNotExist:
self.stdout.write(
f'Ride not found: {
ride_data["name"]}'
)
except Park.DoesNotExist:
self.stdout.write(f'Park not found: {park_data["name"]}')
self.stdout.write("Finished downloading photos")

View File

@@ -0,0 +1,77 @@
import os
from django.core.management.base import BaseCommand
from media.models import Photo
from django.db import transaction
class Command(BaseCommand):
help = "Fix photo paths in database to match actual file locations"
def handle(self, *args, **kwargs):
self.stdout.write("Fixing photo paths in database...")
# Get all photos
photos = Photo.objects.all()
for photo in photos:
try:
with transaction.atomic():
# Get current file path
current_name = photo.image.name
# Remove any 'media/' prefix if it exists
if current_name.startswith("media/"):
# Remove 'media/' prefix
current_name = current_name[6:]
parts = current_name.split("/")
if len(parts) >= 2:
content_type = parts[0] # 'park' or 'ride'
identifier = parts[1] # e.g., 'alton-towers'
# Look for files in the media directory
media_dir = os.path.join("media", content_type, identifier)
if os.path.exists(media_dir):
files = [
f
for f in os.listdir(media_dir)
if not f.startswith(".") # Skip hidden files
and not f.startswith("tmp") # Skip temp files
and os.path.isfile(os.path.join(media_dir, f))
]
if files:
# Get the first file and update the database
# record
file_path = os.path.join(
content_type, identifier, files[0]
)
if os.path.exists(os.path.join("media", file_path)):
photo.image.name = file_path
photo.save()
self.stdout.write(
f"Updated path for photo {
photo.id} to {file_path}"
)
else:
self.stdout.write(
f"File not found for photo {
photo.id}: {file_path}"
)
else:
self.stdout.write(
f"No files found in directory for photo {
photo.id}: {media_dir}"
)
else:
self.stdout.write(
f"Directory not found for photo {
photo.id}: {media_dir}"
)
except Exception as e:
self.stdout.write(f"Error updating photo {photo.id}: {str(e)}")
continue
self.stdout.write("Finished fixing photo paths")

View File

@@ -0,0 +1,116 @@
import os
from django.core.management.base import BaseCommand
from media.models import Photo
from django.conf import settings
import shutil
class Command(BaseCommand):
help = "Move photo files to their normalized locations"
def handle(self, *args, **kwargs):
self.stdout.write("Moving photo files to normalized locations...")
# Get all photos
photos = Photo.objects.all()
# Track processed files to clean up later
processed_files = set()
for photo in photos:
try:
# Get current file path
current_name = photo.image.name
current_path = os.path.join(settings.MEDIA_ROOT, current_name)
# Try to find the actual file
if not os.path.exists(current_path):
# Check if file exists in the old location structure
parts = current_name.split("/")
if len(parts) >= 2:
content_type = parts[0] # 'park' or 'ride'
identifier = parts[1] # e.g., 'alton-towers'
# Look for any files in that directory
old_dir = os.path.join(
settings.MEDIA_ROOT, content_type, identifier
)
if os.path.exists(old_dir):
files = [
f
for f in os.listdir(old_dir)
if not f.startswith(".") # Skip hidden files
and not f.startswith("tmp") # Skip temp files
and os.path.isfile(os.path.join(old_dir, f))
]
if files:
current_path = os.path.join(old_dir, files[0])
# Skip if file still not found
if not os.path.exists(current_path):
self.stdout.write(f"Skipping {current_name} - file not found")
continue
# Get content type and object
content_type_model = photo.content_type.model
obj = photo.content_object
identifier = getattr(obj, "slug", obj.id)
# Get photo number
photo_number = Photo.objects.filter(
content_type=photo.content_type,
object_id=photo.object_id,
created_at__lte=photo.created_at,
).count()
# Create new filename
_, ext = os.path.splitext(current_path)
if not ext:
ext = ".jpg"
ext = ext.lower()
new_filename = f"{identifier}_{photo_number}{ext}"
# Create new path
new_relative_path = f"{content_type_model}/{identifier}/{new_filename}"
new_full_path = os.path.join(settings.MEDIA_ROOT, new_relative_path)
# Create directory if it doesn't exist
os.makedirs(os.path.dirname(new_full_path), exist_ok=True)
# Move the file
if current_path != new_full_path:
shutil.copy2(
current_path, new_full_path
) # Use copy2 to preserve metadata
processed_files.add(current_path)
else:
processed_files.add(current_path)
# Update database
photo.image.name = new_relative_path
photo.save()
self.stdout.write(f"Moved {current_name} to {new_relative_path}")
except Exception as e:
self.stdout.write(f"Error moving photo {photo.id}: {str(e)}")
continue
# Clean up old files
self.stdout.write("Cleaning up old files...")
for content_type in ["park", "ride"]:
base_dir = os.path.join(settings.MEDIA_ROOT, content_type)
if os.path.exists(base_dir):
for root, dirs, files in os.walk(base_dir):
for file in files:
file_path = os.path.join(root, file)
if file_path not in processed_files:
try:
os.remove(file_path)
self.stdout.write(f"Removed old file: {file_path}")
except Exception as e:
self.stdout.write(
f"Error removing {file_path}: {str(e)}"
)
self.stdout.write("Finished moving photo files and cleaning up")