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.
153 lines
6.9 KiB
153 lines
6.9 KiB
from rest_framework.views import APIView
|
|
from rest_framework.response import Response
|
|
from rest_framework.permissions import IsAuthenticated, IsAdminUser
|
|
from django.utils import timezone
|
|
from datetime import timedelta
|
|
from django.db.models import Count, Sum
|
|
from django.db.models.functions import TruncDate
|
|
|
|
from apps.account.models import User, LoginHistory
|
|
from apps.course.models import Course, Participant, CourseLiveSession
|
|
from apps.quiz.models import QuizParticipant
|
|
from apps.blog.models import Blog
|
|
from apps.chat.models import ChatMessage, RoomMessage
|
|
from apps.transaction.models import TransactionParticipant
|
|
from apps.certificate.models import Certificate
|
|
|
|
class AdminDashboardStatsView(APIView):
|
|
permission_classes = [IsAuthenticated, IsAdminUser]
|
|
|
|
def get(self, request):
|
|
now = timezone.now()
|
|
thirty_days_ago = now - timedelta(days=30)
|
|
|
|
# ---------------------------------------------------------
|
|
# 1. CARDS & BASIC STATS
|
|
# ---------------------------------------------------------
|
|
active_students = User.objects.filter(groups__name="Student Group", is_active=True).count()
|
|
professors = User.objects.filter(groups__name="Professor Group", is_active=True).count()
|
|
active_courses = Course.objects.filter(status=Course.StatusChoices.ONGOING).count()
|
|
total_blogs = Blog.objects.count()
|
|
pending_certificates = Certificate.objects.filter(status='pending').count()
|
|
active_live_sessions = CourseLiveSession.objects.filter(ended_at__isnull=True).count()
|
|
|
|
# Revenue (Mocked for 30d sum)
|
|
revenue_30d = 12500.00 # Dummy data
|
|
|
|
# Private vs Group Chat Usage
|
|
chat_types = RoomMessage.objects.values('room_type').annotate(count=Count('id'))
|
|
chat_type_stats = {item['room_type']: item['count'] for item in chat_types}
|
|
|
|
# ---------------------------------------------------------
|
|
# 2. CHARTS DATA
|
|
# ---------------------------------------------------------
|
|
|
|
# Payment Methods (Pie Chart)
|
|
payment_methods = TransactionParticipant.objects.values('payment_method').annotate(total=Count('id'))
|
|
payment_method_chart = [
|
|
{"name": item['payment_method'], "value": item['total']}
|
|
for item in payment_methods
|
|
]
|
|
|
|
# Transaction Status (Donut Chart)
|
|
transaction_status = TransactionParticipant.objects.values('status').annotate(total=Count('id'))
|
|
transaction_chart = [
|
|
{"name": item['status'], "value": item['total']}
|
|
for item in transaction_status
|
|
]
|
|
|
|
# Global Student Distribution (Bar Chart)
|
|
top_countries = LoginHistory.objects.exclude(country__isnull=True).exclude(country="").values('country').annotate(
|
|
count=Count('id')
|
|
).order_by('-count')[:5]
|
|
country_chart = [{"country": item['country'], "students": item['count']} for item in top_countries]
|
|
|
|
# New User Registrations Trend (Line Chart)
|
|
registrations = User.objects.filter(date_joined__gte=thirty_days_ago).annotate(
|
|
date=TruncDate('date_joined')
|
|
).values('date').annotate(count=Count('id')).order_by('date')
|
|
registration_chart = [{"date": item['date'].strftime('%Y-%m-%d'), "users": item['count']} for item in registrations]
|
|
|
|
# Community Chat Volume Trend (Area Chart)
|
|
chat_volume = ChatMessage.objects.filter(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 Pipeline (Stacked Bar)
|
|
course_statuses = Course.objects.values('status').annotate(count=Count('id'))
|
|
course_pipeline = {"name": "Courses"}
|
|
for status in course_statuses:
|
|
course_pipeline[status['status']] = status['count']
|
|
|
|
# Course Efficacy: Completed vs Enrolled (Bar Chart)
|
|
# We take the top 5 courses, check how many users are enrolled, and how many have at least 1 completed lesson
|
|
top_courses_qs = Course.objects.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()
|
|
course_efficacy.append({
|
|
"title": course.title[:20] + "..." if len(course.title) > 20 else course.title,
|
|
"enrolled": enrolled,
|
|
"completed": completed
|
|
})
|
|
|
|
# ---------------------------------------------------------
|
|
# 3. LISTS & LEADERBOARDS
|
|
# ---------------------------------------------------------
|
|
|
|
# Top 5 Courses (List)
|
|
top_courses_list = [
|
|
{
|
|
"id": c.id,
|
|
"title": c.title,
|
|
"professor": c.professor.fullname if c.professor else "Unknown",
|
|
"participants": c.participant_count
|
|
} for c in top_courses_qs
|
|
]
|
|
|
|
# Top 5 Blogs (List)
|
|
top_blogs = Blog.objects.order_by('-views_count')[:5]
|
|
top_blogs_list = [
|
|
{"id": b.id, "title": b._extract_text_from_json(b.title), "views": b.views_count}
|
|
for b in top_blogs
|
|
]
|
|
|
|
# Top Performing Students (Leaderboard)
|
|
top_students = QuizParticipant.objects.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": {
|
|
"active_students": active_students,
|
|
"professors": professors,
|
|
"active_courses": active_courses,
|
|
"total_blogs": total_blogs,
|
|
"revenue_30d": revenue_30d,
|
|
"pending_certificates": pending_certificates,
|
|
"active_live_sessions": active_live_sessions,
|
|
"group_rooms": chat_type_stats.get('group', 0),
|
|
"private_rooms": chat_type_stats.get('private', 0),
|
|
},
|
|
"charts": {
|
|
"transactions_status": transaction_chart,
|
|
"payment_methods": payment_method_chart,
|
|
"countries": country_chart,
|
|
"registrations": registration_chart,
|
|
"chat_volume": chat_chart,
|
|
"course_pipeline": [course_pipeline],
|
|
"course_efficacy": course_efficacy
|
|
},
|
|
"lists": {
|
|
"top_courses": top_courses_list,
|
|
"top_blogs": top_blogs_list,
|
|
"leaderboard": leaderboard
|
|
}
|
|
})
|