""" Physicians API views and viewsets """ from django.db.models import Avg, Count, Q from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from apps.accounts.permissions import IsPXAdminOrHospitalAdmin from apps.organizations.models import Staff from .models import PhysicianMonthlyRating from .serializers import ( PhysicianLeaderboardSerializer, PhysicianMonthlyRatingSerializer, PhysicianPerformanceSerializer, PhysicianSerializer, ) class PhysicianViewSet(viewsets.ReadOnlyModelViewSet): """ ViewSet for Physicians. Permissions: - All authenticated users can view physicians - Filtered by hospital based on user role """ queryset = Staff.objects.all() serializer_class = PhysicianSerializer permission_classes = [IsAuthenticated] filterset_fields = ['hospital', 'department', 'specialization', 'status'] search_fields = ['first_name', 'last_name', 'license_number', 'specialization'] ordering_fields = ['last_name', 'first_name', 'specialization', 'created_at'] ordering = ['last_name', 'first_name'] def get_queryset(self): """Filter physicians based on user role""" queryset = super().get_queryset().select_related('hospital', 'department') user = self.request.user # PX Admins see all physicians if user.is_px_admin(): return queryset # Hospital Admins and staff see physicians for their hospital if user.hospital: return queryset.filter(hospital=user.hospital) return queryset.none() @action(detail=True, methods=['get']) def performance(self, request, pk=None): """ Get physician performance summary. GET /api/physicians/{id}/performance/ Returns: - Current month rating - Previous month rating - Year-to-date average - Best/worst months - Trend analysis """ physician = self.get_object() from django.utils import timezone now = timezone.now() current_year = now.year current_month = now.month # Get current month rating current_month_rating = PhysicianMonthlyRating.objects.filter( staff=physician, year=current_year, month=current_month ).first() # Get previous month rating prev_month = current_month - 1 if current_month > 1 else 12 prev_year = current_year if current_month > 1 else current_year - 1 previous_month_rating = PhysicianMonthlyRating.objects.filter( staff=physician, year=prev_year, month=prev_month ).first() # Get year-to-date stats ytd_ratings = PhysicianMonthlyRating.objects.filter( staff=physician, year=current_year ) ytd_stats = ytd_ratings.aggregate( avg_rating=Avg('average_rating'), total_surveys=Count('id') ) # Get best and worst months (last 12 months) last_12_months = PhysicianMonthlyRating.objects.filter( staff=physician ).order_by('-year', '-month')[:12] best_month = last_12_months.order_by('-average_rating').first() worst_month = last_12_months.order_by('average_rating').first() # Determine trend trend = 'stable' if current_month_rating and previous_month_rating: if current_month_rating.average_rating > previous_month_rating.average_rating: trend = 'improving' elif current_month_rating.average_rating < previous_month_rating.average_rating: trend = 'declining' # Build response data = { 'physician': PhysicianSerializer(physician).data, 'current_month_rating': PhysicianMonthlyRatingSerializer(current_month_rating).data if current_month_rating else None, 'previous_month_rating': PhysicianMonthlyRatingSerializer(previous_month_rating).data if previous_month_rating else None, 'year_to_date_average': ytd_stats['avg_rating'], 'total_surveys_ytd': ytd_stats['total_surveys'], 'best_month': PhysicianMonthlyRatingSerializer(best_month).data if best_month else None, 'worst_month': PhysicianMonthlyRatingSerializer(worst_month).data if worst_month else None, 'trend': trend } serializer = PhysicianPerformanceSerializer(data) return Response(serializer.data) @action(detail=True, methods=['get']) def ratings_history(self, request, pk=None): """ Get physician ratings history. GET /api/physicians/{id}/ratings_history/?months=12 Returns monthly ratings for the specified number of months. """ physician = self.get_object() months = int(request.query_params.get('months', 12)) ratings = PhysicianMonthlyRating.objects.filter( staff=physician ).order_by('-year', '-month')[:months] serializer = PhysicianMonthlyRatingSerializer(ratings, many=True) return Response(serializer.data) class PhysicianMonthlyRatingViewSet(viewsets.ReadOnlyModelViewSet): """ ViewSet for Physician Monthly Ratings. Permissions: - All authenticated users can view ratings - Filtered by hospital based on user role """ queryset = PhysicianMonthlyRating.objects.all() serializer_class = PhysicianMonthlyRatingSerializer permission_classes = [IsAuthenticated] filterset_fields = ['staff', 'year', 'month', 'staff__hospital', 'staff__department'] search_fields = ['staff__first_name', 'staff__last_name', 'staff__license_number'] ordering_fields = ['year', 'month', 'average_rating', 'total_surveys', 'hospital_rank', 'department_rank'] ordering = ['-year', '-month', '-average_rating'] def get_queryset(self): """Filter ratings based on user role""" queryset = super().get_queryset().select_related( 'staff', 'staff__hospital', 'staff__department' ) user = self.request.user # PX Admins see all ratings if user.is_px_admin(): return queryset # Hospital Admins and staff see ratings for their hospital if user.hospital: return queryset.filter(staff__hospital=user.hospital) return queryset.none() @action(detail=False, methods=['get']) def leaderboard(self, request): """ Get physician leaderboard. GET /api/physicians/ratings/leaderboard/?year=2024&month=12&hospital={id}&department={id}&limit=10 Returns top-rated physicians for the specified period. """ from django.utils import timezone # Get parameters year = int(request.query_params.get('year', timezone.now().year)) month = int(request.query_params.get('month', timezone.now().month)) hospital_id = request.query_params.get('hospital') department_id = request.query_params.get('department') limit = int(request.query_params.get('limit', 10)) # Build queryset queryset = PhysicianMonthlyRating.objects.filter( year=year, month=month ).select_related('staff', 'staff__hospital', 'staff__department') # Apply RBAC filters user = request.user if not user.is_px_admin() and user.hospital: queryset = queryset.filter(staff__hospital=user.hospital) # Apply filters if hospital_id: queryset = queryset.filter(staff__hospital_id=hospital_id) if department_id: queryset = queryset.filter(staff__department_id=department_id) # Order by rating and limit queryset = queryset.order_by('-average_rating')[:limit] # Get previous month for trend prev_month = month - 1 if month > 1 else 12 prev_year = year if month > 1 else year - 1 # Build leaderboard data leaderboard = [] for rank, rating in enumerate(queryset, start=1): # Get previous month rating for trend prev_rating = PhysicianMonthlyRating.objects.filter( staff=rating.staff, year=prev_year, month=prev_month ).first() trend = 'stable' if prev_rating: if rating.average_rating > prev_rating.average_rating: trend = 'up' elif rating.average_rating < prev_rating.average_rating: trend = 'down' leaderboard.append({ 'physician_id': rating.staff.id, 'physician_name': rating.staff.get_full_name(), 'physician_license': rating.staff.license_number, 'specialization': rating.staff.specialization, 'department_name': rating.staff.department.name if rating.staff.department else '', 'average_rating': rating.average_rating, 'total_surveys': rating.total_surveys, 'rank': rank, 'trend': trend }) serializer = PhysicianLeaderboardSerializer(leaderboard, many=True) return Response(serializer.data) @action(detail=False, methods=['get']) def statistics(self, request): """ Get physician rating statistics. GET /api/physicians/ratings/statistics/?year=2024&month=12&hospital={id} Returns aggregate statistics for the specified period. """ from django.utils import timezone # Get parameters year = int(request.query_params.get('year', timezone.now().year)) month = int(request.query_params.get('month', timezone.now().month)) hospital_id = request.query_params.get('hospital') # Build queryset queryset = PhysicianMonthlyRating.objects.filter( year=year, month=month ) # Apply RBAC filters user = request.user if not user.is_px_admin() and user.hospital: queryset = queryset.filter(staff__hospital=user.hospital) # Apply filters if hospital_id: queryset = queryset.filter(staff__hospital_id=hospital_id) # Calculate statistics stats = queryset.aggregate( total_physicians=Count('id'), average_rating=Avg('average_rating'), total_surveys=Count('total_surveys'), total_positive=Count('positive_count'), total_neutral=Count('neutral_count'), total_negative=Count('negative_count') ) # Get distribution excellent = queryset.filter(average_rating__gte=4.5).count() good = queryset.filter(average_rating__gte=3.5, average_rating__lt=4.5).count() average = queryset.filter(average_rating__gte=2.5, average_rating__lt=3.5).count() poor = queryset.filter(average_rating__lt=2.5).count() return Response({ 'year': year, 'month': month, 'total_physicians': stats['total_physicians'], 'average_rating': stats['average_rating'], 'total_surveys': stats['total_surveys'], 'distribution': { 'excellent': excellent, # 4.5+ 'good': good, # 3.5-4.5 'average': average, # 2.5-3.5 'poor': poor # <2.5 }, 'sentiment': { 'positive': stats['total_positive'], 'neutral': stats['total_neutral'], 'negative': stats['total_negative'] } })