Files
aerbim-ht-monitor/backend/api/utils/report_generators.py
2025-10-07 15:24:22 +03:00

144 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import csv
from io import StringIO, BytesIO
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from django.utils import timezone
import os
FONT_PATH = os.path.join(os.path.dirname(__file__), 'fonts')
ARIAL_PATH = os.path.join(FONT_PATH, 'arialmt.ttf')
ARIAL_BOLD_PATH = os.path.join(FONT_PATH, 'arial_bolditalicmt.ttf')
if not os.path.exists(ARIAL_PATH) or not os.path.exists(ARIAL_BOLD_PATH):
raise FileNotFoundError(
f"Шрифты не найдены. Пожалуйста, убедитесь что файлы arialmt.ttf и arial_bolditalicmt.ttf "
f"находятся в директории {FONT_PATH}"
)
pdfmetrics.registerFont(TTFont('Arial', ARIAL_PATH))
pdfmetrics.registerFont(TTFont('Arial-Bold', ARIAL_BOLD_PATH))
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(
name='CustomNormal',
fontName='Arial',
fontSize=10,
leading=12,
))
styles.add(ParagraphStyle(
name='CustomHeading1',
fontName='Arial-Bold',
fontSize=14,
leading=16,
))
def generate_csv_report(alerts):
"""Генерирует CSV отчет из списка алертов"""
output = StringIO()
writer = csv.writer(output)
# заголовки
headers = ['ID', 'Датчик', 'Объект', 'Значение', 'Тип датчика', 'Сообщение',
'Уровень важности', 'Дата создания', 'Обработан']
writer.writerow(headers)
# данные
for alert in alerts:
zone = alert.sensor.zones.first()
object_name = zone.object.title if zone else 'Не указан'
writer.writerow([
alert.id,
alert.sensor.name,
object_name,
f"{alert.metric.value} {alert.sensor.signal_format.unit if alert.sensor.signal_format else ''}" if alert.metric.value else alert.metric.raw_value,
alert.sensor_type.name,
alert.message,
alert.severity,
timezone.localtime(alert.created_at).strftime("%Y-%m-%d %H:%M:%S %Z"),
'Да' if alert.resolved else 'Нет'
])
return output.getvalue()
def generate_pdf_report(alerts):
"""Генерирует PDF отчет из списка алертов"""
buffer = BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=letter)
elements = []
# заголовок
title = Paragraph(
f"Отчет по алертам (сгенерирован {timezone.localtime().strftime('%Y-%m-%d %H:%M %Z')})",
styles['CustomHeading1']
)
elements.append(title)
# данные для таблицы
headers = ['ID', 'Датчик', 'Объект', 'Значение', 'Тип датчика', 'Сообщение',
'Уровень', 'Дата создания', 'Обработан']
# оборачиваем заголовки в параграф
header_cells = [Paragraph(str(header), styles['CustomNormal']) for header in headers]
data = [header_cells]
for alert in alerts:
zone = alert.sensor.zones.first()
object_name = zone.object.title if zone else 'Не указан'
# оборачиваем каждую ячейку в параграф
row_data = [
Paragraph(str(alert.id), styles['CustomNormal']),
Paragraph(str(alert.sensor.name), styles['CustomNormal']),
Paragraph(str(object_name), styles['CustomNormal']),
Paragraph(
str(f"{alert.metric.value} {alert.sensor.signal_format.unit if alert.sensor.signal_format else ''}" if alert.metric.value else alert.metric.raw_value),
styles['CustomNormal']
),
Paragraph(str(alert.sensor_type.name), styles['CustomNormal']),
Paragraph(str(alert.message), styles['CustomNormal']),
Paragraph(str(alert.severity), styles['CustomNormal']),
Paragraph(timezone.localtime(alert.created_at).strftime("%Y-%m-%d %H:%M:%S %Z"), styles['CustomNormal']),
Paragraph('Да' if alert.resolved else 'Нет', styles['CustomNormal'])
]
data.append(row_data)
# создаем таблицу
table = Table(data)
table.setStyle(TableStyle([
# стиль заголовка
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#1b8755')),
('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTSIZE', (0, 0), (-1, 0), 11),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('TOPPADDING', (0, 0), (-1, 0), 12),
# стиль данных
('BACKGROUND', (0, 1), (-1, -1), colors.white),
('TEXTCOLOR', (0, 1), (-1, -1), colors.black),
('FONTSIZE', (0, 1), (-1, -1), 10),
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
('GRID', (0, 0), (-1, -1), 1, colors.black),
# отступы для всех ячеек
('LEFTPADDING', (0, 0), (-1, -1), 6),
('RIGHTPADDING', (0, 0), (-1, -1), 6),
('TOPPADDING', (0, 1), (-1, -1), 8),
('BOTTOMPADDING', (0, 1), (-1, -1), 8),
# чередующиеся цвета строк для лучшей читаемости
('BACKGROUND', (0, 1), (-1, -1), colors.white),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f5f5f5')])
]))
elements.append(table)
doc.build(elements)
return buffer.getvalue()