Files
thrillwiki_django_no_react/rides/selectors.py
pacnpal c26414ff74 Add comprehensive tests for Parks API and models
- Implemented extensive test cases for the Parks API, covering endpoints for listing, retrieving, creating, updating, and deleting parks.
- Added tests for filtering, searching, and ordering parks in the API.
- Created tests for error handling in the API, including malformed JSON and unsupported methods.
- Developed model tests for Park, ParkArea, Company, and ParkReview models, ensuring validation and constraints are enforced.
- Introduced utility mixins for API and model testing to streamline assertions and enhance test readability.
- Included integration tests to validate complete workflows involving park creation, retrieval, updating, and deletion.
2025-08-17 19:36:20 -04:00

317 lines
8.3 KiB
Python

"""
Selectors for ride-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 Ride, RideModel, RideReview
from parks.models import Park
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',
'location'
).annotate(
average_rating_calculated=Avg('reviews__rating')
)
if filters:
if 'status' in filters:
queryset = queryset.filter(status=filters['status'])
if 'category' in filters:
queryset = queryset.filter(category=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',
'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(
'location'
).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')