mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 06:51:08 -05:00
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.
This commit is contained in:
226
accounts/selectors.py
Normal file
226
accounts/selectors.py
Normal file
@@ -0,0 +1,226 @@
|
||||
"""
|
||||
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')
|
||||
Reference in New Issue
Block a user