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()