from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated from rest_framework.authentication import TokenAuthentication from django.utils import timezone from datetime import timedelta from django.db.models import Count, Q from django.db.models.functions import TruncDate from apps.account.models import User from apps.course.models import Course, Participant from apps.course.models.course import extract_text_from_json from apps.quiz.models import Quiz, QuizParticipant from apps.chat.models import ChatMessage, RoomMessage from apps.api.permissions import IsProfessorUser class ProfessorDashboardStatsView(APIView): authentication_classes = [TokenAuthentication] permission_classes = [IsAuthenticated, IsProfessorUser] def get(self, request): now = timezone.now() thirty_days_ago = now - timedelta(days=30) # --------------------------------------------------------- # 1. CARDS & BASIC STATS # --------------------------------------------------------- # Total courses taught by this professor courses_count = Course.objects.filter(professor=request.user).count() # Total unique active students enrolled in this professor's courses students_count = Participant.objects.filter( course__professor=request.user, is_active=True ).values('student').distinct().count() # Total quizzes created in this professor's courses quizzes_count = Quiz.objects.filter(course__professor=request.user).count() # Chat rooms count (group rooms for professor's courses + private chats with professor) chat_rooms_count = RoomMessage.objects.filter( Q(course__professor=request.user) | (Q(room_type=RoomMessage.RoomTypeChoices.PRIVATE) & (Q(initiator=request.user) | Q(recipient=request.user))) ).distinct().count() # --------------------------------------------------------- # 2. CHARTS DATA # --------------------------------------------------------- # Chat Volume Trend (30 days) professor_rooms = RoomMessage.objects.filter( Q(course__professor=request.user) | (Q(room_type=RoomMessage.RoomTypeChoices.PRIVATE) & (Q(initiator=request.user) | Q(recipient=request.user))) ) chat_volume = ChatMessage.objects.filter( room__in=professor_rooms, sent_at__gte=thirty_days_ago ).annotate( date=TruncDate('sent_at') ).values('date').annotate(count=Count('id')).order_by('date') chat_chart = [{"date": item['date'].strftime('%Y-%m-%d'), "messages": item['count']} for item in chat_volume] # Course Efficacy: Completed vs Enrolled (Bar Chart for Top 5 Courses) top_courses_qs = Course.objects.filter(professor=request.user).annotate( participant_count=Count('participants') ).order_by('-participant_count')[:5] course_efficacy = [] for course in top_courses_qs: enrolled = course.participant_count completed = Participant.objects.filter( course=course, student__lesson_completions__course_lesson__course=course ).distinct().count() title_text = extract_text_from_json(course.title) course_efficacy.append({ "title": title_text[:20] + "..." if len(title_text) > 20 else title_text, "enrolled": enrolled, "completed": completed }) # Quiz Participation statistics (all quizzes of the professor) quizzes = Quiz.objects.filter(course__professor=request.user) quizzes_stats = [] for quiz in quizzes: course = quiz.course if not course: continue total_enrolled = Participant.objects.filter(course=course, is_active=True).count() participated = QuizParticipant.objects.filter(quiz=quiz).values('user').distinct().count() not_participated = max(0, total_enrolled - participated) quizzes_stats.append({ "id": quiz.id, "title": quiz.title, "course_title": extract_text_from_json(course.title), "participated": participated, "not_participated": not_participated, }) # --------------------------------------------------------- # 3. LISTS & LEADERBOARDS # --------------------------------------------------------- # Top Courses List top_courses_list = [ { "id": c.id, "title": extract_text_from_json(c.title), "participants": c.participant_count } for c in top_courses_qs ] # Leaderboard (Top 5 Performing Students in professor's quizzes) top_students = QuizParticipant.objects.filter( quiz__course__professor=request.user ).select_related('user').order_by('-total_score', 'total_timing')[:5] leaderboard = [ {"id": s.user.id, "name": s.user.fullname, "score": s.total_score, "time": s.total_timing} for s in top_students ] return Response({ "cards": { "courses_count": courses_count, "students_count": students_count, "quizzes_count": quizzes_count, "chat_rooms_count": chat_rooms_count, }, "charts": { "chat_volume": chat_chart, "course_efficacy": course_efficacy, "quizzes_stats": quizzes_stats }, "lists": { "top_courses": top_courses_list, "leaderboard": leaderboard } })