Created
September 4, 2026 06:45
-
-
Save fuyi/f63ae0c5f08249384c4f8f6ed33ab4ec to your computer and use it in GitHub Desktop.
qc_bullet_marts Production Gantt — 4 models, int_qc_bullet_session dominates at 31 min
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>qc_bullet_marts Production Gantt</title> | |
| <style> | |
| body { font-family: monospace; background: #1a1a2e; color: #eee; margin: 20px; } | |
| h2 { color: #a0c4ff; margin-bottom: 4px; } | |
| .subtitle { color: #666; font-size: 12px; margin-bottom: 16px; } | |
| .bar-ok { fill: #4cc9f0; opacity: 0.88; } | |
| .bar-error { fill: #ff4444; opacity: 0.88; } | |
| .dep-line { stroke: #555; stroke-width: 1; fill: none; stroke-dasharray: 3,3; } | |
| .model-label { font-size: 12px; fill: #aaa; text-anchor: end; } | |
| .time-label { font-size: 10px; fill: #555; } | |
| </style> | |
| </head> | |
| <body> | |
| <h2>qc_bullet_marts — Production execution (2026-09-04 01:01 UTC)</h2> | |
| <div class="subtitle">4 models · ~32 min wall-clock · <strong style="color:#ff9">int_qc_bullet_session dominates at 1869s (31 min)</strong></div> | |
| <div id="chart"></div> | |
| <p style="font-size:11px;color:#666"> | |
| <span style="color:#4cc9f0">■</span> success | |
| <span style="color:#555">- - -</span> dependency | |
| </p> | |
| <script> | |
| const data = [{"model": "int_qc_bullet_session", "start": "2026-09-04T01:01:40.148000+00:00", "end": "2026-09-04T01:32:49.078000+00:00", "dur": 1868.93, "status": "ok"}, {"model": "int_qc_bullet_key", "start": "2026-09-04T01:32:49.086000+00:00", "end": "2026-09-04T01:32:49.866000+00:00", "dur": 0.78, "status": "ok"}, {"model": "qc_question_user_day", "start": "2026-09-04T01:32:49.865000+00:00", "end": "2026-09-04T01:32:57.645000+00:00", "dur": 7.78, "status": "ok"}, {"model": "qc_question_team_day", "start": "2026-09-04T01:32:57.649000+00:00", "end": "2026-09-04T01:34:04.709000+00:00", "dur": 67.06, "status": "ok"}]; | |
| const deps = {"qc_question_team_day": ["qc_question_user_day"], "qc_question_user_day": ["int_qc_bullet_key", "int_qc_bullet_session"], "int_qc_bullet_key": ["int_qc_bullet_session"]}; | |
| data.sort((a,b) => new Date(a.start)-new Date(b.start)); | |
| const runStart = new Date(data[0].start).getTime(); | |
| const runEnd = Math.max(...data.map(d=>new Date(d.end).getTime())); | |
| const totalMs = runEnd - runStart; | |
| const LABEL_W = 300, BAR_H = 28, ROW_H = 36, TOP = 30, PX = 1200/(totalMs/1000); | |
| const W = LABEL_W + 1220; | |
| const H = TOP + data.length * ROW_H + 20; | |
| const rowIndex = {}; | |
| data.forEach((d,i) => rowIndex[d.model] = i); | |
| const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | |
| svg.setAttribute('width', W); svg.setAttribute('height', H); | |
| svg.style.display = 'block'; | |
| for (let s = 0; s <= totalMs/1000; s += 300) { | |
| const x = LABEL_W + s*PX; | |
| const tick = document.createElementNS('http://www.w3.org/2000/svg','line'); | |
| tick.setAttribute('x1',x); tick.setAttribute('y1',TOP-10); | |
| tick.setAttribute('x2',x); tick.setAttribute('y2',H); | |
| tick.setAttribute('stroke','#222'); tick.setAttribute('stroke-width','1'); | |
| svg.appendChild(tick); | |
| const lbl = document.createElementNS('http://www.w3.org/2000/svg','text'); | |
| lbl.setAttribute('x',x); lbl.setAttribute('y',TOP-14); | |
| lbl.setAttribute('class','time-label'); lbl.setAttribute('text-anchor','middle'); | |
| lbl.textContent = Math.round(s/60)+'m'; | |
| svg.appendChild(lbl); | |
| } | |
| Object.entries(deps).forEach(([model, parents]) => { | |
| const toRow = rowIndex[model]; | |
| if (toRow===undefined) return; | |
| const toY = TOP + toRow*ROW_H + BAR_H/2; | |
| const toX = LABEL_W + ((new Date(data[toRow].start).getTime()-runStart)/1000)*PX; | |
| parents.forEach(parent => { | |
| const fromRow = rowIndex[parent]; | |
| if (fromRow===undefined) return; | |
| const fromY = TOP + fromRow*ROW_H + BAR_H/2; | |
| const fromX = LABEL_W + ((new Date(data[fromRow].end).getTime()-runStart)/1000)*PX; | |
| const path = document.createElementNS('http://www.w3.org/2000/svg','path'); | |
| const mx = (fromX+toX)/2; | |
| path.setAttribute('d',`M${fromX},${fromY} C${mx},${fromY} ${mx},${toY} ${toX},${toY}`); | |
| path.setAttribute('class','dep-line'); | |
| svg.appendChild(path); | |
| }); | |
| }); | |
| data.forEach((d,i) => { | |
| const y = TOP + i*ROW_H; | |
| const x = LABEL_W + ((new Date(d.start).getTime()-runStart)/1000)*PX; | |
| const w = Math.max(3, ((new Date(d.end).getTime()-new Date(d.start).getTime())/1000)*PX); | |
| const lbl = document.createElementNS('http://www.w3.org/2000/svg','text'); | |
| lbl.setAttribute('x',LABEL_W-4); lbl.setAttribute('y',y+BAR_H-6); | |
| lbl.setAttribute('class','model-label'); | |
| lbl.textContent = d.model; | |
| svg.appendChild(lbl); | |
| const durLbl = document.createElementNS('http://www.w3.org/2000/svg','text'); | |
| durLbl.setAttribute('x',x+w+4); durLbl.setAttribute('y',y+BAR_H-6); | |
| durLbl.setAttribute('font-size','11'); durLbl.setAttribute('fill','#888'); | |
| durLbl.textContent = d.dur > 60 ? Math.round(d.dur/60)+'m' : d.dur+'s'; | |
| svg.appendChild(durLbl); | |
| const rect = document.createElementNS('http://www.w3.org/2000/svg','rect'); | |
| rect.setAttribute('x',x); rect.setAttribute('y',y); | |
| rect.setAttribute('width',w); rect.setAttribute('height',BAR_H); | |
| rect.setAttribute('rx',3); | |
| rect.setAttribute('class','bar bar-'+d.status); | |
| rect.innerHTML = `<title>${d.model} — ${d.dur.toFixed(1)}s</title>`; | |
| svg.appendChild(rect); | |
| }); | |
| document.getElementById('chart').appendChild(svg); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment