Files
thrillwiki_django_no_react/accounts/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

227 lines
6.9 KiB
Python

"""
Selectors for user and account-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.auth import get_user_model
from django.utils import timezone
from datetime import timedelta
User = get_user_model()
def user_profile_optimized(*, user_id: int) -> Any:
"""
Get a user with optimized queries for profile display.
Args:
user_id: User ID
Returns:
User instance with prefetched related data
Raises:
User.DoesNotExist: If user doesn't exist
"""
return User.objects.prefetch_related(
'park_reviews',
'ride_reviews',
'socialaccount_set'
).annotate(
park_review_count=Count('park_reviews', filter=Q(park_reviews__is_published=True)),
ride_review_count=Count('ride_reviews', filter=Q(ride_reviews__is_published=True)),
total_review_count=F('park_review_count') + F('ride_review_count')
).get(id=user_id)
def active_users_with_stats() -> QuerySet:
"""
Get active users with review statistics.
Returns:
QuerySet of active users with review counts
"""
return User.objects.filter(
is_active=True
).annotate(
park_review_count=Count('park_reviews', filter=Q(park_reviews__is_published=True)),
ride_review_count=Count('ride_reviews', filter=Q(ride_reviews__is_published=True)),
total_review_count=F('park_review_count') + F('ride_review_count')
).order_by('-total_review_count')
def users_with_recent_activity(*, days: int = 30) -> QuerySet:
"""
Get users who have been active in the last N days.
Args:
days: Number of days to look back for activity
Returns:
QuerySet of recently active users
"""
cutoff_date = timezone.now() - timedelta(days=days)
return User.objects.filter(
Q(last_login__gte=cutoff_date) |
Q(park_reviews__created_at__gte=cutoff_date) |
Q(ride_reviews__created_at__gte=cutoff_date)
).annotate(
recent_park_reviews=Count('park_reviews', filter=Q(park_reviews__created_at__gte=cutoff_date)),
recent_ride_reviews=Count('ride_reviews', filter=Q(ride_reviews__created_at__gte=cutoff_date)),
recent_total_reviews=F('recent_park_reviews') + F('recent_ride_reviews')
).order_by('-last_login').distinct()
def top_reviewers(*, limit: int = 10) -> QuerySet:
"""
Get top users by review count.
Args:
limit: Maximum number of users to return
Returns:
QuerySet of top reviewers
"""
return User.objects.filter(
is_active=True
).annotate(
park_review_count=Count('park_reviews', filter=Q(park_reviews__is_published=True)),
ride_review_count=Count('ride_reviews', filter=Q(ride_reviews__is_published=True)),
total_review_count=F('park_review_count') + F('ride_review_count')
).filter(
total_review_count__gt=0
).order_by('-total_review_count')[:limit]
def moderator_users() -> QuerySet:
"""
Get users with moderation permissions.
Returns:
QuerySet of users who can moderate content
"""
return User.objects.filter(
Q(is_staff=True) |
Q(groups__name='Moderators') |
Q(user_permissions__codename__in=['change_parkreview', 'change_ridereview'])
).distinct().order_by('username')
def users_by_registration_date(*, start_date, end_date) -> QuerySet:
"""
Get users who registered within a date range.
Args:
start_date: Start of date range
end_date: End of date range
Returns:
QuerySet of users registered in the date range
"""
return User.objects.filter(
date_joined__date__gte=start_date,
date_joined__date__lte=end_date
).order_by('-date_joined')
def user_search_autocomplete(*, query: str, limit: int = 10) -> QuerySet:
"""
Get users matching a search query for autocomplete functionality.
Args:
query: Search string
limit: Maximum number of results
Returns:
QuerySet of matching users for autocomplete
"""
return User.objects.filter(
Q(username__icontains=query) |
Q(first_name__icontains=query) |
Q(last_name__icontains=query),
is_active=True
).order_by('username')[:limit]
def users_with_social_accounts() -> QuerySet:
"""
Get users who have connected social accounts.
Returns:
QuerySet of users with social account connections
"""
return User.objects.filter(
socialaccount__isnull=False
).prefetch_related(
'socialaccount_set'
).distinct().order_by('username')
def user_statistics_summary() -> Dict[str, Any]:
"""
Get overall user statistics for dashboard/analytics.
Returns:
Dictionary containing user statistics
"""
total_users = User.objects.count()
active_users = User.objects.filter(is_active=True).count()
staff_users = User.objects.filter(is_staff=True).count()
# Users with reviews
users_with_reviews = User.objects.filter(
Q(park_reviews__isnull=False) |
Q(ride_reviews__isnull=False)
).distinct().count()
# Recent registrations (last 30 days)
cutoff_date = timezone.now() - timedelta(days=30)
recent_registrations = User.objects.filter(
date_joined__gte=cutoff_date
).count()
return {
'total_users': total_users,
'active_users': active_users,
'inactive_users': total_users - active_users,
'staff_users': staff_users,
'users_with_reviews': users_with_reviews,
'recent_registrations': recent_registrations,
'review_participation_rate': (users_with_reviews / total_users * 100) if total_users > 0 else 0
}
def users_needing_email_verification() -> QuerySet:
"""
Get users who haven't verified their email addresses.
Returns:
QuerySet of users with unverified emails
"""
return User.objects.filter(
is_active=True,
emailaddress__verified=False
).distinct().order_by('date_joined')
def users_by_review_activity(*, min_reviews: int = 1) -> QuerySet:
"""
Get users who have written at least a minimum number of reviews.
Args:
min_reviews: Minimum number of reviews required
Returns:
QuerySet of users with sufficient review activity
"""
return User.objects.annotate(
park_review_count=Count('park_reviews', filter=Q(park_reviews__is_published=True)),
ride_review_count=Count('ride_reviews', filter=Q(ride_reviews__is_published=True)),
total_review_count=F('park_review_count') + F('ride_review_count')
).filter(
total_review_count__gte=min_reviews
).order_by('-total_review_count')