Browse Source

proffessor-special dashboard endpoint and views added.

master
Mohsen Taba 2 months ago
parent
commit
10b2c35eab
  1. 131
      apps/api/permissions.py
  2. 4
      apps/api/views/__init__.py
  3. 139
      apps/api/views/professor_dashboard.py
  4. 3
      config/urls.py

131
apps/api/permissions.py

@ -1,60 +1,71 @@
from rest_framework import permissions
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
class SwaggerTokenPermission(permissions.BasePermission):
"""
Custom permission for Swagger that allows access to authenticated users via token
or admin users via session authentication
"""
def has_permission(self, request, view):
# Check if user is admin (for session-based access)
if request.user and request.user.is_authenticated and request.user.is_staff:
return True
# Check for token in session (from our custom auth system)
swagger_token = request.session.get('swagger_token')
if swagger_token:
try:
token_obj = Token.objects.get(key=swagger_token)
if token_obj.user.is_active:
return True
except Token.DoesNotExist:
pass
# Check for Authorization header
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
if auth_header.startswith('Token '):
token = auth_header.split(' ')[1]
try:
token_obj = Token.objects.get(key=token)
if token_obj.user.is_active:
return True
except Token.DoesNotExist:
pass
return False
class IsAdminOrSwaggerToken(permissions.BasePermission):
"""
Permission that allows access to admin users or users with valid swagger token
"""
def has_permission(self, request, view):
# Allow admin users
if request.user and request.user.is_authenticated and request.user.is_staff:
return True
# Check swagger token in session
swagger_token = request.session.get('swagger_token')
if swagger_token:
try:
token_obj = Token.objects.get(key=swagger_token)
return token_obj.user.is_active
except Token.DoesNotExist:
pass
return False
from rest_framework import permissions
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
class SwaggerTokenPermission(permissions.BasePermission):
"""
Custom permission for Swagger that allows access to authenticated users via token
or admin users via session authentication
"""
def has_permission(self, request, view):
# Check if user is admin (for session-based access)
if request.user and request.user.is_authenticated and request.user.is_staff:
return True
# Check for token in session (from our custom auth system)
swagger_token = request.session.get('swagger_token')
if swagger_token:
try:
token_obj = Token.objects.get(key=swagger_token)
if token_obj.user.is_active:
return True
except Token.DoesNotExist:
pass
# Check for Authorization header
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
if auth_header.startswith('Token '):
token = auth_header.split(' ')[1]
try:
token_obj = Token.objects.get(key=token)
if token_obj.user.is_active:
return True
except Token.DoesNotExist:
pass
return False
class IsAdminOrSwaggerToken(permissions.BasePermission):
"""
Permission that allows access to admin users or users with valid swagger token
"""
def has_permission(self, request, view):
# Allow admin users
if request.user and request.user.is_authenticated and request.user.is_staff:
return True
# Check swagger token in session
swagger_token = request.session.get('swagger_token')
if swagger_token:
try:
token_obj = Token.objects.get(key=swagger_token)
return token_obj.user.is_active
except Token.DoesNotExist:
pass
return False
class IsProfessorUser(permissions.BasePermission):
"""
Permission that allows access only to professors or admin users.
"""
def has_permission(self, request, view):
if not (request.user and request.user.is_authenticated):
return False
return request.user.user_type == 'professor' or request.user.is_staff

4
apps/api/views/__init__.py

@ -5,6 +5,7 @@ from .api_views import HomeView, CountryView, CommentListAPIView
from .documentation import CustomAPIDocumentationView from .documentation import CustomAPIDocumentationView
from .swagger_views import CustomSwaggerView, SwaggerTokenAuthView, clear_swagger_auth from .swagger_views import CustomSwaggerView, SwaggerTokenAuthView, clear_swagger_auth
from .admin_dashboard import AdminDashboardStatsView from .admin_dashboard import AdminDashboardStatsView
from .professor_dashboard import ProfessorDashboardStatsView
__all__ = [ __all__ = [
'HomeView', 'HomeView',
'CountryView', 'CountryView',
@ -13,5 +14,6 @@ __all__ = [
'CustomSwaggerView', 'CustomSwaggerView',
'SwaggerTokenAuthView', 'SwaggerTokenAuthView',
'clear_swagger_auth', 'clear_swagger_auth',
'AdminDashboardStatsView'
'AdminDashboardStatsView',
'ProfessorDashboardStatsView'
] ]

139
apps/api/views/professor_dashboard.py

@ -0,0 +1,139 @@
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
}
})

3
config/urls.py

@ -38,7 +38,7 @@ import requests
from filer import views from filer import views
# Import custom API views # Import custom API views
from apps.api.views import CustomAPIDocumentationView, CustomSwaggerView, SwaggerTokenAuthView, clear_swagger_auth ,admin_dashboard
from apps.api.views import CustomAPIDocumentationView, CustomSwaggerView, SwaggerTokenAuthView, clear_swagger_auth ,admin_dashboard, ProfessorDashboardStatsView
from apps.api.permissions import IsAdminOrSwaggerToken from apps.api.permissions import IsAdminOrSwaggerToken
from apps.api.decorators import swagger_auth_required from apps.api.decorators import swagger_auth_required
@ -85,6 +85,7 @@ api_patterns = [
path('calendar/', include('apps.dobodbi_calendar.urls')), path('calendar/', include('apps.dobodbi_calendar.urls')),
path('blog/', include('apps.blog.urls')), path('blog/', include('apps.blog.urls')),
path('admin/dashboard-stats/' , admin_dashboard.AdminDashboardStatsView.as_view()), path('admin/dashboard-stats/' , admin_dashboard.AdminDashboardStatsView.as_view()),
path('professor/dashboard-stats/' , ProfessorDashboardStatsView.as_view()),
path('settings/', include('dynamic_preferences.urls')), path('settings/', include('dynamic_preferences.urls')),

Loading…
Cancel
Save