from django.views.generic import DetailView, ListView, CreateView 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 from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.contenttypes.models import ContentType from django.http import JsonResponse, HttpResponseRedirect, HttpResponse from .models import Park, ParkArea from rides.models import Ride from core.views import SlugRedirectMixin from moderation.mixins import EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin from moderation.models import EditSubmission import pycountry class ParkCreateView(LoginRequiredMixin, CreateView): model = Park template_name = 'parks/park_form.html' fields = ['name', 'location', 'country', 'description', 'owner', 'status', 'opening_date', 'closing_date', 'operating_season', 'size_acres', 'website'] def form_valid(self, form): # If user is moderator or above, save directly if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']: self.object = form.save() return HttpResponseRedirect(self.get_success_url()) # Otherwise, create a submission cleaned_data = form.cleaned_data.copy() # Convert model instances to IDs for JSON serialization if cleaned_data.get('owner'): cleaned_data['owner'] = cleaned_data['owner'].id submission = EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Park), submission_type='CREATE', changes=cleaned_data, reason=self.request.POST.get('reason', ''), source=self.request.POST.get('source', '') ) return HttpResponseRedirect(reverse('park_list')) def get_success_url(self): return reverse('park_detail', kwargs={'slug': self.object.slug}) def search_countries(request): query = request.GET.get('q', '').strip() countries = [] if query: # Use pycountry's search functionality for fuzzy matching try: # Try exact search first country = pycountry.countries.get(name=query) if country: countries = [country] else: # If no exact match, try fuzzy search countries = pycountry.countries.search_fuzzy(query) except LookupError: # If search fails, fallback to manual filtering countries = [ country for country in pycountry.countries if query.lower() in country.name.lower() ] return render(request, 'parks/partials/country_search_results.html', { 'countries': countries[:10] # Limit to top 10 results }) def select_country(request): if request.method == 'POST': country = request.POST.get('country', '') return HttpResponse(country) return HttpResponse('Invalid request', status=400) class ParkDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin, DetailView): model = Park template_name = 'parks/park_detail.html' context_object_name = 'park' def get_object(self, queryset=None): if queryset is None: queryset = self.get_queryset() slug = self.kwargs.get(self.slug_url_kwarg) # Try to get by current or historical slug return self.model.get_by_slug(slug)[0] 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) return context def get_redirect_url_pattern(self): return 'park_detail' class ParkAreaDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin, DetailView): model = ParkArea 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') # 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: raise self.model.DoesNotExist("Park slug doesn't match") return obj def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['rides'] = Ride.objects.filter( area=self.object ).select_related('coaster_stats') return context def get_redirect_url_pattern(self): return 'park_detail' def get_redirect_url_kwargs(self): 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' def get_queryset(self): queryset = Park.objects.select_related('owner').prefetch_related('photos', 'rides') search = self.request.GET.get('search', '').strip() or None location = self.request.GET.get('location', '').strip() or None status = self.request.GET.get('status', '').strip() or None if search: queryset = queryset.filter( Q(name__icontains=search) | Q(location__icontains=search) ) if location: queryset = queryset.filter(location=location) if status: queryset = queryset.filter(status=status) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Get unique locations for filter dropdown context['locations'] = list(Park.objects.values_list('location', flat=True) .distinct().order_by('location')) # Add current filter values to context context['current_filters'] = { 'search': self.request.GET.get('search', ''), 'location': self.request.GET.get('location', ''), 'status': self.request.GET.get('status', '') } return context def get(self, request, *args, **kwargs): # 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' return super().get(request, *args, **kwargs)