""" Selectors for user and account-related data retrieval. Following Django styleguide pattern for separating data access from business logic. """ from datetime import timedelta from typing import Any from django.contrib.auth import get_user_model from django.db.models import Count, F, Q, QuerySet from django.utils import timezone 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(display_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") )