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.
103 lines
4.5 KiB
103 lines
4.5 KiB
import logging
|
|
from django.db.models import Count, Avg, Sum
|
|
from rest_framework import status
|
|
from rest_framework.views import APIView
|
|
from rest_framework.generics import GenericAPIView
|
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
|
from rest_framework.response import Response
|
|
from drf_spectacular.utils import extend_schema
|
|
|
|
from apps.profiles.models import Institution
|
|
from apps.projects.models import Project
|
|
from apps.lms.models import Course, CourseEnrollment, DigitalCertificate
|
|
from apps.tickets.models import Ticket, TicketStatus
|
|
from apps.donations.models import DonationCampaign
|
|
from apps.diplomacy.models import RepresentativeReport, ReportStatus
|
|
from apps.analytics.serializers import ExecutiveDashboardSerializer
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ExecutiveDashboardView(GenericAPIView):
|
|
permission_classes = [AllowAny]
|
|
serializer_class = ExecutiveDashboardSerializer
|
|
|
|
@extend_schema(
|
|
summary="Executive KPIs & Global Analytics Dashboard",
|
|
description="Aggregates platform KPIs across international institutions, collaborative projects, LMS academy, official ticketing, campaigns, and field diplomacy reports.",
|
|
responses={200: ExecutiveDashboardSerializer},
|
|
tags=["Executive Analytics & KPIs"],
|
|
)
|
|
def get(self, request, *args, **kwargs):
|
|
# 1. Institutions
|
|
inst_qs = Institution.objects.all()
|
|
total_inst = inst_qs.count()
|
|
verified_inst = inst_qs.filter(verification_status=Institution.VerificationStatus.APPROVED).count()
|
|
countries_count = inst_qs.values('country').distinct().count()
|
|
|
|
type_counts_raw = inst_qs.values('type').annotate(count=Count('id'))
|
|
by_type = {item['type']: item['count'] for item in type_counts_raw}
|
|
|
|
top_countries_raw = inst_qs.values('country').annotate(count=Count('id')).order_by('-count')[:8]
|
|
top_countries = [{'country': item['country'], 'count': item['count']} for item in top_countries_raw]
|
|
|
|
institutions_data = {
|
|
'total_institutions': total_inst,
|
|
'verified_institutions': verified_inst,
|
|
'countries_count': countries_count,
|
|
'by_type': by_type,
|
|
'top_countries': top_countries,
|
|
}
|
|
|
|
# 2. Projects
|
|
proj_qs = Project.objects.all()
|
|
projects_data = {
|
|
'total_projects': proj_qs.count(),
|
|
'active_projects': proj_qs.filter(status=Project.Status.ACTIVE).count(),
|
|
'completed_projects': proj_qs.filter(status=Project.Status.COMPLETED).count(),
|
|
}
|
|
|
|
# 3. LMS
|
|
lms_data = {
|
|
'total_courses': Course.objects.filter(is_published=True).count(),
|
|
'total_enrollments': CourseEnrollment.objects.count(),
|
|
'total_certificates_issued': DigitalCertificate.objects.filter(is_revoked=False).count(),
|
|
}
|
|
|
|
# 4. Ticketing
|
|
tickets_qs = Ticket.objects.all()
|
|
avg_rating = tickets_qs.filter(satisfaction_rating__isnull=False).aggregate(avg=Avg('satisfaction_rating'))['avg']
|
|
ticketing_data = {
|
|
'total_tickets': tickets_qs.count(),
|
|
'open_tickets': tickets_qs.filter(status__in=[TicketStatus.OPEN, TicketStatus.IN_PROGRESS]).count(),
|
|
'answered_or_closed': tickets_qs.filter(status__in=[TicketStatus.ANSWERED, TicketStatus.CLOSED]).count(),
|
|
'average_satisfaction_rating': round(avg_rating, 2) if avg_rating else None,
|
|
}
|
|
|
|
# 5. Donations
|
|
campaigns_qs = DonationCampaign.objects.filter(is_active=True)
|
|
total_funds = DonationCampaign.objects.aggregate(total=Sum('raised_amount'))['total'] or 0.0
|
|
donations_data = {
|
|
'active_campaigns_count': campaigns_qs.count(),
|
|
'total_funds_raised_usd': float(total_funds),
|
|
}
|
|
|
|
# 6. Diplomacy
|
|
reports_qs = RepresentativeReport.objects.all()
|
|
total_visited = reports_qs.aggregate(total=Sum('centers_visited_count'))['total'] or 0
|
|
diplomacy_data = {
|
|
'total_reports_submitted': reports_qs.count(),
|
|
'approved_reports_count': reports_qs.filter(status=ReportStatus.APPROVED).count(),
|
|
'total_centers_visited': total_visited,
|
|
}
|
|
|
|
dashboard_data = {
|
|
'institutions': institutions_data,
|
|
'projects': projects_data,
|
|
'lms': lms_data,
|
|
'ticketing': ticketing_data,
|
|
'donations': donations_data,
|
|
'diplomacy': diplomacy_data,
|
|
}
|
|
|
|
return Response(dashboard_data, status=status.HTTP_200_OK)
|