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

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)