Kalletlamadhav/sql-optimization-env
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="utf-8" />5 <meta name="viewport" content="width=device-width, initial-scale=1" />6 <title>SQL Optimization Env — Domain Dashboard</title>7 <style>8 :root {9 --bg: #0f1419;10 --panel: #1a2332;11 --border: #2d3a4d;12 --text: #e7ecf3;13 --muted: #8b9cb3;14 --accent: #3d8bfd;15 --gst: #3d8bfd;16 --pds: #f5a623;17 --ok: #3ecf8e;18 }19 * { box-sizing: border-box; }20 body {21 margin: 0;22 font-family: "Segoe UI", system-ui, sans-serif;23 background: var(--bg);24 color: var(--text);25 line-height: 1.5;26 }27 a { color: var(--accent); }28 header {29 padding: 1.25rem 1.5rem;30 border-bottom: 1px solid var(--border);31 display: flex;32 flex-wrap: wrap;33 align-items: center;34 gap: 1rem;35 justify-content: space-between;36 }37 header h1 { margin: 0; font-size: 1.25rem; font-weight: 600; }38 .health-bar {39 display: flex;40 flex-wrap: wrap;41 gap: 0.75rem 1.25rem;42 font-size: 0.875rem;43 color: var(--muted);44 }45 .health-bar strong { color: var(--text); }46 .pill {47 display: inline-block;48 padding: 0.15rem 0.5rem;49 border-radius: 999px;50 background: var(--panel);51 border: 1px solid var(--border);52 font-size: 0.75rem;53 margin-right: 0.35rem;54 }55 .pill.ok { border-color: var(--ok); color: var(--ok); }56 main { padding: 1.25rem 1.5rem 2.5rem; max-width: 1200px; margin: 0 auto; }57 h2 { font-size: 1rem; margin: 2rem 0 0.75rem; color: var(--muted); font-weight: 600; }58 .cards {59 display: grid;60 grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));61 gap: 1rem;62 }63 .card {64 background: var(--panel);65 border: 1px solid var(--border);66 border-radius: 10px;67 padding: 1rem 1.1rem;68 display: flex;69 flex-direction: column;70 gap: 0.5rem;71 }72 .card h3 { margin: 0; font-size: 1.05rem; }73 .card .scale { font-size: 0.8rem; color: var(--muted); }74 .counts { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.25rem; }75 .counts span {76 font-size: 0.72rem;77 background: #0f1419;78 padding: 0.2rem 0.45rem;79 border-radius: 6px;80 border: 1px solid var(--border);81 }82 .samples { margin: 0.35rem 0 0; padding-left: 1.1rem; font-size: 0.78rem; color: var(--muted); }83 .samples li { margin: 0.25rem 0; }84 details.ddl {85 margin-top: 0.5rem;86 border-top: 1px solid var(--border);87 padding-top: 0.5rem;88 }89 details.ddl summary {90 cursor: pointer;91 font-size: 0.8rem;92 color: var(--accent);93 }94 details.ddl pre {95 margin: 0.5rem 0 0;96 max-height: 220px;97 overflow: auto;98 font-size: 0.65rem;99 background: #0f1419;100 padding: 0.6rem;101 border-radius: 6px;102 border: 1px solid var(--border);103 white-space: pre-wrap;104 }105 .map-wrap {106 background: var(--panel);107 border: 1px solid var(--border);108 border-radius: 10px;109 padding: 1rem;110 }111 .map-controls {112 display: flex;113 flex-wrap: wrap;114 gap: 0.5rem;115 align-items: center;116 margin-bottom: 0.75rem;117 font-size: 0.85rem;118 }119 .map-controls label { margin-right: 0.35rem; color: var(--muted); }120 .map-controls button {121 background: #0f1419;122 border: 1px solid var(--border);123 color: var(--text);124 padding: 0.35rem 0.65rem;125 border-radius: 6px;126 cursor: pointer;127 font-size: 0.8rem;128 }129 .map-controls button.active {130 border-color: var(--accent);131 color: var(--accent);132 }133 .map-legend {134 font-size: 0.75rem;135 color: var(--muted);136 margin-top: 0.5rem;137 }138 .err { color: #f87171; font-size: 0.85rem; }139 footer {140 margin-top: 2rem;141 padding: 1rem 1.5rem;142 border-top: 1px solid var(--border);143 font-size: 0.8rem;144 color: var(--muted);145 text-align: center;146 }147 </style>148</head>149<body>150 <header>151 <h1>SQL Optimization Environment</h1>152 <div class="health-bar" id="health-bar">153 <span>Loading <code>/health</code>…</span>154 </div>155 </header>156 <main>157 <p style="color:var(--muted);font-size:0.9rem;margin:0 0 1rem;">158 Indian public-data domains for query optimization tasks.159 API: <a href="/docs">OpenAPI docs</a> · <a href="/health"><code>/health</code></a> ·160 <a href="/domains"><code>/domains</code></a>161 </p>162 <div id="domain-error" class="err" hidden></div>163 <h2>Domains</h2>164 <div class="cards" id="domain-cards"></div>165 166 <h2>India map — data density (GST vs PDS)</h2>167 <div class="map-wrap">168 <div class="map-controls">169 <span><label>Layer</label></span>170 <button type="button" data-layer="gst" class="active">GST invoices</button>171 <button type="button" data-layer="pds">PDS cards</button>172 <button type="button" data-layer="both">Combined</button>173 </div>174 <svg id="india-map" viewBox="0 0 200 160" width="100%" style="max-height:420px;display:block;">175 <rect width="200" height="160" fill="#0f1419" rx="4" />176 <path id="india-outline" fill="#152030" stroke="#2d3a4d" stroke-width="0.4"177 d="M28 38 L172 32 L178 48 L184 95 L168 128 L142 142 L96 148 L44 138 L22 98 Z" />178 <g id="bubbles"></g>179 <g id="labels"></g>180 </svg>181 <p class="map-legend">Bubble area scales with row counts for the selected layer (state_code). Positions are stylized, not survey-accurate.</p>182 </div>183 <h2>🚀 Try Live Optimization</h2>184 185<div class="card">186 <button onclick="resetEnv()">🔄 Reset Task</button>187 <pre id="task-output"></pre>188 189 <h3>✏️ Optimized Query</h3>190 <textarea id="query-input" style="width:100%;height:120px;"></textarea>191 192 <input id="pattern-input" placeholder="Pattern (e.g. SELECT_STAR)" style="width:100%;margin-top:8px;" />193 194 <textarea id="explanation-input" placeholder="Explain your optimization..." style="width:100%;height:80px;margin-top:8px;"></textarea>195 196 <button onclick="runStep()">▶ Run Step</button>197 198 <h3>📊 Reward</h3>199 <pre id="reward-output"></pre>200 201 <h3>📈 Execution Plan</h3>202 <pre id="plan-output"></pre>203 204 <h3>🎯 Curriculum</h3>205 <pre id="state-output"></pre>206</div>207 </main>208 <footer>OpenEnv SQL optimization · static dashboard</footer>209 210 <script>211 const STATE_POS = {212 '01': [48, 28], '02': [62, 34], '03': [58, 40], '06': [64, 46], '07': [66, 44], '08': [42, 58],213 '09': [76, 52], '10': [102, 68], '11': [156, 80], '12': [176, 72], '13': [168, 62], '14': [150, 52],214 '18': [188, 78], '19': [98, 58], '21': [88, 72], '22': [74, 70], '23': [62, 70], '24': [30, 82],215 '27': [52, 92], '29': [46, 118], '32': [48, 134], '33': [76, 138], '36': [70, 110], '37': [90, 96]216 };217 218 let mapData = { gst_by_state: {}, pds_by_state: {}, state_labels: {} };219 let mapLayer = 'gst';220 221 async function loadHealth() {222 const el = document.getElementById('health-bar');223 try {224 const r = await fetch('/health');225 const j = await r.json();226 const counts = j.table_row_counts || {};227 const keys = Object.keys(counts).sort();228 const preview = keys.slice(0, 6).map(k => `${k}: ${counts[k]}`).join(' · ');229 el.innerHTML = `230 <span class="pill ok">${j.status}</span>231 <span><strong>Total rows</strong> ${j.total_rows != null ? j.total_rows.toLocaleString() : '—'}</span>232 <span><strong>DB</strong> <code style="font-size:0.75rem">${(j.database_path || '').split('/').slice(-3).join('/')}</code></span>233 <span><strong>Tables</strong> ${keys.length} · ${preview}${keys.length > 6 ? ' …' : ''}</span>234 `;235 } catch (e) {236 el.innerHTML = `<span class="err">Health check failed: ${e.message}</span>`;237 }238 }239 240 function renderCards(domains) {241 const root = document.getElementById('domain-cards');242 root.innerHTML = '';243 domains.forEach(d => {244 const card = document.createElement('article');245 card.className = 'card';246 const tables = (d.tables || []).map(t =>247 `<span>${t.table}: <strong>${t.row_count.toLocaleString()}</strong></span>`248 ).join('');249 const samples = (d.sample_queries || []).map(q => `<li><code>${escapeHtml(q)}</code></li>`).join('');250 card.innerHTML = `251 <h3>${escapeHtml(d.domain)}</h3>252 <div class="scale">${escapeHtml(d.real_world_scale || '')}</div>253 <div style="font-size:0.85rem;color:var(--muted)">${escapeHtml(d.description || '')}</div>254 <div class="counts">${tables}</div>255 <ul class="samples">${samples}</ul>256 <details class="ddl">257 <summary>Schema DDL (collapsible)</summary>258 <pre>${escapeHtml(d.schema_ddl || '')}</pre>259 </details>260 `;261 root.appendChild(card);262 });263 }264 265 function escapeHtml(s) {266 return String(s)267 .replace(/&/g, '&')268 .replace(/</g, '<')269 .replace(/>/g, '>')270 .replace(/"/g, '"');271 }272 273 function valueForState(code, layer) {274 const g = mapData.gst_by_state[code] || 0;275 const p = mapData.pds_by_state[code] || 0;276 if (layer === 'gst') return g;277 if (layer === 'pds') return p;278 return g + p;279 }280 281 function drawMap() {282 const bubbles = document.getElementById('bubbles');283 const labels = document.getElementById('labels');284 bubbles.innerHTML = '';285 labels.innerHTML = '';286 const codes = new Set([287 ...Object.keys(mapData.gst_by_state || {}),288 ...Object.keys(mapData.pds_by_state || {})289 ]);290 let maxV = 0;291 codes.forEach(c => { maxV = Math.max(maxV, valueForState(c, mapLayer)); });292 const scale = maxV > 0 ? 1 / Math.sqrt(maxV) : 1;293 294 codes.forEach(code => {295 const pos = STATE_POS[code];296 if (!pos) return;297 const v = valueForState(code, mapLayer);298 if (v <= 0) return;299 const r = Math.max(2.2, Math.sqrt(v) * scale * 14);300 const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');301 circle.setAttribute('cx', pos[0]);302 circle.setAttribute('cy', pos[1]);303 circle.setAttribute('r', r);304 const fill = mapLayer === 'pds' ? 'rgba(245,166,35,0.45)' :305 mapLayer === 'gst' ? 'rgba(61,139,253,0.45)' : 'rgba(160,120,255,0.4)';306 circle.setAttribute('fill', fill);307 circle.setAttribute('stroke', mapLayer === 'pds' ? '#f5a623' : '#3d8bfd');308 circle.setAttribute('stroke-width', '0.35');309 const lbl = mapData.state_labels[code] || code;310 const tip = document.createElementNS('http://www.w3.org/2000/svg', 'title');311 tip.textContent = `${lbl} (${code}): ${v.toLocaleString()} rows`;312 circle.appendChild(tip);313 bubbles.appendChild(circle);314 315 const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');316 text.setAttribute('x', pos[0]);317 text.setAttribute('y', pos[1] + r + 3.5);318 text.setAttribute('text-anchor', 'middle');319 text.setAttribute('fill', '#8b9cb3');320 text.setAttribute('font-size', '3.2');321 text.textContent = lbl;322 labels.appendChild(text);323 });324 }325 326 document.querySelectorAll('.map-controls button').forEach(btn => {327 btn.addEventListener('click', () => {328 document.querySelectorAll('.map-controls button').forEach(b => b.classList.remove('active'));329 btn.classList.add('active');330 mapLayer = btn.getAttribute('data-layer');331 drawMap();332 });333 });334 335 async function loadDomains() {336 const err = document.getElementById('domain-error');337 err.hidden = true;338 try {339 const r = await fetch('/domains');340 if (!r.ok) throw new Error(await r.text());341 const j = await r.json();342 mapData = j.map || mapData;343 renderCards(j.domains || []);344 drawMap();345 } catch (e) {346 err.textContent = 'Failed to load /domains: ' + e.message;347 err.hidden = false;348 }349 }350 351 loadHealth();352 loadDomains();353 setInterval(loadHealth, 12000);354 async function resetEnv() {355 const res = await fetch('/reset');356 const data = await res.json();357 358 document.getElementById("task-output").innerText =359 "🎯 TASK:\n" + data.goal + "\n\n" +360 "💡 HINT:\n" + data.anti_pattern_hint + "\n\n" +361 "🆔 Task ID: " + data.task_id;362 363 document.getElementById("query-input").value =364 data.current_query || "";365 366 document.getElementById("plan-output").innerText =367 JSON.stringify(data.execution_plan, null, 2);368 369 document.getElementById("state-output").innerText =370 "Level: " + data.curriculum_level;371}372 373async function runStep() {374 const body = {375 optimized_query: document.getElementById("query-input").value,376 identified_pattern: document.getElementById("pattern-input").value,377 explanation: document.getElementById("explanation-input").value,378 index_statements: [],379 schema_analysis: ""380 };381 382 const res = await fetch('/step', {383 method: "POST",384 headers: {"Content-Type": "application/json"},385 body: JSON.stringify(body)386 });387 388 const data = await res.json();389 390 document.getElementById("reward-output").innerText =391 JSON.stringify(data.reward || data, null, 2);392 393 document.getElementById("plan-output").innerText =394 JSON.stringify(data.observation?.execution_plan || {}, null, 2);395 396 loadState();397}398 399async function loadState() {400 const res = await fetch('/state');401 const data = await res.json();402 403 document.getElementById("state-output").innerText =404 JSON.stringify(data, null, 2);405}406 </script>407</body>408</html>409 