Initial import: Carteasy Shop (Dev-Stand)
This commit is contained in:
Executable
+233
@@ -0,0 +1,233 @@
|
||||
#!/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 "<div>Keine Daten verfügbar</div>"
|
||||
|
||||
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='<b>%{x}</b><br>%{fullData.name}: %{y}<extra></extra>'
|
||||
))
|
||||
|
||||
# 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 "<div>Keine Daten verfügbar</div>"
|
||||
|
||||
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='<b>%{x}</b><br>%{fullData.name}: %{y}<extra></extra>'
|
||||
))
|
||||
|
||||
# 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"""
|
||||
<div style="padding: 20px; background: #fee; border: 1px solid #fcc; border-radius: 4px;">
|
||||
<h3 style="color: #c33; margin: 0 0 10px 0;">Chart-Generierung fehlgeschlagen</h3>
|
||||
<p style="margin: 0; color: #666;">Fehler: {str(e)}</p>
|
||||
</div>
|
||||
"""
|
||||
print(error_html)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user