""" Alert Digests - PDF-Generierung und E-Mail-Versand. """ import io import logging from ..db.models import AlertDigestDB logger = logging.getLogger(__name__) async def generate_pdf_from_html(html_content: str) -> bytes: """ Generiere PDF aus HTML. Verwendet WeasyPrint oder wkhtmltopdf als Fallback. """ try: from weasyprint import HTML pdf_bytes = HTML(string=html_content).write_pdf() return pdf_bytes except ImportError: pass try: import pdfkit pdf_bytes = pdfkit.from_string(html_content, False) return pdf_bytes except ImportError: pass try: from xhtml2pdf import pisa result = io.BytesIO() pisa.CreatePDF(io.StringIO(html_content), dest=result) return result.getvalue() except ImportError: pass raise ImportError( "Keine PDF-Bibliothek verfuegbar. " "Installieren Sie: pip install weasyprint oder pip install pdfkit oder pip install xhtml2pdf" ) async def send_digest_by_email(digest: AlertDigestDB, recipient_email: str): """ Versende Digest per E-Mail. Verwendet: - Lokalen SMTP-Server (Postfix/Sendmail) - SMTP-Relay (z.B. SES, Mailgun) - SendGrid API """ import os import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication msg = MIMEMultipart('alternative') msg['Subject'] = f"Wochenbericht: {digest.period_start.strftime('%d.%m.%Y')} - {digest.period_end.strftime('%d.%m.%Y')}" msg['From'] = os.getenv('SMTP_FROM', 'alerts@breakpilot.app') msg['To'] = recipient_email text_content = f""" BreakPilot Alerts - Wochenbericht Zeitraum: {digest.period_start.strftime('%d.%m.%Y')} - {digest.period_end.strftime('%d.%m.%Y')} Gesamt: {digest.total_alerts} Meldungen Kritisch: {digest.critical_count} Dringend: {digest.urgent_count} Oeffnen Sie die HTML-Version fuer die vollstaendige Uebersicht. --- Diese E-Mail wurde automatisch von BreakPilot Alerts generiert. """ msg.attach(MIMEText(text_content, 'plain', 'utf-8')) if digest.summary_html: msg.attach(MIMEText(digest.summary_html, 'html', 'utf-8')) try: pdf_bytes = await generate_pdf_from_html(digest.summary_html) pdf_attachment = MIMEApplication(pdf_bytes, _subtype='pdf') pdf_attachment.add_header( 'Content-Disposition', 'attachment', filename=f"wochenbericht_{digest.period_start.strftime('%Y%m%d')}.pdf" ) msg.attach(pdf_attachment) except Exception: pass # PDF-Anhang ist optional smtp_host = os.getenv('SMTP_HOST', 'localhost') smtp_port = int(os.getenv('SMTP_PORT', '25')) smtp_user = os.getenv('SMTP_USER', '') smtp_pass = os.getenv('SMTP_PASS', '') try: if smtp_port == 465: server = smtplib.SMTP_SSL(smtp_host, smtp_port) else: server = smtplib.SMTP(smtp_host, smtp_port) if smtp_port == 587: server.starttls() if smtp_user and smtp_pass: server.login(smtp_user, smtp_pass) server.send_message(msg) server.quit() except Exception as e: sendgrid_key = os.getenv('SENDGRID_API_KEY') if sendgrid_key: await send_via_sendgrid(msg, sendgrid_key) else: raise e async def send_via_sendgrid(msg, api_key: str): """Fallback: SendGrid API.""" import httpx async with httpx.AsyncClient() as client: response = await client.post( "https://api.sendgrid.com/v3/mail/send", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }, json={ "personalizations": [{"to": [{"email": msg['To']}]}], "from": {"email": msg['From']}, "subject": msg['Subject'], "content": [ {"type": "text/plain", "value": msg.get_payload(0).get_payload()}, {"type": "text/html", "value": msg.get_payload(1).get_payload() if len(msg.get_payload()) > 1 else ""} ] } ) if response.status_code >= 400: raise Exception(f"SendGrid error: {response.status_code}")