""" Selectors for park-related data retrieval. Following Django styleguide pattern for separating data access from business logic. """ from typing import Optional, Dict, Any, List from django.db.models import QuerySet, Q, F, Count, Avg, Prefetch from django.contrib.gis.geos import Point from django.contrib.gis.measure import Distance from .models import Park, ParkArea, ParkReview from rides.models import Ride def park_list_with_stats(*, filters: Optional[Dict[str, Any]] = None) -> QuerySet[Park]: """ Get parks optimized for list display with basic stats. Args: filters: Optional dictionary of filter parameters Returns: QuerySet of parks with optimized queries """ queryset = Park.objects.select_related( 'operator', 'property_owner' ).prefetch_related( 'location' ).annotate( ride_count_calculated=Count('rides', distinct=True), coaster_count_calculated=Count( 'rides', filter=Q(rides__category__in=['RC', 'WC']), distinct=True ), average_rating_calculated=Avg('reviews__rating') ) if filters: if 'status' in filters: queryset = queryset.filter(status=filters['status']) if 'operator' in filters: queryset = queryset.filter(operator=filters['operator']) if 'country' in filters: queryset = queryset.filter(location__country=filters['country']) if 'search' in filters: search_term = filters['search'] queryset = queryset.filter( Q(name__icontains=search_term) | Q(description__icontains=search_term) ) return queryset.order_by('name') def park_detail_optimized(*, slug: str) -> Park: """ Get a single park with all related data optimized for detail view. Args: slug: Park slug identifier Returns: Park instance with optimized prefetches Raises: Park.DoesNotExist: If park with slug doesn't exist """ return Park.objects.select_related( 'operator', 'property_owner' ).prefetch_related( 'location', 'areas', Prefetch( 'rides', queryset=Ride.objects.select_related('manufacturer', 'designer', 'ride_model') ), Prefetch( 'reviews', queryset=ParkReview.objects.select_related('user').filter(is_published=True) ), 'photos' ).get(slug=slug) def parks_near_location( *, point: Point, distance_km: float = 50, limit: int = 10 ) -> QuerySet[Park]: """ Get parks near a specific geographic location. Args: point: Geographic point (longitude, latitude) distance_km: Maximum distance in kilometers limit: Maximum number of results Returns: QuerySet of nearby parks ordered by distance """ return Park.objects.filter( location__coordinates__distance_lte=(point, Distance(km=distance_km)) ).select_related( 'operator' ).prefetch_related( 'location' ).distance(point).order_by('distance')[:limit] def park_statistics() -> Dict[str, Any]: """ Get overall park statistics for dashboard/analytics. Returns: Dictionary containing park statistics """ total_parks = Park.objects.count() operating_parks = Park.objects.filter(status='OPERATING').count() total_rides = Ride.objects.count() total_coasters = Ride.objects.filter(category__in=['RC', 'WC']).count() return { 'total_parks': total_parks, 'operating_parks': operating_parks, 'closed_parks': total_parks - operating_parks, 'total_rides': total_rides, 'total_coasters': total_coasters, 'average_rides_per_park': total_rides / total_parks if total_parks > 0 else 0 } def parks_by_operator(*, operator_id: int) -> QuerySet[Park]: """ Get all parks operated by a specific company. Args: operator_id: Company ID of the operator Returns: QuerySet of parks operated by the company """ return Park.objects.filter( operator_id=operator_id ).select_related( 'operator' ).prefetch_related( 'location' ).annotate( ride_count_calculated=Count('rides') ).order_by('name') def parks_with_recent_reviews(*, days: int = 30) -> QuerySet[Park]: """ Get parks that have received reviews in the last N days. Args: days: Number of days to look back for reviews Returns: QuerySet of parks with recent reviews """ from django.utils import timezone from datetime import timedelta cutoff_date = timezone.now() - timedelta(days=days) return Park.objects.filter( reviews__created_at__gte=cutoff_date, reviews__is_published=True ).select_related( 'operator' ).prefetch_related( 'location' ).annotate( recent_review_count=Count('reviews', filter=Q(reviews__created_at__gte=cutoff_date)) ).order_by('-recent_review_count').distinct() def park_search_autocomplete(*, query: str, limit: int = 10) -> QuerySet[Park]: """ Get parks matching a search query for autocomplete functionality. Args: query: Search string limit: Maximum number of results Returns: QuerySet of matching parks for autocomplete """ return Park.objects.filter( Q(name__icontains=query) | Q(location__city__icontains=query) | Q(location__region__icontains=query) ).select_related( 'operator' ).prefetch_related( 'location' ).order_by('name')[:limit] def park_areas_for_park(*, park_slug: str) -> QuerySet[ParkArea]: """ Get all areas for a specific park. Args: park_slug: Slug of the park Returns: QuerySet of park areas with related data """ return ParkArea.objects.filter( park__slug=park_slug ).select_related( 'park' ).prefetch_related( 'rides' ).annotate( ride_count=Count('rides') ).order_by('name') def park_reviews_for_park(*, park_id: int, limit: int = 20) -> QuerySet[ParkReview]: """ Get reviews for a specific park. Args: park_id: Park ID limit: Maximum number of reviews to return Returns: QuerySet of park reviews """ return ParkReview.objects.filter( park_id=park_id, is_published=True ).select_related( 'user', 'park' ).order_by('-created_at')[:limit]