Enhance park search with autocomplete and improved filtering options

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
This commit is contained in:
pac7
2025-09-23 21:44:12 +00:00
parent 7feb7c462d
commit 4c954fff6f
13 changed files with 1588 additions and 245 deletions

View File

@@ -0,0 +1,178 @@
"""
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', [])
})