from rest_framework import status from rest_framework.views import APIView from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiExample, OpenApiParameter from api.utils.decorators import handle_exceptions from datetime import timedelta from django.utils import timezone from django.db.models import Count from django.db.models.functions import TruncHour from sitemanagement.models import Alert from api.account.serializers.dashboard_serializers import DashboardSerializer @extend_schema(tags=['Дашборд']) class DashboardView(APIView): permission_classes = [IsAuthenticated] @extend_schema( summary="Получение статистики алертов", description="Возвращает статистику алертов за выбранный период времени", parameters=[OpenApiParameter( name='time_period', type=str, location=OpenApiParameter.QUERY, description='Период времени в часах', required=False, enum=['24', '72', '168', '720'], default='168' )], responses = { 200: OpenApiResponse(response=DashboardSerializer, description="Данные для дашборда успешно получены", examples=[OpenApiExample( 'Успешный ответ', value={ "chart_data": [ {"timestamp": "2025-10-07T10:00:00+03:00", "value": 5}, {"timestamp": "2025-10-07T11:00:00+03:00", "value": 3} ], "table_data": [ { "id": 1, "name": "GA-1", "object": "Объект 1", "metric_value": "23.5 °C", "sensor_type_name": "Датчик температуры", "message": "Превышение температуры", "severity": "warning", "created_at": "2025-10-07T10:30:00+03:00", "resolved": False } ] } )]) } ) @handle_exceptions def get(self, request): time_period = request.query_params.get('time_period', '168') if time_period not in ['720', '168', '72', '24']: return Response( {"error": "Неверный период. Допустимые значения: 720, 168, 72, 24"}, status=status.HTTP_400_BAD_REQUEST ) # определяем начальную дату start_date = timezone.now() - timedelta(hours=int(time_period)) # получаем все алерты за период с фронта alerts = Alert.objects.filter(created_at__gte=start_date) # данные для графиков (группировка по часам) alerts_by_hour = alerts.annotate( hour=TruncHour('created_at') ).values('hour').annotate( count=Count('id') ).order_by('hour') # !! обязательно локальное время chart_data = [] for entry in alerts_by_hour: local_time = timezone.localtime(entry['hour']) chart_data.append({ "timestamp": local_time.isoformat(), "value": entry['count'] }) # данные для таблицы с алертами alerts_list = alerts.select_related( 'sensor', 'sensor_type', 'metric', 'sensor__signal_format' ).prefetch_related( 'sensor__zones', 'sensor__zones__object' ).order_by('-created_at') dashboard_data = { "chart_data": chart_data, "table_data": alerts_list } serializer = DashboardSerializer(instance=dashboard_data) return Response(serializer.data, status=status.HTTP_200_OK)