""" Selectors for ride-related data retrieval. Following Django styleguide pattern for separating data access from business logic. """ from typing import Optional, Dict, Any from django.db.models import QuerySet, Q, Count, Avg, Prefetch from django.contrib.gis.geos import Point from django.contrib.gis.measure import Distance from .models import Ride, RideModel, RideReview, CATEGORY_CHOICES def ride_list_for_display( *, filters: Optional[Dict[str, Any]] = None ) -> QuerySet[Ride]: """ Get rides optimized for list display with related data. Args: filters: Optional dictionary of filter parameters Returns: QuerySet of rides with optimized queries """ queryset = ( Ride.objects.select_related( "park", "park__operator", "manufacturer", "designer", "ride_model", "park_area", ) .prefetch_related("park__location") .annotate(average_rating_calculated=Avg("reviews__rating")) ) if filters: if "status" in filters and filters["status"]: queryset = queryset.filter(status__in=filters["status"]) if "category" in filters and filters["category"]: queryset = queryset.filter(category__in=filters["category"]) if "manufacturer" in filters: queryset = queryset.filter(manufacturer=filters["manufacturer"]) if "park" in filters: queryset = queryset.filter(park=filters["park"]) if "search" in filters: search_term = filters["search"] queryset = queryset.filter( Q(name__icontains=search_term) | Q(description__icontains=search_term) | Q(park__name__icontains=search_term) ) return queryset.order_by("park__name", "name") def ride_detail_optimized(*, slug: str, park_slug: str) -> Ride: """ Get a single ride with all related data optimized for detail view. Args: slug: Ride slug identifier park_slug: Park slug for the ride Returns: Ride instance with optimized prefetches Raises: Ride.DoesNotExist: If ride doesn't exist """ return ( Ride.objects.select_related( "park", "park__operator", "manufacturer", "designer", "ride_model", "park_area", ) .prefetch_related( "park__location", Prefetch( "reviews", queryset=RideReview.objects.select_related("user").filter( is_published=True ), ), "photos", ) .get(slug=slug, park__slug=park_slug) ) def rides_by_category(*, category: str) -> QuerySet[Ride]: """ Get all rides in a specific category. Args: category: Ride category code Returns: QuerySet of rides in the category """ return ( Ride.objects.filter(category=category) .select_related("park", "manufacturer", "designer") .prefetch_related("park__location") .annotate(average_rating_calculated=Avg("reviews__rating")) .order_by("park__name", "name") ) def rides_by_manufacturer(*, manufacturer_id: int) -> QuerySet[Ride]: """ Get all rides manufactured by a specific company. Args: manufacturer_id: Company ID of the manufacturer Returns: QuerySet of rides by the manufacturer """ return ( Ride.objects.filter(manufacturer_id=manufacturer_id) .select_related("park", "manufacturer", "ride_model") .prefetch_related("park__location") .annotate(average_rating_calculated=Avg("reviews__rating")) .order_by("park__name", "name") ) def rides_by_designer(*, designer_id: int) -> QuerySet[Ride]: """ Get all rides designed by a specific company. Args: designer_id: Company ID of the designer Returns: QuerySet of rides by the designer """ return ( Ride.objects.filter(designer_id=designer_id) .select_related("park", "designer", "ride_model") .prefetch_related("park__location") .annotate(average_rating_calculated=Avg("reviews__rating")) .order_by("park__name", "name") ) def rides_in_park(*, park_slug: str) -> QuerySet[Ride]: """ Get all rides in a specific park. Args: park_slug: Slug of the park Returns: QuerySet of rides in the park """ return ( Ride.objects.filter(park__slug=park_slug) .select_related("manufacturer", "designer", "ride_model", "park_area") .prefetch_related() .annotate(average_rating_calculated=Avg("reviews__rating")) .order_by("park_area__name", "name") ) def rides_near_location( *, point: Point, distance_km: float = 50, limit: int = 10 ) -> QuerySet[Ride]: """ Get rides 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 rides ordered by distance """ return ( Ride.objects.filter( park__location__coordinates__distance_lte=( point, Distance(km=distance_km), ) ) .select_related("park", "manufacturer") .prefetch_related("park__location") .distance(point) .order_by("distance")[:limit] ) def ride_models_with_installations() -> QuerySet[RideModel]: """ Get ride models that have installations with counts. Returns: QuerySet of ride models with installation counts """ return ( RideModel.objects.annotate(installation_count=Count("rides")) .filter(installation_count__gt=0) .select_related("manufacturer") .order_by("-installation_count", "name") ) def ride_search_autocomplete(*, query: str, limit: int = 10) -> QuerySet[Ride]: """ Get rides matching a search query for autocomplete functionality. Args: query: Search string limit: Maximum number of results Returns: QuerySet of matching rides for autocomplete """ return ( Ride.objects.filter( Q(name__icontains=query) | Q(park__name__icontains=query) | Q(manufacturer__name__icontains=query) ) .select_related("park", "manufacturer") .prefetch_related("park__location") .order_by("park__name", "name")[:limit] ) def rides_with_recent_reviews(*, days: int = 30) -> QuerySet[Ride]: """ Get rides that have received reviews in the last N days. Args: days: Number of days to look back for reviews Returns: QuerySet of rides with recent reviews """ from django.utils import timezone from datetime import timedelta cutoff_date = timezone.now() - timedelta(days=days) return ( Ride.objects.filter( reviews__created_at__gte=cutoff_date, reviews__is_published=True ) .select_related("park", "manufacturer") .prefetch_related("park__location") .annotate( recent_review_count=Count( "reviews", filter=Q(reviews__created_at__gte=cutoff_date) ) ) .order_by("-recent_review_count") .distinct() ) def ride_statistics_by_category() -> Dict[str, Any]: """ Get ride statistics grouped by category. Returns: Dictionary containing ride statistics by category """ from .models import CATEGORY_CHOICES stats = {} for category_code, category_name in CATEGORY_CHOICES: if category_code: # Skip empty choice count = Ride.objects.filter(category=category_code).count() stats[category_code] = {"name": category_name, "count": count} return stats def rides_by_opening_year(*, year: int) -> QuerySet[Ride]: """ Get rides that opened in a specific year. Args: year: The opening year Returns: QuerySet of rides that opened in the specified year """ return ( Ride.objects.filter(opening_date__year=year) .select_related("park", "manufacturer") .prefetch_related("park__location") .order_by("opening_date", "park__name", "name") )