#!/usr/bin/env python3 """ Chart Generation Service with Plotly Generates modern, interactive charts from statistical data """ import json import sys import plotly.graph_objects as go from plotly.subplots import make_subplots import plotly.colors as pc def generate_chart(data, chart_config): """ Generate Plotly chart from statistical data Args: data: List of lists - first row is headers, rest is data chart_config: Dict with chart configuration (title, type, etc.) Returns: HTML string with embedded Plotly chart """ if not data or len(data) < 2: return "
Keine Daten verfügbar
" headers = data[0] rows = data[1:] # Extract x-axis labels (first column) x_labels = [str(row[0]) for row in rows] # Prepare traces for each data series fig = go.Figure() # Color palette - modern and professional colors = pc.qualitative.Plotly # Create a trace for each column (except first which is x-axis) for i in range(1, len(headers)): y_values = [row[i] if i < len(row) else 0 for row in rows] fig.add_trace(go.Bar( name=headers[i], x=x_labels, y=y_values, marker_color=colors[i % len(colors)], hovertemplate='%{x}
%{fullData.name}: %{y}' )) # Update layout with modern styling fig.update_layout( title={ 'text': chart_config.get('title', 'Statistik'), 'x': 0.5, 'xanchor': 'center', 'font': {'size': 20, 'color': '#333'} }, barmode='group', plot_bgcolor='rgba(240, 240, 240, 0.5)', paper_bgcolor='white', font={'family': 'Arial, sans-serif', 'size': 12, 'color': '#333'}, hovermode='x unified', legend={ 'orientation': 'h', 'yanchor': 'bottom', 'y': -0.2, 'xanchor': 'center', 'x': 0.5, 'bgcolor': 'rgba(255, 255, 255, 0.8)', 'bordercolor': '#ccc', 'borderwidth': 1 }, xaxis={ 'title': headers[0] if headers else '', 'showgrid': False, 'showline': True, 'linecolor': '#ccc' }, yaxis={ 'title': 'Anzahl', 'showgrid': True, 'gridcolor': 'rgba(200, 200, 200, 0.3)', 'showline': True, 'linecolor': '#ccc' }, margin={'l': 60, 'r': 40, 't': 80, 'b': 100}, height=500 ) # Generate HTML with Plotly CDN html = fig.to_html( include_plotlyjs='cdn', div_id='plotly-chart', config={ 'displayModeBar': True, 'displaylogo': False, 'modeBarButtonsToRemove': ['pan2d', 'lasso2d', 'select2d'], 'responsive': True } ) return html def generate_line_chart(data, chart_config): """ Generate Plotly line chart for time series data """ if not data or len(data) < 2: return "
Keine Daten verfügbar
" headers = data[0] rows = data[1:] x_labels = [str(row[0]) for row in rows] fig = go.Figure() colors = pc.qualitative.Plotly # Create line traces for i in range(1, len(headers)): y_values = [row[i] if i < len(row) else 0 for row in rows] fig.add_trace(go.Scatter( name=headers[i], x=x_labels, y=y_values, mode='lines+markers', line={'color': colors[i % len(colors)], 'width': 3}, marker={'size': 8}, hovertemplate='%{x}
%{fullData.name}: %{y}' )) # Update layout - NO fixed y-axis minimum for line charts fig.update_layout( title={ 'text': chart_config.get('title', 'Statistik Verlauf'), 'x': 0.5, 'xanchor': 'center', 'font': {'size': 20, 'color': '#333'} }, plot_bgcolor='rgba(240, 240, 240, 0.5)', paper_bgcolor='white', font={'family': 'Arial, sans-serif', 'size': 12, 'color': '#333'}, hovermode='x unified', legend={ 'orientation': 'h', 'yanchor': 'bottom', 'y': -0.2, 'xanchor': 'center', 'x': 0.5, 'bgcolor': 'rgba(255, 255, 255, 0.8)', 'bordercolor': '#ccc', 'borderwidth': 1 }, xaxis={ 'title': headers[0] if headers else '', 'showgrid': False, 'showline': True, 'linecolor': '#ccc' }, yaxis={ 'title': 'Anzahl', 'showgrid': True, 'gridcolor': 'rgba(200, 200, 200, 0.3)', 'showline': True, 'linecolor': '#ccc', 'rangemode': 'normal', # Auto-scale, don't force to 0 'autorange': True }, margin={'l': 60, 'r': 40, 't': 80, 'b': 100}, height=500 ) html = fig.to_html( include_plotlyjs='cdn', div_id='plotly-line-chart', config={ 'displayModeBar': True, 'displaylogo': False, 'modeBarButtonsToRemove': ['pan2d', 'lasso2d', 'select2d'], 'responsive': True } ) return html def main(): """ Main entry point - expects JSON input via stdin Expected format: { "data": [[headers], [row1], [row2], ...], "config": { "title": "Chart Title", "type": "bar" or "line", "period": "year", "month", or "day" } } """ try: # Read JSON from stdin input_data = json.load(sys.stdin) data = input_data.get('data', []) config = input_data.get('config', {}) # Generate chart based on type chart_type = config.get('type', 'bar') if chart_type == 'line': html = generate_line_chart(data, config) else: html = generate_chart(data, config) # Output HTML print(html) except Exception as e: error_html = f"""

Chart-Generierung fehlgeschlagen

Fehler: {str(e)}

""" print(error_html) sys.exit(1) if __name__ == '__main__': main()