Open-source AI data analyst platform extracted from internal repo. Includes data sync engine, Keboola adapter, Flask web portal, server deployment scripts, and configuration templates.
204 lines
6.4 KiB
Python
204 lines
6.4 KiB
Python
"""
|
|
Generate a sample report chart for /test command.
|
|
|
|
Creates a polished demo dashboard image with fake data
|
|
to showcase what notifications can look like.
|
|
"""
|
|
|
|
import os
|
|
import random
|
|
import tempfile
|
|
from datetime import datetime, timedelta
|
|
|
|
import numpy as np
|
|
|
|
|
|
# Brand colors
|
|
COLOR_PRIMARY = "#0073D1"
|
|
COLOR_DARK = "#1A253C"
|
|
COLOR_SUCCESS = "#10B77F"
|
|
COLOR_WARNING = "#F59F0A"
|
|
COLOR_ERROR = "#EA580C"
|
|
COLOR_BG = "#F5F7FA"
|
|
COLOR_SURFACE = "#FFFFFF"
|
|
COLOR_GRAY = "#6B7280"
|
|
|
|
|
|
def generate_test_report(username: str) -> tuple[str, str]:
|
|
"""Generate a test report image and return (image_path, caption).
|
|
|
|
Creates a professional-looking dashboard with:
|
|
- Revenue trend line (7 days)
|
|
- KPI summary bar
|
|
- Top metrics breakdown
|
|
"""
|
|
import matplotlib
|
|
matplotlib.use("Agg")
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.dates as mdates
|
|
from matplotlib.patches import FancyBboxPatch
|
|
|
|
rng = random.Random(42)
|
|
|
|
# Generate fake data - 14 days of revenue
|
|
today = datetime.now()
|
|
dates = [today - timedelta(days=i) for i in range(13, -1, -1)]
|
|
base_revenue = 52000
|
|
revenues = [
|
|
base_revenue + rng.gauss(0, 5000) + i * 300
|
|
for i, _ in enumerate(dates)
|
|
]
|
|
# Make today slightly lower for "alert" feel
|
|
revenues[-1] = revenues[-2] * 0.82
|
|
|
|
# KPI data
|
|
today_rev = revenues[-1]
|
|
yesterday_rev = revenues[-2]
|
|
week_avg = np.mean(revenues[-7:])
|
|
total_7d = sum(revenues[-7:])
|
|
change_pct = ((today_rev - yesterday_rev) / yesterday_rev) * 100
|
|
|
|
# Create figure with subplots
|
|
fig = plt.figure(figsize=(10, 7), facecolor=COLOR_BG)
|
|
fig.subplots_adjust(top=0.88, bottom=0.08, left=0.08, right=0.95, hspace=0.45)
|
|
|
|
# Title
|
|
fig.text(
|
|
0.08, 0.95,
|
|
"Data Analyst Report",
|
|
fontsize=18, fontweight="bold", color=COLOR_DARK,
|
|
fontfamily="sans-serif",
|
|
)
|
|
fig.text(
|
|
0.08, 0.91,
|
|
f"{today.strftime('%B %d, %Y')} | Demo report for {username}",
|
|
fontsize=11, color=COLOR_GRAY, fontfamily="sans-serif",
|
|
)
|
|
|
|
# --- KPI Cards (top row) ---
|
|
kpi_data = [
|
|
("Today's Revenue", f"${today_rev:,.0f}", f"{change_pct:+.1f}%", change_pct >= 0),
|
|
("7-Day Average", f"${week_avg:,.0f}", "", True),
|
|
("7-Day Total", f"${total_7d:,.0f}", "", True),
|
|
("Active Projects", f"{rng.randint(180, 220)}", "+12", True),
|
|
]
|
|
|
|
for i, (label, value, badge, is_positive) in enumerate(kpi_data):
|
|
ax_kpi = fig.add_axes([0.08 + i * 0.225, 0.72, 0.19, 0.14])
|
|
ax_kpi.set_xlim(0, 1)
|
|
ax_kpi.set_ylim(0, 1)
|
|
ax_kpi.axis("off")
|
|
|
|
# Card background
|
|
card = FancyBboxPatch(
|
|
(0, 0), 1, 1,
|
|
boxstyle="round,pad=0.05",
|
|
facecolor=COLOR_SURFACE, edgecolor="#E5E7EB", linewidth=1,
|
|
)
|
|
ax_kpi.add_patch(card)
|
|
|
|
ax_kpi.text(0.1, 0.7, label, fontsize=8, color=COLOR_GRAY, fontfamily="sans-serif")
|
|
ax_kpi.text(0.1, 0.25, value, fontsize=16, fontweight="bold", color=COLOR_DARK, fontfamily="sans-serif")
|
|
|
|
if badge:
|
|
badge_color = COLOR_SUCCESS if is_positive else COLOR_ERROR
|
|
ax_kpi.text(0.9, 0.25, badge, fontsize=9, fontweight="bold",
|
|
color=badge_color, ha="right", fontfamily="sans-serif")
|
|
|
|
# --- Revenue Chart ---
|
|
ax_chart = fig.add_subplot(2, 1, 2)
|
|
ax_chart.set_facecolor(COLOR_SURFACE)
|
|
|
|
# Plot area fill
|
|
ax_chart.fill_between(
|
|
dates, revenues,
|
|
alpha=0.1, color=COLOR_PRIMARY,
|
|
)
|
|
|
|
# Main line
|
|
ax_chart.plot(
|
|
dates[:-1], revenues[:-1],
|
|
color=COLOR_PRIMARY, linewidth=2.5, solid_capstyle="round",
|
|
)
|
|
|
|
# Today's point (highlighted)
|
|
ax_chart.plot(
|
|
dates[-1], revenues[-1],
|
|
"o", color=COLOR_ERROR, markersize=8, zorder=5,
|
|
)
|
|
ax_chart.plot(
|
|
dates[-1], revenues[-1],
|
|
"o", color=COLOR_ERROR, markersize=14, alpha=0.2, zorder=4,
|
|
)
|
|
|
|
# Dashed line connecting to today
|
|
ax_chart.plot(
|
|
dates[-2:], revenues[-2:],
|
|
color=COLOR_ERROR, linewidth=2, linestyle="--", alpha=0.7,
|
|
)
|
|
|
|
# Average line
|
|
ax_chart.axhline(
|
|
y=week_avg, color=COLOR_WARNING, linewidth=1, linestyle=":",
|
|
alpha=0.8, label=f"7d avg: ${week_avg:,.0f}",
|
|
)
|
|
|
|
# Styling
|
|
ax_chart.set_title(
|
|
"Daily Revenue (14 days)", fontsize=13, fontweight="bold",
|
|
color=COLOR_DARK, loc="left", pad=12, fontfamily="sans-serif",
|
|
)
|
|
ax_chart.xaxis.set_major_formatter(mdates.DateFormatter("%b %d"))
|
|
ax_chart.xaxis.set_major_locator(mdates.DayLocator(interval=2))
|
|
plt.setp(ax_chart.xaxis.get_majorticklabels(), rotation=0, fontsize=9, color=COLOR_GRAY)
|
|
plt.setp(ax_chart.yaxis.get_majorticklabels(), fontsize=9, color=COLOR_GRAY)
|
|
|
|
ax_chart.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f"${x/1000:.0f}k"))
|
|
|
|
ax_chart.spines["top"].set_visible(False)
|
|
ax_chart.spines["right"].set_visible(False)
|
|
ax_chart.spines["left"].set_color("#E5E7EB")
|
|
ax_chart.spines["bottom"].set_color("#E5E7EB")
|
|
ax_chart.tick_params(colors="#E5E7EB")
|
|
ax_chart.grid(axis="y", color="#F3F4F6", linewidth=0.8)
|
|
|
|
ax_chart.legend(
|
|
loc="upper left", fontsize=9, frameon=False,
|
|
labelcolor=COLOR_GRAY,
|
|
)
|
|
|
|
# Annotate today's drop
|
|
ax_chart.annotate(
|
|
f"${today_rev:,.0f}\n({change_pct:+.1f}%)",
|
|
xy=(dates[-1], revenues[-1]),
|
|
xytext=(30, 25), textcoords="offset points",
|
|
fontsize=9, fontweight="bold", color=COLOR_ERROR,
|
|
fontfamily="sans-serif",
|
|
arrowprops=dict(arrowstyle="->", color=COLOR_ERROR, lw=1.2),
|
|
bbox=dict(boxstyle="round,pad=0.4", facecolor="white", edgecolor=COLOR_ERROR, alpha=0.9),
|
|
)
|
|
|
|
# Footer
|
|
fig.text(
|
|
0.5, 0.01,
|
|
"This is a demo report. Set up real notifications with your AI assistant.",
|
|
fontsize=8, color=COLOR_GRAY, ha="center", fontstyle="italic",
|
|
fontfamily="sans-serif",
|
|
)
|
|
|
|
# Save
|
|
chart_path = os.path.join(
|
|
tempfile.gettempdir(),
|
|
f"notify_test_{username}_{datetime.now():%Y%m%d%H%M%S}.png",
|
|
)
|
|
fig.savefig(chart_path, dpi=180, bbox_inches="tight", facecolor=COLOR_BG)
|
|
plt.close(fig)
|
|
|
|
caption = (
|
|
f"*Test Report for {username}*\n"
|
|
f"Revenue today: ${today_rev:,.0f} ({change_pct:+.1f}%)\n"
|
|
f"7d avg: ${week_avg:,.0f}\n\n"
|
|
f"This is a demo. Ask your AI assistant to create real notifications!"
|
|
)
|
|
|
|
return chart_path, caption
|