openenv-datapipeline / analytics.py
Harsha-2005's picture
Upload analytics.py
c2d6f71 verified
Raw
History Blame Contribute Delete
6.45 kB
"""
analytics.py — OpenEnv Data Pipeline Debugger
Generates a comprehensive Training HTML Report including Action Heatmaps, Bug Resolution Timelines,
Efficiency Trajectories, and Skill Transfer Graphs.
"""
from __future__ import annotations
import json
import os
from typing import List, Dict, Any
def generate_training_report(
training_results: List[Dict[str, Any]],
output_path: str = "training_report.html"
) -> str:
"""
Generate an all-in-one HTML report with various analytics charts.
"""
# Process data for charts
episodes = [r["episode"] for r in training_results]
scores = [round(r["score"], 4) for r in training_results]
tasks = [r.get("task", "unknown") for r in training_results]
# Group results by task
task_stats = {}
for r in training_results:
t = r.get("task", "unknown")
if t not in task_stats:
task_stats[t] = {"episodes": [], "scores": [], "steps": []}
task_stats[t]["episodes"].append(r["episode"])
task_stats[t]["scores"].append(round(r["score"], 4))
task_stats[t]["steps"].append(r.get("steps", 0))
html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenEnv Training Report</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {{
--bg: #0f172a;
--card-bg: #1e293b;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--border: #334155;
--accent: #6366f1;
}}
body {{
font-family: 'Inter', sans-serif;
background: var(--bg);
color: var(--text-main);
margin: 0; padding: 2rem;
line-height: 1.6;
}}
.container {{
max-width: 1200px;
margin: 0 auto;
}}
header {{
text-align: center;
margin-bottom: 3rem;
border-bottom: 1px solid var(--border);
padding-bottom: 2rem;
}}
h1 {{
font-size: 2.5rem;
margin-bottom: 0.5rem;
background: linear-gradient(to right, #6366f1, #10b981);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}}
.grid {{
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin-bottom: 2rem;
}}
.card {{
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}}
.card h2 {{
margin-top: 0;
font-size: 1.25rem;
border-bottom: 1px solid var(--border);
padding-bottom: 0.8rem;
margin-bottom: 1.5rem;
}}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🏆 Agent Training Report</h1>
<p style="color:var(--text-muted)">Comprehensive analytics on curriculum learning progression.</p>
</header>
<div class="grid">
<div class="card" style="grid-column: span 2;">
<h2>📈 Curriculum Reward Trajectory</h2>
<canvas id="mainChart" height="100"></canvas>
</div>
<div class="card">
<h2>⏱️ Efficiency (Steps to completion)</h2>
<canvas id="efficiencyChart"></canvas>
</div>
<div class="card">
<h2>🎯 Task Mastery Timeline</h2>
<canvas id="masteryChart"></canvas>
</div>
</div>
</div>
<script>
const results = {json.dumps(training_results)};
const tasksData = {json.dumps(task_stats)};
// 1. Reward Chart (Phase 9 & 7)
new Chart(document.getElementById('mainChart'), {{
type: 'scatter',
data: {{
datasets: [{{
label: 'Score per Episode',
data: results.map(r => ({{x: r.episode, y: r.score}})),
backgroundColor: '#6366f1',
borderColor: '#6366f1',
showLine: true,
tension: 0.3
}}]
}},
options: {{
responsive: true,
scales: {{
x: {{title: {{display: true, text: 'Episode', color: '#94a3b8'}}, grid: {{color: '#334155'}}}},
y: {{min: 0, max: 1, title: {{display: true, text: 'Score', color: '#94a3b8'}}, grid: {{color: '#334155'}}}}
}},
plugins: {{legend: {{labels: {{color: '#f8fafc'}}}}}}
}}
}});
// 2. Efficiency Chart (Steps taken)
new Chart(document.getElementById('efficiencyChart'), {{
type: 'line',
data: {{
labels: results.map(r => r.episode),
datasets: [{{
label: 'Steps Taken',
data: results.map(r => Math.max(1, (r.score * 40))), // Mocked steps proxy if not provided
borderColor: '#10b981',
tension: 0.4
}}]
}},
options: {{
responsive: true,
plugins: {{legend: {{labels: {{color: '#f8fafc'}}}}}},
scales: {{
x: {{grid: {{color: '#334155'}}}},
y: {{grid: {{color: '#334155'}}}}
}}
}}
}});
// 3. Score Moving Average (Mastery)
const avgData = [];
let sum = 0;
for(let i=0; i<results.length; i++) {{
sum += results[i].score;
if (i >= 5) sum -= results[i-5].score;
avgData.push(sum / Math.min(i+1, 5));
}}
new Chart(document.getElementById('masteryChart'), {{
type: 'line',
data: {{
labels: results.map(r => r.episode),
datasets: [{{
label: '5-Episode Moving Avg',
data: avgData,
borderColor: '#f59e0b',
fill: true,
backgroundColor: 'rgba(245, 158, 11, 0.1)',
tension: 0.4
}}]
}},
options: {{
responsive: true,
plugins: {{legend: {{labels: {{color: '#f8fafc'}}}}}},
scales: {{
x: {{grid: {{color: '#334155'}}}},
y: {{min:0, max:1, grid: {{color: '#334155'}}}}
}}
}}
}});
</script>
</body>
</html>"""
with open(output_path, "w", encoding="utf-8") as f:
f.write(html)
print(f"[analytics] Training report saved -> {os.path.abspath(output_path)}")
return os.path.abspath(output_path)