You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
5.8 KiB
139 lines
5.8 KiB
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
|
|
}
|
|
})
|