144 lines
5.7 KiB
Python
144 lines
5.7 KiB
Python
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()
|