MiroxMirox
  • Plattform

    • Philosophie
    • Plattform-Übersicht
    • Plattform-Ressourcen
  • Mirox-Cloud

    • Cloud-Übersicht
    • Verbundene Microservices
  • Mirox-Agent

    • Agent-Übersicht
    • Bereitstellungsoptionen
    • Data Scraper
    • Digital Twin
  • Technische Details

    • Metriksammlung
  • Information

    • Unterstützte Anlagen
  • Anlagentypen

    • Solaranlagen
    • Windanlagen
    • Batteriespeicher
  • Überwachung & Visualisierung

    • Echtzeit-Monitoring
    • Digitaler Zwilling
    • Komponentenzustände
    • Verlusterkennung
    • Effizienzerkennung
    • KPI-Dashboard
  • Datenverwaltung

    • Ereignisse
    • Tickets
    • Prognosen
    • Berichte
  • Integration & Freigabe

    • Kooperationen
    • API-Tokens
    • VPN
    • Proxy
  • KI

    • KI-Assistent & Wizards
    • Agentischer Zugriff (MCP)
  • Abrechnung

    • Markt & Tarife
    • Buchhaltung & Abrechnung
  • Kollaboration

    • Einladungen
  • Sicherheit

    • Authentifizierung
    • Berechtigungssystem
    • Kooperationsbeschränkungen
    • Zugriffs-Audit-Logging
  • Knoten

    • mrxnode
  • Anwendung

    • Türsteuerung
    • Generisches Relais
  • Edge-Cluster

    • Orchestrierung
  • Erste Schritte

    • Erste Schritte
  • Persönlich

    • VPN verwenden
    • Proxy verwenden
    • Zwei-Faktor-Authentifizierung
    • Sitzungen
    • API-Tokens
  • Pro Anlage

    • Kontakte
    • Netzwerkgeräte
    • Datenlogger
    • Komponenten
    • Direktes VPN (pro Agent)
  • Organisation

    • Mitgliederberechtigungen
    • Kooperationen
    • Dateispeicher
  • Datenexport

    • Export-Metrik-API
    • MiroxQL-Abfragesprache
    • Externe Berichterstellung
    • Grafana
    • API-Überblick
  • Unterstützung

    • Integrationsleitfaden anfordern
  • mrxnode

    • Übersicht
    • Anleitungen
    • Container-Bereitstellung
    • Befehlsreferenz
    • Fehlerbehebung
  • Berichterstellung

    • Externer Berichtgenerator
  • English
  • Deutsch
  • Español
  • Français
  • Português
  • Italiano
  • English
  • Plattform

    • Philosophie
    • Plattform-Übersicht
    • Plattform-Ressourcen
  • Mirox-Cloud

    • Cloud-Übersicht
    • Verbundene Microservices
  • Mirox-Agent

    • Agent-Übersicht
    • Bereitstellungsoptionen
    • Data Scraper
    • Digital Twin
  • Technische Details

    • Metriksammlung
  • Information

    • Unterstützte Anlagen
  • Anlagentypen

    • Solaranlagen
    • Windanlagen
    • Batteriespeicher
  • Überwachung & Visualisierung

    • Echtzeit-Monitoring
    • Digitaler Zwilling
    • Komponentenzustände
    • Verlusterkennung
    • Effizienzerkennung
    • KPI-Dashboard
  • Datenverwaltung

    • Ereignisse
    • Tickets
    • Prognosen
    • Berichte
  • Integration & Freigabe

    • Kooperationen
    • API-Tokens
    • VPN
    • Proxy
  • KI

    • KI-Assistent & Wizards
    • Agentischer Zugriff (MCP)
  • Abrechnung

    • Markt & Tarife
    • Buchhaltung & Abrechnung
  • Kollaboration

    • Einladungen
  • Sicherheit

    • Authentifizierung
    • Berechtigungssystem
    • Kooperationsbeschränkungen
    • Zugriffs-Audit-Logging
  • Knoten

    • mrxnode
  • Anwendung

    • Türsteuerung
    • Generisches Relais
  • Edge-Cluster

    • Orchestrierung
  • Erste Schritte

    • Erste Schritte
  • Persönlich

    • VPN verwenden
    • Proxy verwenden
    • Zwei-Faktor-Authentifizierung
    • Sitzungen
    • API-Tokens
  • Pro Anlage

    • Kontakte
    • Netzwerkgeräte
    • Datenlogger
    • Komponenten
    • Direktes VPN (pro Agent)
  • Organisation

    • Mitgliederberechtigungen
    • Kooperationen
    • Dateispeicher
  • Datenexport

    • Export-Metrik-API
    • MiroxQL-Abfragesprache
    • Externe Berichterstellung
    • Grafana
    • API-Überblick
  • Unterstützung

    • Integrationsleitfaden anfordern
  • mrxnode

    • Übersicht
    • Anleitungen
    • Container-Bereitstellung
    • Befehlsreferenz
    • Fehlerbehebung
  • Berichterstellung

    • Externer Berichtgenerator
  • English
  • Deutsch
  • Español
  • Français
  • Português
  • Italiano
  • English
  • Berichterstellung

    • Externe Berichtserstellung - API-Beispiele

Externe Berichtserstellung - API-Beispiele

Rufen Sie Anlagendaten direkt aus den Mirox-Export-APIs ab und erstellen Sie Ihre eigenen Berichte mit dem Werkzeug Ihrer Wahl. Dieses Beispiel führt der Klarheit halber mit curl durch die relevanten Endpunkte, aber dieselben Prinzipien gelten auch für Python, Power BI, Tableau oder jeden anderen HTTP-Client.

Voraussetzungen

Bevor Sie Daten für die externe Berichtserstellung exportieren können, benötigen Sie:

  1. Mirox-Konto - Ein gültiges Mirox-Benutzerkonto
  2. Berechtigungen - Ihr Konto muss die Berechtigung haben, den Park zu lesen und Berichte zu erstellen für diesen Park
  3. Park-UID - Die eindeutige Kennung der Park-Ressource, zu der Sie berichten möchten
  4. API-Token - Ein API-Token mit mindestens der Berechtigungsgruppe Reporting (siehe API-Token-Nutzung für eine Anleitung zur Erstellung)

Verfügbare Export-Endpunkte

Die Mirox-Plattform stellt mehrere API-Endpunkte bereit, um Daten in Formaten zu exportieren, die für die externe Berichtserstellung geeignet sind. Parkinformationen und Ereignis-Exporte liegen unter dem API-Pfad /v1/export/report/; Produktionsmetriken nutzen den dedizierten Template-Export-Endpunkt /v1/export/metrics/template/{template_uid}.

Wichtiger Hinweis

Die in diesem Dokument gezeigten API-Endpunkte sind Beispiele zur Veranschaulichung. Die genauen Endpunkte können sich ändern. Bitte konsultieren Sie die aktuelle API-Dokumentation unter https://service.mirox.io/docs für die neuesten Informationen.

Parkinformationen (JSON)

Grundlegende Parkinformationen können als JSON exportiert werden mit:

GET /v1/export/report/{park_uid}/info

Dies liefert Park-Metadaten einschließlich:

  • Name, Typ und Beschreibung
  • Geografische Lage und Zeitzone
  • Technische Spezifikationen (Spitzenproduktion usw.)
  • Organisations- und Portfolio-Informationen
  • Adresse und Inbetriebnahmedetails

Vollständige Details zum Antwortschema finden Sie in der API-Dokumentation für Parkinformationen.

Park-Ereignisse (CSV)

Ereignisdaten können als CSV exportiert werden mit:

GET /v1/export/report/{park_uid}/events.csv

Dieser Endpunkt unterstützt die folgenden Abfrageparameter:

  • year: Filtern nach Jahr (z. B. 2025)
  • quarter: Filtern nach Quartal (1-4)
  • month: Filtern nach Monat (1-12)

Die zurückgegebene CSV-Datei enthält die folgenden Spalten:

  • Ereignis-ID
  • Startdatum
  • Enddatum
  • Dauer (Stunden)
  • Typ
  • Ersteller
  • Beschreibung
  • Priorität

Ereignisfilterung nach Priorität

Das Feld Priorität gibt die Wichtigkeit eines Ereignisses an:

  • Werte >= 1000: Wichtige Ereignisse (produktionsrelevante Abschaltungen, Alarme)
  • Werte < 1000: Informative Ereignisse (Verbindungen, geringfügige Statusänderungen)

Filtern Sie in Ihrem Berichtswerkzeug nach Priorität, um bedeutsame Ereignisse hervorzuheben.

Wenn keine Zeitparameter angegeben werden, gibt der Endpunkt Ereignisse aus dem zuletzt abgeschlossenen Quartal zurück.

Details zu Anfrageparametern und Antwortformat finden Sie in der API-Dokumentation für Park-Ereignisse.

Park-Metriken (CSV)

Produktions- und Leistungsmetriken können über den Template-Export-Endpunkt als CSV exportiert werden:

GET /v1/export/metrics/template/{template_uid}

Dieser Endpunkt unterstützt die folgenden Abfrageparameter:

  • park: Eine oder mehrere Park-UIDs (kommagetrennt), die in den Export einbezogen werden sollen
  • portfolio: Eine oder mehrere Portfolio-UIDs (kommagetrennt); jedes Portfolio wird zu seinen Parks aufgelöst
  • resolution: Aggregationsintervall (daily, weekly, monthly, quarterly oder yearly; Standardwert ist monthly)
  • year: Filtern nach Jahr (z. B. 2025)
  • quarter: Filtern nach Quartal (1-4)
  • month: Filtern nach Monat (1-12)
  • week: Filtern nach ISO-Woche (wöchentliche Auflösung)
  • day: Filtern nach Tag (erfordert month)

Der Parameter resolution bestimmt, wie die Daten aggregiert werden:

  • daily: Gibt eine Zeile pro Tag mit täglichen Messwerten zurück
  • monthly: Gibt eine Zeile pro Monat mit aggregierten Monatssummen und einer zusätzlichen Spalte „Tage im Monat" zurück

Ihr Business-Intelligence-Werkzeug muss diese unterschiedlichen Auflösungsformate für die Visualisierung und Analyse entsprechend verarbeiten.

Exporte über mehrere Anlagen

Ein einziger Aufruf kann mehrere Anlagen oder ganze Portfolios auf einmal exportieren, indem kommagetrennte park- und portfolio-UIDs übergeben werden. Dies ist der empfohlene Weg, wenn Sie einen Bericht erstellen, der mehr als eine Anlage umfasst.

Die Spalten werden durch das Export-Template definiert, auf das Sie verweisen. Das folgende Beispiel verwendet das Template ABCD12340001 („Report Technical v1"), das Folgendes enthält:

  • Energieproduktion (kWh)
  • Energiebericht (kWh)
  • Energie-Abschaltung durch Netz (kWh)
  • Energie-Abschaltung extern (kWh)
  • Energieziel (kWh)
  • Einstrahlung (kWh)

Vollständige Details zu Anfrageparametern und Antwortformat finden Sie in der aktuellen API-Dokumentation unter https://service.mirox.io/docs.

API-Nutzungsbeispiele

Nachfolgend finden Sie Beispiele dafür, wie die Export-Endpunkte direkt mit curl verwendet werden. Diese Beispiele veranschaulichen die Struktur von API-Aufrufen und das Format der Antworten.

Beispiel 1: Abrufen von Parkinformationen (JSON)

Anfrage:

curl "https://service.mirox.io/api/v1/export/report/ABC123DEF456/info" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Antwort (Auszug):

{
  "uid": "ABC123DEF456",
  "name": "Sunnyside Solar Park",
  "type": "solar",
  "description": "Sunnyside Solar Park is a 42 MW photovoltaic installation...",
  "latitude": 48.7511,
  "longitude": 9.1225,
  "timezone": "Europe/Berlin",
  "peak_production_w": 42000000,
  "active": true,
  "created_at": "2024-05-16T10:30:25.104830Z",
  "portfolio": {
    "uid": "GHI789JKL012",
    "name": "Renewable South"
  },
  "organization": {
    "uid": "MNO345PQR678",
    "name": "GreenPower Inc."
  },
  "address": {
    "address1": "123 Solar Way",
    "zip_code": "70123",
    "city": "Sunnyville",
    "country": "Germany"
  }
}

Beispiel 2: Export von Ereignisdaten (CSV)

Anfrage:

curl "https://service.mirox.io/api/v1/export/report/ABC123DEF456/events.csv" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Antwort (CSV):

Event ID,Start Date,End Date,Duration (hours),Type,Creator,Description,Priority
9876543210123456789,2025-01-15 08:45,Ongoing,720.25,Sensor Error,System,Irradiation sensor malfunction detected,1200
8765432101234567890,2025-02-10 14:30,2025-02-15 09:15,114.75,Data Availability,System,Connection issues with data acquisition system,1100

Beachten Sie das Feld Priority mit Werten größer als 1000, das bedeutsame, produktionsrelevante Ereignisse kennzeichnet.

Beispiel 3: Export monatlicher Metriken (CSV)

Anfrage:

curl "https://service.mirox.io/api/v1/export/metrics/template/ABCD12340001?park=ABC123DEF456&resolution=monthly" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Antwort (CSV):

Date (YYYY-MM),Energy Production (kWh),Energy Report (kWh),Energy Shutdown by grid (kWh),Energy Shutdown by external (kWh),Energy Target (kWh),Irradiation (kWh),Days in Month (d)
2025-01,950000.45,980000.00,15000.50,25000.75,980000.00,18.350,31
2025-02,1250000.32,1300000.00,2000.25,18500.40,1300000.00,22.780,28
2025-03,1850000.75,1900000.00,45000.20,30000.65,1900000.00,35.420,31

Dieses Beispiel zeigt monatlich aggregierte Daten mit der für die monatliche Intervalloption spezifischen Spalte „Days in Month (d)".

Nutzung in verschiedenen Werkzeugen

Diese API-Aufrufe lassen sich in verschiedenen Umgebungen anwenden:

  • Python/Requests: Verwenden Sie die requests-Bibliothek, um API-Aufrufe in Python-Skripten durchzuführen
  • Power BI: Verwenden Sie „Web"-Datenquellen, um JSON-/CSV-Daten direkt zu importieren
  • Tableau: Konfigurieren Sie Web Data Connectors für die API-Integration
  • Excel: Verwenden Sie Power Query, um API-Daten zu importieren und zu transformieren
  • Eigene Skripte: Implementieren Sie in jeder Sprache mit HTTP-Client-Unterstützung

Best Practices

Für eine effektive API-Nutzung:

  1. API-Token sicher verwenden - Hinterlegen Sie Token niemals fest im Skript; verwenden Sie Umgebungsvariablen
  2. Intervalldaten korrekt verarbeiten - Stellen Sie sicher, dass Ihre Analyse tägliche und monatliche Datenformate korrekt verarbeitet
  3. Ereignisse angemessen filtern - Verwenden Sie das Prioritätsfeld, um sich auf bedeutsame Ereignisse zu konzentrieren (>=1000)
  4. Datenvolumen berücksichtigen - Exportieren Sie bei großen Parks oder langen Zeiträumen Daten in handhabbaren Teilstücken
  5. Fehlerbehandlung implementieren - Behandeln Sie HTTP-Fehler und Netzwerkprobleme angemessen

Fehlerbehebung

Authentifizierungsfehler

Wenn Sie einen HTTP-401-Fehler erhalten:

  1. Überprüfen Sie Ihr API-Token: Stellen Sie sicher, dass Sie ein gültiges, nicht abgelaufenes API-Token verwenden. Token können in Ihren Mirox-Benutzereinstellungen erzeugt werden.

  2. Überprüfen Sie die Park-UID: Stellen Sie sicher, dass die Park-UID gültig ist und Sie Zugriff darauf haben. Der Beispielwert ABC123DEF456 ist ein Platzhalter.

  3. Token-Berechtigungen: Überprüfen Sie, dass Ihrem Token die erforderlichen Berechtigungen für den Zugriff auf die Export-Endpunkte erteilt wurden.

Probleme mit der CSV-Kodierung

CSV-Exporte verwenden die UTF-8-Kodierung. In manchen Werkzeugen (z. B. Excel) müssen Sie die Kodierung beim Import möglicherweise explizit angeben.

Beispiel einer Python-Implementierung: SolarViz Report Generator

Das folgende Beispiel zeigt eine vollständige Python-Implementierung, die die oben beschriebenen APIs verwendet, um einen individuellen Energieproduktionsbericht zu erstellen.

Voraussetzungen

Um dieses Python-Beispiel auszuführen, benötigen Sie die folgenden Bibliotheken:

pip install requests pandas matplotlib seaborn tabulate
  • requests: Für API-Aufrufe an die Mirox-Plattform
  • pandas: Für Datenmanipulation und -analyse
  • matplotlib und seaborn: Für die Erstellung von Visualisierungen
  • tabulate: Für die Formatierung von Tabellen im Bericht

Vollständiger Beispielcode

Nachfolgend finden Sie den vollständigen Code für den SolarViz Report Generator:

#!/usr/bin/env python3
"""
SolarViz - Mirox External Report Generator

This script demonstrates how to fetch data from the Mirox API and generate
a custom energy production report with visualizations as a PDF file.
"""

import os
import requests
import json
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timezone
import io
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle
from reportlab.graphics.shapes import Drawing
from reportlab.lib.units import inch, cm

# Configuration - Replace with your values
API_URL = "https://service.mirox.io"
API_TOKEN = "YOUR_API_TOKEN"  # Your personal access token
PARK_UID = "ABC123DEF456"  # The UID of the park you want to work with
TEMPLATE_UID = "ABCD12340001"  # The export template ("Report Technical v1")

# Time range parameters
YEAR = datetime.now(timezone.utc).year
QUARTER = (datetime.now(timezone.utc).month - 1) // 3 + 1

# Setup headers
headers = {
    "Authorization": f"Bearer {API_TOKEN}",
    "Content-Type": "application/json"
}


def fetch_park_info():
    """Fetch basic park information as JSON"""
    response = requests.get(
        f"{API_URL}/api/v1/export/report/{PARK_UID}/info",
        headers=headers
    )
    response.raise_for_status()
    return response.json()


def fetch_events(year=None, quarter=None):
    """Fetch park events as DataFrame"""
    params = {}
    if year:
        params["year"] = year
    if quarter:
        params["quarter"] = quarter

    response = requests.get(
        f"{API_URL}/api/v1/export/report/{PARK_UID}/events.csv",
        headers=headers,
        params=params
    )
    response.raise_for_status()

    # Convert CSV content to DataFrame
    events_df = pd.read_csv(io.StringIO(response.content.decode('utf-8')))

    # Filter for significant events (priority >= 1000)
    significant_events = events_df[events_df['Priority'] >= 1000]

    # Process events data - remove ID, Priority, and format duration
    if not significant_events.empty:
        # Remove any ID-related columns (there could be 'ID', 'Event ID', etc.)
        id_columns = [col for col in significant_events.columns if 'id' in col.lower()]
        significant_events = significant_events.drop(id_columns, axis=1, errors='ignore')

        # Remove Priority column as well
        if 'Priority' in significant_events.columns:
            significant_events = significant_events.drop('Priority', axis=1)

        # Convert duration from minutes to days/hours
        if 'DurationMin' in significant_events.columns:
            def format_duration(minutes):
                if minutes < 60:
                    return f"{minutes} min"
                elif minutes < 24*60:
                    hours = minutes / 60
                    return f"{hours:.1f} hours"
                else:
                    days = minutes / (24*60)
                    return f"{days:.1f} days"

            significant_events['Duration'] = significant_events['DurationMin'].apply(format_duration)
            significant_events = significant_events.drop('DurationMin', axis=1)

    return significant_events


def fetch_metrics(resolution="monthly", year=None, quarter=None):
    """Fetch park production metrics as DataFrame"""
    params = {"park": PARK_UID, "resolution": resolution}
    if year:
        params["year"] = year
    if quarter:
        params["quarter"] = quarter

    response = requests.get(
        f"{API_URL}/api/v1/export/metrics/template/{TEMPLATE_UID}",
        headers=headers,
        params=params
    )
    response.raise_for_status()

    # Convert CSV content to DataFrame
    metrics_df = pd.read_csv(io.StringIO(response.content.decode('utf-8')))

    return metrics_df


def create_production_chart(metrics_df):
    """Create a chart visualizing energy production and target with stacked losses"""
    plt.figure(figsize=(8, 4))

    # Set seaborn style
    sns.set_style("whitegrid")

    # Prepare data for stacked bar chart
    x_positions = range(len(metrics_df))
    months = metrics_df.iloc[:, 0]

    # Create stacked bar chart
    # Base layer: Actual production
    plt.bar(x_positions, metrics_df['Energy Production (kWh)'],
            color='#2986cc', label='Actual Production')

    # Stack grid shutdown losses on top
    grid_shutdown = metrics_df['Energy Shutdown by grid (kWh)']
    external_shutdown = metrics_df['Energy Shutdown by external (kWh)']

    # Second layer: Grid shutdown losses
    plt.bar(x_positions, grid_shutdown,
            bottom=metrics_df['Energy Production (kWh)'],
            color='#e06666', label='Grid Shutdown Losses')

    # Third layer: External shutdown losses
    plt.bar(x_positions, external_shutdown,
            bottom=metrics_df['Energy Production (kWh)'] + grid_shutdown,
            color='#f1c232', label='External Shutdown Losses')

    # Plot Target as a line
    plt.plot(x_positions, metrics_df['Energy Target (kWh)'],
             marker='o', color='#6aa84f', linewidth=2, label='Target')

    # Customize the chart
    plt.title('Monthly Energy Production vs Target with Losses')
    plt.xlabel('Month')
    plt.ylabel('Energy (kWh)')
    plt.xticks(x_positions, months, rotation=45)

    # Add legend
    plt.legend(loc='upper left')

    plt.tight_layout()

    # Save chart to a bytes buffer
    buffer = io.BytesIO()
    plt.savefig(buffer, format='png', dpi=300)
    buffer.seek(0)
    plt.close()

    return buffer


def calculate_performance_metrics(metrics_df):
    """Calculate performance metrics based on the production data"""
    performance = {}

    # Total energy production
    performance['total_production'] = metrics_df['Energy Production (kWh)'].sum()

    # Total target
    performance['total_target'] = metrics_df['Energy Target (kWh)'].sum()

    # Performance ratio (actual vs target)
    performance['performance_ratio'] = (performance['total_production'] / performance['total_target']) * 100

    # Total shutdown energy
    performance['grid_shutdown'] = metrics_df['Energy Shutdown by grid (kWh)'].sum()
    performance['external_shutdown'] = metrics_df['Energy Shutdown by external (kWh)'].sum()
    performance['total_shutdown'] = performance['grid_shutdown'] + performance['external_shutdown']

    # Lost production percentage
    performance['lost_production_pct'] = (performance['total_shutdown'] /
                                          (performance['total_production'] + performance['total_shutdown'])) * 100

    return performance


def generate_pdf_report(park_info, events_df, metrics_df, performance, chart_data):
    """Generate a PDF report with the data"""
    # Define the output filename
    output_file = f"SolarViz_Report_{park_info['name'].replace(' ', '_')}_{YEAR}_Q{QUARTER}.pdf"

    # Create PDF document
    doc = SimpleDocTemplate(output_file, pagesize=A4)
    styles = getSampleStyleSheet()

    # Modify existing styles
    styles['Title'].fontName = 'Helvetica-Bold'
    styles['Title'].fontSize = 18
    styles['Title'].alignment = 1  # Center aligned
    styles['Title'].spaceAfter = 12

    # Modify Heading2 style
    styles['Heading2'].fontName = 'Helvetica-Bold'
    styles['Heading2'].fontSize = 14
    styles['Heading2'].spaceBefore = 12
    styles['Heading2'].spaceAfter = 6

    # Modify Normal style
    styles['Normal'].fontName = 'Helvetica'
    styles['Normal'].fontSize = 10
    styles['Normal'].spaceBefore = 6
    styles['Normal'].spaceAfter = 6

    # Add a custom style for tables
    styles.add(ParagraphStyle(name='TableHeader',
                              parent=styles['Normal'],
                              fontName='Helvetica-Bold',
                              fontSize=9,
                              alignment=1))

    # Create document elements
    elements = []

    # Title
    elements.append(Paragraph(f"Energy Production Report: {park_info['name']}", styles['Title']))
    elements.append(Spacer(1, 0.5*cm))

    # Park Information section
    elements.append(Paragraph("Park Information", styles['Heading2']))

    park_info_data = [
        ["Name:", park_info['name']],
        ["Type:", park_info['type']],
        ["Location:", f"{park_info['address']['city']}, {park_info['address']['country']}"],
        ["Peak Capacity:", f"{park_info['peak_production_w'] / 1000000:.2f} MW"],
        ["Portfolio:", park_info['portfolio']['name']],
        ["Organization:", park_info['organization']['name']]
    ]

    park_info_table = Table(park_info_data, colWidths=[3*cm, 10*cm])
    park_info_table.setStyle(TableStyle([
        ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
        ('FONTNAME', (1, 0), (1, -1), 'Helvetica'),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('LEFTPADDING', (0, 0), (-1, -1), 6),
        ('TOPPADDING', (0, 0), (-1, -1), 3),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 3),
    ]))

    elements.append(park_info_table)
    elements.append(Spacer(1, 0.5*cm))

    # Performance Summary section
    elements.append(Paragraph(f"Performance Summary ({YEAR} Q{QUARTER})", styles['Heading2']))

    performance_data = [
        ["Total Energy Production:", f"{performance['total_production']:,.2f} kWh"],
        ["Target Production:", f"{performance['total_target']:,.2f} kWh"],
        ["Performance Ratio:", f"{performance['performance_ratio']:.2f}%"],
        ["Total Energy Lost to Shutdowns:", f"{performance['total_shutdown']:,.2f} kWh"],
        ["  • Grid Shutdowns:", f"{performance['grid_shutdown']:,.2f} kWh"],
        ["  • External Shutdowns:", f"{performance['external_shutdown']:,.2f} kWh"],
        ["Lost Production Percentage:", f"{performance['lost_production_pct']:.2f}%"]
    ]

    performance_table = Table(performance_data, colWidths=[6*cm, 7*cm])
    performance_table.setStyle(TableStyle([
        ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
        ('FONTNAME', (1, 0), (1, -1), 'Helvetica'),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('LEFTPADDING', (0, 0), (-1, -1), 6),
        ('TOPPADDING', (0, 0), (-1, -1), 3),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 3),
    ]))

    elements.append(performance_table)
    elements.append(Spacer(1, 0.5*cm))

    # Monthly Production Chart
    elements.append(Paragraph("Monthly Production Chart", styles['Heading2']))

    # Add the chart image
    img = Image(chart_data, width=16*cm, height=8*cm)
    elements.append(img)
    elements.append(Spacer(1, 0.5*cm))

    # Add metrics data table below the graph
    elements.append(Paragraph("Monthly Production Data", styles['Heading2']))

    # Prepare metrics data for table
    # Select relevant columns
    if not metrics_df.empty:
        # Create a copy of the dataframe with selected columns
        table_df = metrics_df.copy()

        # Use the first column as the month column
        month_col_name = table_df.columns[0]

        # Create table data with headers
        metrics_data = [['Month', 'Production (kWh)', 'Target (kWh)',
                        'Grid Shutdown (kWh)', 'External Shutdown (kWh)']]

        # Add data rows with formatted numbers
        for _, row in table_df.iterrows():
            metrics_data.append([
                row[month_col_name],
                f"{row['Energy Production (kWh)']:,.2f}",
                f"{row['Energy Target (kWh)']:,.2f}",
                f"{row['Energy Shutdown by grid (kWh)']:,.2f}",
                f"{row['Energy Shutdown by external (kWh)']:,.2f}"
            ])

        # Create table and set style
        metrics_table = Table(metrics_data, repeatRows=1)
        metrics_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),
            ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
            ('ALIGN', (0, 0), (-1, 0), 'CENTER'),
            ('ALIGN', (1, 1), (-1, -1), 'RIGHT'),  # Right-align numeric columns
            ('BOTTOMPADDING', (0, 0), (-1, 0), 6),
            ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
            ('FONTSIZE', (0, 0), (-1, -1), 9),
        ]))
        elements.append(metrics_table)
    else:
        elements.append(Paragraph("No production data available for the selected period.", styles['Normal']))

    elements.append(Spacer(1, 0.5*cm))

    # Significant Events section
    elements.append(Paragraph("Significant Events", styles['Heading2']))

    if not events_df.empty:
        # Double check to make sure all ID columns and Priority are removed
        id_columns = [col for col in events_df.columns if 'id' in col.lower()]
        events_df = events_df.drop(id_columns, axis=1, errors='ignore')

        # Also remove Priority column if it still exists
        if 'Priority' in events_df.columns:
            events_df = events_df.drop('Priority', axis=1)

        # Convert DataFrame to a list of lists
        events_data = [events_df.columns.tolist()] + events_df.values.tolist()

        # Create table and set style
        events_table = Table(events_data, repeatRows=1)
        events_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),
            ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
            ('ALIGN', (0, 0), (-1, 0), 'CENTER'),
            ('BOTTOMPADDING', (0, 0), (-1, 0), 6),
            ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
            ('FONTSIZE', (0, 0), (-1, -1), 8),
        ]))
        elements.append(events_table)
    else:
        elements.append(Paragraph("No significant events recorded during this period.", styles['Normal']))

    elements.append(Spacer(1, 1*cm))

    # Footer section
    elements.append(Paragraph("Data Source", styles['Heading2']))
    elements.append(Paragraph(
        f"This report was generated using data exported from the Mirox platform via its external reporting API.<br/>"
        f"Report creation date: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M')}<br/><br/>"
        f"<i>Generated by SolarViz - External Reporting Tool</i>",
        styles['Normal']))

    # Build the PDF document
    doc.build(elements)

    return output_file


def main():
    """Main function that orchestrates the report generation process"""
    try:
        print("Fetching park information...")
        park_info = fetch_park_info()

        print("Fetching events data...")
        events_df = fetch_events(year=YEAR, quarter=QUARTER)

        print("Fetching production metrics...")
        metrics_df = fetch_metrics(resolution="monthly", year=YEAR)

        print("Calculating performance metrics...")
        performance = calculate_performance_metrics(metrics_df)

        print("Creating visualization...")
        chart_data = create_production_chart(metrics_df)

        print("Generating PDF report...")
        output_file = generate_pdf_report(park_info, events_df, metrics_df, performance, chart_data)

        print(f"Report successfully generated: {output_file}")

    except Exception as e:
        print(f"Error generating report: {str(e)}")


if __name__ == "__main__":
    main()

Funktionsweise

  1. Konfiguration: Das Skript legt zu Beginn die Mirox-API-URL, das API-Token und die Park-UID fest.

  2. Datenerfassung: Das Skript ruft drei Arten von Daten aus Mirox ab:

    • Parkinformationen (JSON) einschließlich Name, Lage und Kapazität
    • Bedeutsame Ereignisse (CSV), nach Priorität gefiltert
    • Monatliche Energieproduktionsmetriken (CSV)
  3. Datenverarbeitung: Das Skript:

    • Konvertiert CSV-Daten in Pandas-DataFrames für eine einfache Verarbeitung
    • Berechnet Leistungsmetriken wie das Produktionsverhältnis und die Auswirkung von Abschaltungen
    • Erstellt eine gestapelte Balkenvisualisierung der monatlichen Produktion und der Abschaltverluste im Vergleich zur Ziellinie
  4. Berichtserstellung: Das Skript erstellt einen PDF-Bericht (mit ReportLab), der Folgendes enthält:

    • Zusammenfassung der Parkinformationen
    • Leistungsmetriken
    • Energieproduktionsdiagramm
    • Tabelle bedeutsamer Ereignisse
  5. Ausgabe: Der Bericht wird als PDF-Datei gespeichert, die:

    • In jedem PDF-Betrachter geöffnet werden kann
    • Direkt mit Beteiligten geteilt werden kann
    • Archiviert oder in Berichts-Workflows integriert werden kann

Anpassungsmöglichkeiten

Der SolarViz Report Generator kann auf verschiedene Weise angepasst werden:

  • Zeiträume: Ändern Sie die Variablen YEAR und QUARTER, um Berichte für unterschiedliche Zeiträume zu erstellen
  • Diagrammtypen: Passen Sie die Funktion create_production_chart an, um andere Visualisierungsstile zu verwenden
  • Zusätzliche Metriken: Rufen Sie zusätzliche Metriken aus Mirox oder anderen Datenquellen ab und binden Sie sie ein
  • Berichtsformat: Passen Sie das ReportLab-Layout an oder tauschen Sie es bei Bedarf gegen ein HTML- oder anderes Ausgabeformat aus
  • Automatisierung: Integrieren Sie das Skript mit Cron-Jobs oder Task-Schedulern in automatisierte Workflows

Verwandte Funktionen

  • Leitfaden zur externen Berichtserstellung — konzeptioneller Überblick und weitere Integrationsmöglichkeiten
  • Metrik-Export-API — vollständige Referenz für den oben verwendeten Template-Export-Endpunkt
  • API-Token-Nutzung — erstellen und verwalten Sie das für dieses Beispiel benötigte Token
  • Berichte — erstellen Sie fertige PDF-/CSV-Berichte direkt in der Plattform
MIT Licensed | Copyright 2026 Mirox Verwaltungs GmbH