from typing import Any, Dict, Optional, Tuple, Union, cast, Type from django.views.generic import DetailView, ListView, CreateView, UpdateView, RedirectView from django.shortcuts import get_object_or_404, render from django.core.serializers.json import DjangoJSONEncoder from django.urls import reverse from django.db.models import Q, Model from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.decorators import login_required from django.contrib.contenttypes.models import ContentType from django.contrib import messages from django.http import ( JsonResponse, HttpResponseRedirect, Http404, HttpRequest, HttpResponse, ) from django.db.models import Count from django.core.files.uploadedfile import UploadedFile from django.forms import ModelForm from django.db.models.query import QuerySet from simple_history.models import HistoricalRecords from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from .models import Ride, RollerCoasterStats, RideModel, CATEGORY_CHOICES from .forms import RideForm from parks.models import Park from core.views import SlugRedirectMixin from moderation.mixins import EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin from moderation.models import EditSubmission from media.models import Photo from accounts.models import User from companies.models import Manufacturer from designers.models import Designer def show_coaster_fields(request: HttpRequest) -> HttpResponse: """Show roller coaster specific fields based on category selection""" category = request.GET.get('category') if category != 'RC': # Only show for roller coasters return HttpResponse('') return render(request, "rides/partials/coaster_fields.html") class RideCreateView(LoginRequiredMixin, CreateView): """View for creating a new ride""" model = Ride form_class = RideForm template_name = 'rides/ride_form.html' def get_success_url(self): """Get URL to redirect to after successful creation""" if hasattr(self, 'park'): return reverse('parks:rides:ride_detail', kwargs={ 'park_slug': self.park.slug, 'ride_slug': self.object.slug }) return reverse('rides:ride_detail', kwargs={'ride_slug': self.object.slug}) def get_form_kwargs(self): """Pass park to the form""" kwargs = super().get_form_kwargs() if 'park_slug' in self.kwargs: self.park = get_object_or_404(Park, slug=self.kwargs['park_slug']) kwargs['park'] = self.park return kwargs def get_context_data(self, **kwargs): """Add park and park_slug to context""" context = super().get_context_data(**kwargs) if hasattr(self, 'park'): context['park'] = self.park context['park_slug'] = self.park.slug context['is_edit'] = False return context def form_valid(self, form): """Handle form submission including new items""" # Check for new manufacturer manufacturer_name = form.cleaned_data.get('manufacturer_search') if manufacturer_name and not form.cleaned_data.get('manufacturer'): # Create submission for new manufacturer EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Manufacturer), submission_type="CREATE", changes={"name": manufacturer_name}, ) # Check for new designer designer_name = form.cleaned_data.get('designer_search') if designer_name and not form.cleaned_data.get('designer'): # Create submission for new designer EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Designer), submission_type="CREATE", changes={"name": designer_name}, ) # Check for new ride model ride_model_name = form.cleaned_data.get('ride_model_search') manufacturer = form.cleaned_data.get('manufacturer') if ride_model_name and not form.cleaned_data.get('ride_model') and manufacturer: # Create submission for new ride model EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(RideModel), submission_type="CREATE", changes={ "name": ride_model_name, "manufacturer": manufacturer.id }, ) return super().form_valid(form) class RideDetailView(DetailView): """View for displaying ride details""" model = Ride template_name = 'rides/ride_detail.html' slug_url_kwarg = 'ride_slug' def get_queryset(self): """Get ride for the specific park if park_slug is provided""" queryset = Ride.objects.all().select_related( 'park', 'ride_model', 'ride_model__manufacturer' ).prefetch_related('photos') if 'park_slug' in self.kwargs: queryset = queryset.filter(park__slug=self.kwargs['park_slug']) return queryset def get_context_data(self, **kwargs): """Add park_slug to context if it exists""" context = super().get_context_data(**kwargs) if 'park_slug' in self.kwargs: context['park_slug'] = self.kwargs['park_slug'] return context class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView): """View for updating an existing ride""" model = Ride form_class = RideForm template_name = 'rides/ride_form.html' slug_url_kwarg = 'ride_slug' def get_success_url(self): """Get URL to redirect to after successful update""" if hasattr(self, 'park'): return reverse('parks:rides:ride_detail', kwargs={ 'park_slug': self.park.slug, 'ride_slug': self.object.slug }) return reverse('rides:ride_detail', kwargs={'ride_slug': self.object.slug}) def get_queryset(self): """Get ride for the specific park if park_slug is provided""" queryset = Ride.objects.all() if 'park_slug' in self.kwargs: queryset = queryset.filter(park__slug=self.kwargs['park_slug']) return queryset def get_form_kwargs(self): """Pass park to the form""" kwargs = super().get_form_kwargs() # For park-specific URLs, use the park from the URL if 'park_slug' in self.kwargs: self.park = get_object_or_404(Park, slug=self.kwargs['park_slug']) kwargs['park'] = self.park # For global URLs, use the ride's park else: self.park = self.get_object().park kwargs['park'] = self.park return kwargs def get_context_data(self, **kwargs): """Add park and park_slug to context""" context = super().get_context_data(**kwargs) if hasattr(self, 'park'): context['park'] = self.park context['park_slug'] = self.park.slug context['is_edit'] = True return context def form_valid(self, form): """Handle form submission including new items""" # Check for new manufacturer manufacturer_name = form.cleaned_data.get('manufacturer_search') if manufacturer_name and not form.cleaned_data.get('manufacturer'): # Create submission for new manufacturer EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Manufacturer), submission_type="CREATE", changes={"name": manufacturer_name}, ) # Check for new designer designer_name = form.cleaned_data.get('designer_search') if designer_name and not form.cleaned_data.get('designer'): # Create submission for new designer EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Designer), submission_type="CREATE", changes={"name": designer_name}, ) # Check for new ride model ride_model_name = form.cleaned_data.get('ride_model_search') manufacturer = form.cleaned_data.get('manufacturer') if ride_model_name and not form.cleaned_data.get('ride_model') and manufacturer: # Create submission for new ride model EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(RideModel), submission_type="CREATE", changes={ "name": ride_model_name, "manufacturer": manufacturer.id }, ) return super().form_valid(form) class RideListView(ListView): """View for displaying a list of rides""" model = Ride template_name = 'rides/ride_list.html' context_object_name = 'rides' def get_queryset(self): """Get all rides or filter by park if park_slug is provided""" queryset = Ride.objects.all().select_related( 'park', 'ride_model', 'ride_model__manufacturer' ).prefetch_related('photos') if 'park_slug' in self.kwargs: self.park = get_object_or_404(Park, slug=self.kwargs['park_slug']) queryset = queryset.filter(park=self.park) return queryset def get_context_data(self, **kwargs): """Add park to context if park_slug is provided""" context = super().get_context_data(**kwargs) if hasattr(self, 'park'): context['park'] = self.park context['park_slug'] = self.kwargs['park_slug'] return context class SingleCategoryListView(ListView): """View for displaying rides of a specific category""" model = Ride template_name = 'rides/park_category_list.html' context_object_name = 'rides' def get_queryset(self): """Get rides filtered by category and optionally by park""" category = self.kwargs.get('category') queryset = Ride.objects.filter( category=category ).select_related( 'park', 'ride_model', 'ride_model__manufacturer' ) if 'park_slug' in self.kwargs: self.park = get_object_or_404(Park, slug=self.kwargs['park_slug']) queryset = queryset.filter(park=self.park) return queryset def get_context_data(self, **kwargs): """Add park and category information to context""" context = super().get_context_data(**kwargs) if hasattr(self, 'park'): context['park'] = self.park context['park_slug'] = self.kwargs['park_slug'] context['category'] = dict(CATEGORY_CHOICES).get(self.kwargs['category']) return context # Alias for parks app to maintain backward compatibility ParkSingleCategoryListView = SingleCategoryListView def is_privileged_user(user: Any) -> bool: """Check if user has privileged access""" return bool(user and hasattr(user, 'is_staff') and (user.is_staff or user.is_superuser)) @login_required def search_manufacturers(request: HttpRequest) -> HttpResponse: """Search manufacturers and return results for HTMX""" query = request.GET.get("q", "").strip() # Show all manufacturers on click, filter on input manufacturers = Manufacturer.objects.all().order_by("name") if query: manufacturers = manufacturers.filter(name__icontains=query) manufacturers = manufacturers[:10] return render( request, "rides/partials/manufacturer_search_results.html", {"manufacturers": manufacturers, "search_term": query}, ) @login_required def search_designers(request: HttpRequest) -> HttpResponse: """Search designers and return results for HTMX""" query = request.GET.get("q", "").strip() # Show all designers on click, filter on input designers = Designer.objects.all().order_by("name") if query: designers = designers.filter(name__icontains=query) designers = designers[:10] return render( request, "rides/partials/designer_search_results.html", {"designers": designers, "search_term": query}, ) @login_required def search_ride_models(request: HttpRequest) -> HttpResponse: """Search ride models and return results for HTMX""" query = request.GET.get("q", "").strip() manufacturer_id = request.GET.get("manufacturer") # Show all ride models on click, filter on input ride_models = RideModel.objects.select_related("manufacturer").order_by("name") if query: ride_models = ride_models.filter(name__icontains=query) if manufacturer_id: ride_models = ride_models.filter(manufacturer_id=manufacturer_id) ride_models = ride_models[:10] return render( request, "rides/partials/ride_model_search_results.html", {"ride_models": ride_models, "search_term": query, "manufacturer_id": manufacturer_id}, )