mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 17:31:09 -05:00
Introduce autocomplete for park searches, optimize park data fetching with select_related and prefetch_related, add new API endpoints for autocomplete and quick filters, and refactor the park list view to use new Django Cotton components for a more dynamic and user-friendly experience. Replit-Commit-Author: Agent Replit-Commit-Session-Id: c446bc9e-66df-438c-a86c-f53e6da13649 Replit-Commit-Checkpoint-Type: intermediate_checkpoint
178 lines
6.1 KiB
Python
178 lines
6.1 KiB
Python
"""
|
|
Park search autocomplete views for enhanced search functionality.
|
|
Provides fast, cached autocomplete suggestions for park search.
|
|
"""
|
|
|
|
from typing import Dict, List, Any
|
|
from django.http import JsonResponse
|
|
from django.views import View
|
|
from django.core.cache import cache
|
|
from django.db.models import Q
|
|
from django.utils.decorators import method_decorator
|
|
from django.views.decorators.cache import cache_page
|
|
|
|
from .models import Park
|
|
from .models.companies import Company
|
|
from .services.filter_service import ParkFilterService
|
|
|
|
|
|
class ParkAutocompleteView(View):
|
|
"""
|
|
Provides autocomplete suggestions for park search.
|
|
Returns JSON with park names, operators, and location suggestions.
|
|
"""
|
|
|
|
def get(self, request):
|
|
"""Handle GET request for autocomplete suggestions."""
|
|
query = request.GET.get('q', '').strip()
|
|
|
|
if len(query) < 2:
|
|
return JsonResponse({
|
|
'suggestions': [],
|
|
'message': 'Type at least 2 characters to search'
|
|
})
|
|
|
|
# Check cache first
|
|
cache_key = f"park_autocomplete:{query.lower()}"
|
|
cached_result = cache.get(cache_key)
|
|
|
|
if cached_result:
|
|
return JsonResponse(cached_result)
|
|
|
|
# Generate suggestions
|
|
suggestions = self._get_suggestions(query)
|
|
|
|
# Cache results for 5 minutes
|
|
result = {
|
|
'suggestions': suggestions,
|
|
'query': query
|
|
}
|
|
cache.set(cache_key, result, 300)
|
|
|
|
return JsonResponse(result)
|
|
|
|
def _get_suggestions(self, query: str) -> List[Dict[str, Any]]:
|
|
"""Generate autocomplete suggestions based on query."""
|
|
suggestions = []
|
|
|
|
# Park name suggestions (top 5)
|
|
park_suggestions = self._get_park_suggestions(query)
|
|
suggestions.extend(park_suggestions)
|
|
|
|
# Operator suggestions (top 3)
|
|
operator_suggestions = self._get_operator_suggestions(query)
|
|
suggestions.extend(operator_suggestions)
|
|
|
|
# Location suggestions (top 3)
|
|
location_suggestions = self._get_location_suggestions(query)
|
|
suggestions.extend(location_suggestions)
|
|
|
|
# Remove duplicates and limit results
|
|
seen = set()
|
|
unique_suggestions = []
|
|
for suggestion in suggestions:
|
|
key = suggestion['name'].lower()
|
|
if key not in seen:
|
|
seen.add(key)
|
|
unique_suggestions.append(suggestion)
|
|
|
|
return unique_suggestions[:10] # Limit to 10 suggestions
|
|
|
|
def _get_park_suggestions(self, query: str) -> List[Dict[str, Any]]:
|
|
"""Get park name suggestions."""
|
|
parks = Park.objects.filter(
|
|
name__icontains=query,
|
|
status='OPERATING'
|
|
).select_related('operator').order_by('name')[:5]
|
|
|
|
suggestions = []
|
|
for park in parks:
|
|
suggestion = {
|
|
'name': park.name,
|
|
'type': 'park',
|
|
'operator': park.operator.name if park.operator else None,
|
|
'url': f'/parks/{park.slug}/' if park.slug else None
|
|
}
|
|
suggestions.append(suggestion)
|
|
|
|
return suggestions
|
|
|
|
def _get_operator_suggestions(self, query: str) -> List[Dict[str, Any]]:
|
|
"""Get operator suggestions."""
|
|
operators = Company.objects.filter(
|
|
roles__contains=['OPERATOR'],
|
|
name__icontains=query
|
|
).order_by('name')[:3]
|
|
|
|
suggestions = []
|
|
for operator in operators:
|
|
suggestion = {
|
|
'name': operator.name,
|
|
'type': 'operator',
|
|
'park_count': operator.operated_parks.filter(status='OPERATING').count()
|
|
}
|
|
suggestions.append(suggestion)
|
|
|
|
return suggestions
|
|
|
|
def _get_location_suggestions(self, query: str) -> List[Dict[str, Any]]:
|
|
"""Get location (city/country) suggestions."""
|
|
# Get unique cities
|
|
city_parks = Park.objects.filter(
|
|
location__city__icontains=query,
|
|
status='OPERATING'
|
|
).select_related('location').order_by('location__city').distinct()[:2]
|
|
|
|
# Get unique countries
|
|
country_parks = Park.objects.filter(
|
|
location__country__icontains=query,
|
|
status='OPERATING'
|
|
).select_related('location').order_by('location__country').distinct()[:2]
|
|
|
|
suggestions = []
|
|
|
|
# Add city suggestions
|
|
for park in city_parks:
|
|
if park.location and park.location.city:
|
|
city_name = park.location.city
|
|
if park.location.country:
|
|
city_name += f", {park.location.country}"
|
|
|
|
suggestion = {
|
|
'name': city_name,
|
|
'type': 'location',
|
|
'location_type': 'city'
|
|
}
|
|
suggestions.append(suggestion)
|
|
|
|
# Add country suggestions
|
|
for park in country_parks:
|
|
if park.location and park.location.country:
|
|
suggestion = {
|
|
'name': park.location.country,
|
|
'type': 'location',
|
|
'location_type': 'country'
|
|
}
|
|
suggestions.append(suggestion)
|
|
|
|
return suggestions
|
|
|
|
|
|
@method_decorator(cache_page(60 * 5), name='dispatch') # Cache for 5 minutes
|
|
class QuickFilterSuggestionsView(View):
|
|
"""
|
|
Provides quick filter suggestions and popular filters.
|
|
Used for search dropdown quick actions.
|
|
"""
|
|
|
|
def get(self, request):
|
|
"""Handle GET request for quick filter suggestions."""
|
|
filter_service = ParkFilterService()
|
|
popular_filters = filter_service.get_popular_filters()
|
|
filter_counts = filter_service.get_filter_counts()
|
|
|
|
return JsonResponse({
|
|
'quick_filters': popular_filters.get('quick_filters', []),
|
|
'filter_counts': filter_counts,
|
|
'recommended_sorts': popular_filters.get('recommended_sorts', [])
|
|
}) |