179 lines
7.8 KiB
Python
179 lines
7.8 KiB
Python
from rest_framework import status
|
||
from rest_framework.viewsets import ViewSet
|
||
from rest_framework.permissions import IsAuthenticated
|
||
from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiExample, inline_serializer, OpenApiParameter
|
||
from rest_framework import serializers
|
||
from rest_framework.decorators import action
|
||
from rest_framework.response import Response
|
||
from django.http import HttpResponse
|
||
from django.db.models import Case, When, IntegerField
|
||
from api.account.serializers.alert_serializers import AlertSerializer
|
||
from sitemanagement.models import Alert
|
||
from api.utils.decorators import handle_exceptions
|
||
from api.utils.error_serializer import ErrorResponseSerializer
|
||
from api.utils.report_generators import generate_csv_report, generate_pdf_report
|
||
from django.utils import timezone
|
||
|
||
@extend_schema(tags=['Алерты'])
|
||
class AlertView(ViewSet):
|
||
# permission_classes = [IsAuthenticated]
|
||
|
||
@extend_schema(
|
||
summary="Получение списка алертов",
|
||
description="Возвращает список алертов в системе с фильтрацией по уровню тревоги. Алерты сортируются: сначала warning, затем critical",
|
||
parameters=[
|
||
OpenApiParameter(
|
||
name='severity',
|
||
description='Фильтр по уровню тревоги',
|
||
required=False,
|
||
type=str,
|
||
enum=['warning', 'critical']
|
||
)
|
||
],
|
||
responses={
|
||
200: OpenApiResponse(response=AlertSerializer(many=True), description="Список алертов успешно получен",
|
||
examples=[OpenApiExample(
|
||
'Успешный ответ',
|
||
value=[{
|
||
"id": 1,
|
||
"name": "Датчик 1",
|
||
"object": "Объект 1",
|
||
"metric_value": "12.5 °C",
|
||
"sensor_type_name": "Инклинометр",
|
||
"message": "alert message",
|
||
"severity": "warning",
|
||
"created_at": "2025-10-06T15:53:11.759725+03:00",
|
||
"resolved": False
|
||
}]
|
||
)])
|
||
})
|
||
|
||
@action(detail=False, methods=['get'])
|
||
@handle_exceptions
|
||
def get_alerts(self, request):
|
||
alerts = Alert.objects.all()
|
||
|
||
# фильтрация по severity
|
||
severity = request.query_params.get('severity')
|
||
if severity and severity in ['warning', 'critical']:
|
||
alerts = alerts.filter(severity=severity)
|
||
|
||
# сортировка: warning первыми, потом critical
|
||
alerts = alerts.annotate(
|
||
severity_order=Case(
|
||
When(severity='critical', then=1),
|
||
When(severity='warning', then=2),
|
||
default=3,
|
||
output_field=IntegerField()
|
||
)
|
||
).order_by('severity_order', '-created_at')
|
||
|
||
serializer = AlertSerializer(alerts, many=True)
|
||
return Response(serializer.data)
|
||
|
||
@extend_schema(
|
||
summary="Изменение статуса алерта",
|
||
description="Изменяет статус обработки алерта на противоположный",
|
||
responses={
|
||
200: OpenApiResponse(response=AlertSerializer, description="Статус алерта успешно изменен",
|
||
examples=[OpenApiExample(
|
||
'Успешный ответ',
|
||
value={
|
||
"message": "Статус алерта успешно изменен"
|
||
}
|
||
)]),
|
||
404: OpenApiResponse(
|
||
response=ErrorResponseSerializer,
|
||
description="Алерт не найден",
|
||
examples=[
|
||
OpenApiExample(
|
||
'Алерт не найден',
|
||
value={"error": "Алерт не найден"},
|
||
status_codes=['404']
|
||
)
|
||
]
|
||
)
|
||
})
|
||
@action(detail=True, methods=['patch'])
|
||
@handle_exceptions
|
||
def change_alert_status(self, request, pk=None):
|
||
try:
|
||
alert = Alert.objects.get(pk=pk)
|
||
alert.resolved = not alert.resolved
|
||
alert.save()
|
||
|
||
return Response({"message": "Статус алерта успешно изменен"}, status=status.HTTP_200_OK)
|
||
|
||
except Alert.DoesNotExist:
|
||
return Response(
|
||
{"error": "Алерт не найден"},
|
||
status=status.HTTP_404_NOT_FOUND)
|
||
|
||
@extend_schema(tags=['Репорты'])
|
||
class ReportView(ViewSet):
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
@extend_schema(
|
||
summary="Генерация отчета",
|
||
description="Генерирует отчет в выбранном формате (PDF или CSV)",
|
||
request={'application/json': {'type': 'object', 'properties': {'report_format': {'type': 'string', 'enum': ['pdf', 'csv']}}}},
|
||
methods=['POST'],
|
||
responses={
|
||
200: OpenApiResponse(
|
||
response=inline_serializer(
|
||
name='BinaryFile',
|
||
fields={
|
||
'file': serializers.FileField()
|
||
}
|
||
),
|
||
description="Файл отчета для скачивания в формате PDF или CSV"
|
||
),
|
||
400: OpenApiResponse(
|
||
response=ErrorResponseSerializer,
|
||
description="Неверный формат",
|
||
examples=[OpenApiExample(
|
||
'Неверный формат',
|
||
value={"error": "Неверный формат"},
|
||
status_codes=['400']
|
||
)]
|
||
)
|
||
})
|
||
@action(detail=False, methods=['post'])
|
||
@handle_exceptions
|
||
def get_reports(self, request):
|
||
"""Генерация отчета в выбранном формате"""
|
||
report_format = request.data.get('report_format', '').lower()
|
||
|
||
if not report_format:
|
||
report_format = request.query_params.get('format', '').lower()
|
||
|
||
if report_format not in ["pdf", "csv"]:
|
||
return Response(
|
||
{"error": "Неверный формат. Допустимые значения: pdf, csv"},
|
||
status=status.HTTP_400_BAD_REQUEST
|
||
)
|
||
|
||
alerts = Alert.objects.select_related(
|
||
'sensor',
|
||
'sensor__signal_format',
|
||
'sensor_type',
|
||
'metric'
|
||
).prefetch_related(
|
||
'sensor__zones',
|
||
'sensor__zones__object'
|
||
).all()
|
||
|
||
# текущая дата для имени файла
|
||
timestamp = timezone.now().strftime("%Y%m%d_%H%M%S")
|
||
|
||
if report_format == "csv":
|
||
response = HttpResponse(content_type='text/csv')
|
||
response['Content-Disposition'] = f'attachment; filename="alerts_report_{timestamp}.csv"'
|
||
response.write(generate_csv_report(alerts))
|
||
return response
|
||
|
||
else: # pdf
|
||
response = HttpResponse(content_type='application/pdf')
|
||
response['Content-Disposition'] = f'attachment; filename="alerts_report_{timestamp}.pdf"'
|
||
response.write(generate_pdf_report(alerts))
|
||
return response |