mirror of
https://github.com/codeflash-ai/codeflash-agent.git
synced 2026-05-04 18:25:19 +00:00
Dash app at .codeflash/standups/ for weekly eng meetings. Pulls live PR data across 4 org repos, renders markdown standup notes, integrates CI audit report with corrected billing numbers from real GitHub API data. Deployed to Plotly Cloud.
1543 lines
52 KiB
Python
1543 lines
52 KiB
Python
"""Codeflash Weekly Engineering Meeting Dashboard
|
|
|
|
Two-tab report served at http://localhost:8052/:
|
|
1. This Week — hero metrics, open PRs, recently merged PRs, meeting notes
|
|
2. CI Audit — operational audit findings and savings
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import plotly.graph_objects as go
|
|
from dash import Dash, Input, Output, dash_table, dcc, html
|
|
from theme import (
|
|
ACCENT,
|
|
AMBER,
|
|
BG,
|
|
BLUE,
|
|
CARD,
|
|
CARD_BORDER,
|
|
FLEX_ROW,
|
|
FONT,
|
|
GRAY,
|
|
GREEN,
|
|
GRID_OVERLAY,
|
|
MONO,
|
|
PURPLE,
|
|
RED,
|
|
SECTION_H2,
|
|
SLATE,
|
|
TAB_SELECTED,
|
|
TAB_STYLE,
|
|
TABLE_CELL,
|
|
TABLE_DATA,
|
|
TABLE_DATA_CONDITIONAL,
|
|
TABLE_HEADER,
|
|
TABLE_WRAP,
|
|
WHITE,
|
|
)
|
|
|
|
DATA_FILE = Path(__file__).parent / "data.json"
|
|
NOTES_DIR = Path(__file__).parent / "notes"
|
|
|
|
CI_AUDIT_ANCHORS = {
|
|
"ci-findings",
|
|
"ci-savings",
|
|
"ci-actions",
|
|
"ci-reproduce",
|
|
"ci-prs-merged",
|
|
"ci-billing",
|
|
"ci-charts",
|
|
}
|
|
|
|
|
|
def _slug(text: str) -> str:
|
|
return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")
|
|
|
|
|
|
def _title_case(text: str) -> str:
|
|
return " ".join(w[0].upper() + w[1:] if w else w for w in text.split(" "))
|
|
|
|
|
|
_state = {"last_mtime": 0.0}
|
|
|
|
|
|
def _notes_changed() -> bool:
|
|
latest = max(
|
|
(f.stat().st_mtime for f in NOTES_DIR.glob("*.md")),
|
|
default=0.0,
|
|
)
|
|
if latest > _state["last_mtime"]:
|
|
_state["last_mtime"] = latest
|
|
return True
|
|
return False
|
|
|
|
|
|
def load_data() -> dict:
|
|
if DATA_FILE.exists():
|
|
return json.loads(DATA_FILE.read_text(encoding="utf-8"))
|
|
try:
|
|
from generate import main as regenerate
|
|
|
|
regenerate()
|
|
return json.loads(DATA_FILE.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
return {}
|
|
|
|
|
|
app = Dash(__name__)
|
|
app.title = "Codeflash Weekly Eng Meeting"
|
|
server = app.server
|
|
|
|
|
|
def hero_card(label: str, value: str | int, color: str = ACCENT) -> html.Div:
|
|
return html.Div(
|
|
[
|
|
html.Div(
|
|
str(value),
|
|
style={
|
|
"fontSize": "36px",
|
|
"fontWeight": "700",
|
|
"color": color,
|
|
"fontFamily": MONO,
|
|
},
|
|
),
|
|
html.Div(
|
|
label,
|
|
style={"fontSize": "13px", "color": GRAY, "marginTop": "4px"},
|
|
),
|
|
],
|
|
style={
|
|
**CARD,
|
|
"textAlign": "center",
|
|
"minWidth": "160px",
|
|
"flex": "1",
|
|
},
|
|
)
|
|
|
|
|
|
def pr_table(prs: list[dict], table_id: str) -> html.Div:
|
|
rows = []
|
|
for pr in prs:
|
|
rows.append(
|
|
{
|
|
"Repo": pr["repo"],
|
|
"#": pr["number"],
|
|
"Title": pr["title"],
|
|
"Author": pr["author"],
|
|
"Updated": pr["updated_at"][:10],
|
|
}
|
|
)
|
|
if not rows:
|
|
return html.Div("No PRs", style={"color": GRAY, "padding": "16px"})
|
|
return html.Div(
|
|
dash_table.DataTable(
|
|
id=table_id,
|
|
columns=[
|
|
{"name": c, "id": c}
|
|
for c in ["Repo", "#", "Title", "Author", "Updated"]
|
|
],
|
|
data=rows,
|
|
style_header=TABLE_HEADER,
|
|
style_cell=TABLE_CELL,
|
|
style_data=TABLE_DATA,
|
|
style_data_conditional=TABLE_DATA_CONDITIONAL,
|
|
page_size=15,
|
|
style_table={"overflowX": "auto"},
|
|
),
|
|
style=TABLE_WRAP,
|
|
)
|
|
|
|
|
|
SECTION_COLORS = {
|
|
"done (yesterday)": GREEN,
|
|
"done (today)": GREEN,
|
|
"done": GREEN,
|
|
"blocked": RED,
|
|
"asks": AMBER,
|
|
"next": BLUE,
|
|
"in summary": WHITE,
|
|
"strategy: vertical optimization": PURPLE,
|
|
"strategy in action: typeagent-py": PURPLE,
|
|
"follow-ups": AMBER,
|
|
"open questions": RED,
|
|
"dollar impact: what vertical optimization saves customers": GREEN,
|
|
"competitive landscape: codspeed and why we win": PURPLE,
|
|
"what's next for the team": BLUE,
|
|
}
|
|
|
|
COLLAPSED_SECTIONS = {
|
|
"strategy: vertical optimization",
|
|
"strategy in action: typeagent-py",
|
|
"in summary",
|
|
}
|
|
|
|
|
|
def _flush_list_items(
|
|
section_blocks: list, list_items: list, *, is_flat: bool
|
|
) -> None:
|
|
if not list_items:
|
|
return
|
|
if is_flat:
|
|
section_blocks.append(
|
|
html.Div(
|
|
list_items[:], style={"paddingLeft": "4px", "margin": "0"}
|
|
)
|
|
)
|
|
else:
|
|
section_blocks.append(
|
|
html.Ol(
|
|
list_items[:], style={"paddingLeft": "20px", "margin": "0"}
|
|
)
|
|
)
|
|
list_items.clear()
|
|
|
|
|
|
def _parse_inline(text: str) -> list:
|
|
parts = re.split(r"\*\*(.+?)\*\*", text)
|
|
if len(parts) == 1:
|
|
return [text]
|
|
result: list = []
|
|
for i, part in enumerate(parts):
|
|
if not part:
|
|
continue
|
|
if i % 2 == 1:
|
|
result.append(
|
|
html.Strong(part, style={"color": WHITE, "fontWeight": "600"})
|
|
)
|
|
else:
|
|
result.append(part)
|
|
return result
|
|
|
|
|
|
def _is_table_line(text: str) -> bool:
|
|
return text.startswith("|") and text.endswith("|") and text.count("|") >= 3
|
|
|
|
|
|
def _is_separator_line(text: str) -> bool:
|
|
return bool(re.match(r"^\|[\s\-:|]+\|$", text))
|
|
|
|
|
|
def _render_md_table(lines: list[str]) -> html.Div:
|
|
headers = [c.strip() for c in lines[0].split("|")[1:-1]]
|
|
data_lines = [ln for ln in lines[1:] if not _is_separator_line(ln)]
|
|
rows = []
|
|
for line in data_lines:
|
|
cells = [c.strip() for c in line.split("|")[1:-1]]
|
|
rows.append(cells)
|
|
|
|
header_row = html.Tr(
|
|
[
|
|
html.Th(
|
|
h,
|
|
style={
|
|
"padding": "10px 16px",
|
|
"color": ACCENT,
|
|
"fontWeight": "600",
|
|
"fontSize": "12px",
|
|
"textTransform": "uppercase",
|
|
"letterSpacing": "0.5px",
|
|
"borderBottom": f"1px solid {CARD_BORDER}",
|
|
"textAlign": "left",
|
|
},
|
|
)
|
|
for h in headers
|
|
]
|
|
)
|
|
|
|
body_rows = []
|
|
for i, row in enumerate(rows):
|
|
bg = "rgba(31,31,35,0.6)" if i % 2 else "rgba(24,24,27,0.5)"
|
|
body_rows.append(
|
|
html.Tr(
|
|
[
|
|
html.Td(
|
|
_parse_inline(cell),
|
|
style={
|
|
"padding": "10px 16px",
|
|
"color": SLATE,
|
|
"fontSize": "13px",
|
|
"borderBottom": f"1px solid {CARD_BORDER}",
|
|
},
|
|
)
|
|
for cell in row
|
|
],
|
|
style={"backgroundColor": bg},
|
|
)
|
|
)
|
|
|
|
return html.Div(
|
|
html.Table(
|
|
[html.Thead(header_row), html.Tbody(body_rows)],
|
|
style={
|
|
"width": "100%",
|
|
"borderCollapse": "collapse",
|
|
},
|
|
),
|
|
style={
|
|
"borderRadius": "8px",
|
|
"overflow": "hidden",
|
|
"border": f"1px solid {CARD_BORDER}",
|
|
"marginBottom": "12px",
|
|
"marginTop": "8px",
|
|
},
|
|
)
|
|
|
|
|
|
def _build_savings_chart() -> dcc.Graph:
|
|
layers = [
|
|
"Layer 4 — Eng Team",
|
|
"Layer 3 — Infra",
|
|
"Layer 2 — Dev Tooling",
|
|
"Layer 1 — Code",
|
|
]
|
|
low = [400, 1500, 500, 200]
|
|
high = [1500, 6000, 2000, 800]
|
|
colors = [BLUE, AMBER, PURPLE, GREEN]
|
|
|
|
fig = go.Figure()
|
|
fig.add_trace(
|
|
go.Bar(
|
|
y=layers,
|
|
x=[h - lo for h, lo in zip(high, low)],
|
|
base=low,
|
|
orientation="h",
|
|
marker_color=colors,
|
|
text=[f"${lo:,}K - ${h:,}K" for lo, h in zip(low, high)],
|
|
textposition="inside",
|
|
textfont={"color": WHITE, "size": 12},
|
|
)
|
|
)
|
|
fig.update_layout(
|
|
title={
|
|
"text": "Estimated Annual Savings by Layer (mid-size org)",
|
|
"font": {"color": WHITE, "size": 14},
|
|
},
|
|
paper_bgcolor="rgba(0,0,0,0)",
|
|
plot_bgcolor="rgba(0,0,0,0)",
|
|
font={"color": SLATE, "family": FONT},
|
|
xaxis={
|
|
"gridcolor": CARD_BORDER,
|
|
"color": GRAY,
|
|
"title": "$ thousands / year",
|
|
"showgrid": True,
|
|
},
|
|
yaxis={"gridcolor": CARD_BORDER, "color": GRAY},
|
|
margin={"l": 150, "r": 40, "t": 50, "b": 40},
|
|
height=250,
|
|
showlegend=False,
|
|
)
|
|
return dcc.Graph(figure=fig, config={"displayModeBar": False})
|
|
|
|
|
|
def _build_comparison_table() -> dcc.Graph:
|
|
fig = go.Figure(
|
|
data=[
|
|
go.Table(
|
|
header={
|
|
"values": [
|
|
"<b>Dimension</b>",
|
|
"<b>CodSpeed</b>",
|
|
"<b>Codeflash</b>",
|
|
],
|
|
"fill_color": "rgba(24,24,27,0.8)",
|
|
"font": {"color": ACCENT, "size": 12},
|
|
"align": "left",
|
|
"height": 36,
|
|
"line": {"color": CARD_BORDER},
|
|
},
|
|
cells={
|
|
"values": [
|
|
[
|
|
"Scope",
|
|
"Deliverable",
|
|
"Model",
|
|
"Depth",
|
|
"Coverage",
|
|
"Outcome",
|
|
],
|
|
[
|
|
"Layer 1 detection only",
|
|
"Dashboard + PR checks",
|
|
"SaaS ($15/user/mo)",
|
|
'"This function got slower"',
|
|
"Code perf regressions",
|
|
"Alerts",
|
|
],
|
|
[
|
|
"All 4 layers + remediation",
|
|
"Merged PRs + benchmarks",
|
|
"Service engagement + ROI",
|
|
"Root cause + fix + proof",
|
|
"Code, CI, Docker, K8s, team",
|
|
"Shipped optimizations + $$$",
|
|
],
|
|
],
|
|
"fill_color": [
|
|
"rgba(24,24,27,0.5)",
|
|
"rgba(248,113,113,0.08)",
|
|
"rgba(74,222,128,0.08)",
|
|
],
|
|
"font": {"color": [SLATE, GRAY, GREEN], "size": 12},
|
|
"align": "left",
|
|
"height": 32,
|
|
"line": {"color": CARD_BORDER},
|
|
},
|
|
)
|
|
]
|
|
)
|
|
fig.update_layout(
|
|
paper_bgcolor="rgba(0,0,0,0)",
|
|
margin={"l": 0, "r": 0, "t": 0, "b": 0},
|
|
height=260,
|
|
)
|
|
return dcc.Graph(figure=fig, config={"displayModeBar": False})
|
|
|
|
|
|
def notes_section(notes: list[dict]) -> html.Div:
|
|
if not notes:
|
|
return html.Div(
|
|
"No meeting notes yet.", style={"color": GRAY, "padding": "16px"}
|
|
)
|
|
children = []
|
|
for note in notes:
|
|
title = note.get("title", note["date"])
|
|
sections = note.get("sections", {})
|
|
blocks: list = []
|
|
for section_name, bullets in sections.items():
|
|
header_color = SECTION_COLORS.get(section_name, ACCENT)
|
|
anchor = _slug(section_name)
|
|
is_checklist = section_name == "follow-ups"
|
|
is_qa = section_name == "open questions"
|
|
is_collapsed = section_name in COLLAPSED_SECTIONS
|
|
section_blocks: list = []
|
|
list_items: list = []
|
|
table_lines: list[str] = []
|
|
|
|
for b in bullets:
|
|
if table_lines and not _is_table_line(b):
|
|
_flush_list_items(
|
|
section_blocks,
|
|
list_items,
|
|
is_flat=is_qa or is_checklist,
|
|
)
|
|
section_blocks.append(_render_md_table(table_lines))
|
|
table_lines = []
|
|
|
|
if b.startswith("source:"):
|
|
source_url = b.removeprefix("source:")
|
|
section_blocks.append(
|
|
html.Div(
|
|
[
|
|
html.Div(
|
|
[
|
|
html.Span(
|
|
"Sourced from meeting transcript",
|
|
style={
|
|
"color": SLATE,
|
|
"fontSize": "13px",
|
|
},
|
|
),
|
|
html.A(
|
|
"View in Granola",
|
|
href=source_url,
|
|
target="_blank",
|
|
style={
|
|
"color": ACCENT,
|
|
"fontSize": "13px",
|
|
"textDecoration": "none",
|
|
"fontWeight": "600",
|
|
"marginLeft": "8px",
|
|
},
|
|
),
|
|
],
|
|
style={
|
|
"display": "flex",
|
|
"alignItems": "center",
|
|
"gap": "4px",
|
|
},
|
|
),
|
|
html.Div(
|
|
'Tip: Use the "Ask anything" chat on the transcript page to get clarity on any item.',
|
|
style={
|
|
"color": GRAY,
|
|
"fontSize": "12px",
|
|
"marginTop": "4px",
|
|
},
|
|
),
|
|
],
|
|
style={
|
|
"padding": "10px 12px",
|
|
"background": "rgba(255,210,39,0.06)",
|
|
"borderRadius": "6px",
|
|
"border": f"1px solid {CARD_BORDER}",
|
|
"marginBottom": "12px",
|
|
},
|
|
)
|
|
)
|
|
continue
|
|
|
|
if b.startswith("code:"):
|
|
_flush_list_items(
|
|
section_blocks,
|
|
list_items,
|
|
is_flat=is_qa or is_checklist,
|
|
)
|
|
section_blocks.append(
|
|
html.Pre(
|
|
b.removeprefix("code:"),
|
|
style={
|
|
"color": GREEN,
|
|
"fontSize": "13px",
|
|
"lineHeight": "1.5",
|
|
"padding": "16px 20px",
|
|
"background": "rgba(0,0,0,0.3)",
|
|
"borderRadius": "8px",
|
|
"border": f"1px solid {CARD_BORDER}",
|
|
"fontFamily": MONO,
|
|
"overflowX": "auto",
|
|
"margin": "8px 0",
|
|
},
|
|
)
|
|
)
|
|
continue
|
|
|
|
if b.startswith("### "):
|
|
_flush_list_items(
|
|
section_blocks,
|
|
list_items,
|
|
is_flat=is_qa or is_checklist,
|
|
)
|
|
section_blocks.append(
|
|
html.H5(
|
|
b.removeprefix("### "),
|
|
style={
|
|
"color": WHITE,
|
|
"fontSize": "14px",
|
|
"fontWeight": "600",
|
|
"margin": "16px 0 8px 0",
|
|
"borderBottom": f"1px solid {CARD_BORDER}",
|
|
"paddingBottom": "6px",
|
|
},
|
|
)
|
|
)
|
|
continue
|
|
|
|
if _is_table_line(b):
|
|
_flush_list_items(
|
|
section_blocks,
|
|
list_items,
|
|
is_flat=is_qa or is_checklist,
|
|
)
|
|
table_lines.append(b)
|
|
continue
|
|
|
|
if is_qa:
|
|
if b.startswith("answer:"):
|
|
answer_text = b.removeprefix("answer:")
|
|
if list_items:
|
|
list_items[-1].children.append(
|
|
html.Div(
|
|
[
|
|
html.Div(
|
|
"ANSWER",
|
|
style={
|
|
"color": GREEN,
|
|
"fontSize": "10px",
|
|
"fontWeight": "700",
|
|
"letterSpacing": "0.5px",
|
|
"marginBottom": "4px",
|
|
},
|
|
),
|
|
html.Div(
|
|
answer_text,
|
|
style={
|
|
"color": SLATE,
|
|
"fontSize": "13px",
|
|
"lineHeight": "1.6",
|
|
},
|
|
),
|
|
],
|
|
style={
|
|
"marginTop": "10px",
|
|
"paddingTop": "10px",
|
|
"borderTop": f"1px solid {CARD_BORDER}",
|
|
},
|
|
)
|
|
)
|
|
continue
|
|
parts = b.split(" — ", 1)
|
|
question_text = parts[0]
|
|
citation_text = parts[1] if len(parts) > 1 else None
|
|
asker = ""
|
|
q_body = question_text
|
|
if ": " in question_text:
|
|
asker, q_body = question_text.split(": ", 1)
|
|
card_children: list = []
|
|
if asker:
|
|
card_children.append(
|
|
html.Div(
|
|
asker,
|
|
style={
|
|
"color": AMBER,
|
|
"fontSize": "11px",
|
|
"fontWeight": "700",
|
|
"textTransform": "uppercase",
|
|
"letterSpacing": "0.5px",
|
|
"marginBottom": "4px",
|
|
},
|
|
)
|
|
)
|
|
card_children.append(
|
|
html.Div(
|
|
q_body,
|
|
style={
|
|
"color": WHITE,
|
|
"fontSize": "15px",
|
|
"fontWeight": "500",
|
|
"lineHeight": "1.5",
|
|
"marginBottom": "6px",
|
|
},
|
|
)
|
|
)
|
|
if citation_text:
|
|
card_children.append(
|
|
html.Div(
|
|
citation_text,
|
|
style={
|
|
"color": GRAY,
|
|
"fontSize": "12px",
|
|
"fontStyle": "italic",
|
|
"lineHeight": "1.5",
|
|
},
|
|
)
|
|
)
|
|
list_items.append(
|
|
html.Div(
|
|
card_children,
|
|
style={
|
|
"padding": "12px 16px",
|
|
"background": "rgba(0,0,0,0.2)",
|
|
"borderRadius": "8px",
|
|
"border": f"1px solid {CARD_BORDER}",
|
|
"borderLeft": f"3px solid {RED}",
|
|
"marginBottom": "10px",
|
|
},
|
|
)
|
|
)
|
|
elif is_checklist:
|
|
parts = b.split(" — ", 1)
|
|
label_children: list = [
|
|
html.Span(
|
|
parts[0],
|
|
style={"color": SLATE},
|
|
)
|
|
]
|
|
if len(parts) > 1:
|
|
label_children.append(
|
|
html.Span(
|
|
f" — {parts[1]}",
|
|
style={
|
|
"color": GRAY,
|
|
"fontStyle": "italic",
|
|
"fontSize": "13px",
|
|
},
|
|
)
|
|
)
|
|
list_items.append(
|
|
html.Div(
|
|
[
|
|
html.Span(
|
|
"○",
|
|
style={
|
|
"marginRight": "10px",
|
|
"color": GRAY,
|
|
"fontSize": "14px",
|
|
"flexShrink": "0",
|
|
"marginTop": "1px",
|
|
},
|
|
),
|
|
html.Span(
|
|
label_children,
|
|
style={
|
|
"fontSize": "14px",
|
|
"lineHeight": "1.6",
|
|
},
|
|
),
|
|
],
|
|
style={
|
|
"display": "flex",
|
|
"alignItems": "flex-start",
|
|
"marginBottom": "6px",
|
|
},
|
|
)
|
|
)
|
|
else:
|
|
display_text = re.sub(r"^\d+\.\s+", "", b)
|
|
list_items.append(
|
|
html.Li(
|
|
_parse_inline(display_text),
|
|
style={
|
|
"color": SLATE,
|
|
"fontSize": "14px",
|
|
"lineHeight": "1.6",
|
|
"marginBottom": "6px",
|
|
},
|
|
)
|
|
)
|
|
|
|
if table_lines:
|
|
section_blocks.append(_render_md_table(table_lines))
|
|
_flush_list_items(
|
|
section_blocks,
|
|
list_items,
|
|
is_flat=is_qa or is_checklist,
|
|
)
|
|
|
|
if "dollar impact" in section_name:
|
|
section_blocks.append(_build_savings_chart())
|
|
elif "codspeed" in section_name:
|
|
section_blocks.append(_build_comparison_table())
|
|
|
|
if is_collapsed:
|
|
blocks.append(
|
|
html.Details(
|
|
[
|
|
html.Summary(
|
|
_title_case(section_name),
|
|
id=anchor,
|
|
style={
|
|
"color": header_color,
|
|
"fontSize": "16px",
|
|
"fontWeight": "600",
|
|
"cursor": "pointer",
|
|
"margin": "16px 0 8px 0",
|
|
"outline": "none",
|
|
},
|
|
),
|
|
html.Div(
|
|
section_blocks,
|
|
style={"paddingTop": "8px"},
|
|
),
|
|
],
|
|
)
|
|
)
|
|
else:
|
|
blocks.append(
|
|
html.H4(
|
|
_title_case(section_name),
|
|
id=anchor,
|
|
style={
|
|
"color": header_color,
|
|
"margin": "16px 0 8px 0",
|
|
"fontSize": "16px",
|
|
},
|
|
)
|
|
)
|
|
blocks.extend(section_blocks)
|
|
|
|
children.append(
|
|
html.Div(
|
|
[
|
|
html.H3(
|
|
title,
|
|
style={
|
|
"color": WHITE,
|
|
"fontSize": "18px",
|
|
"marginBottom": "8px",
|
|
},
|
|
),
|
|
html.Div(blocks),
|
|
],
|
|
style={**CARD, "marginBottom": "16px"},
|
|
)
|
|
)
|
|
return html.Div(children)
|
|
|
|
|
|
def repo_breakdown_chart(prs: list[dict], title: str) -> dcc.Graph:
|
|
repo_counts: dict[str, int] = {}
|
|
for pr in prs:
|
|
repo_counts[pr["repo"]] = repo_counts.get(pr["repo"], 0) + 1
|
|
repos = list(repo_counts.keys())
|
|
counts = list(repo_counts.values())
|
|
colors = [ACCENT, BLUE, GREEN, PURPLE, AMBER]
|
|
fig = go.Figure(
|
|
go.Bar(
|
|
x=repos,
|
|
y=counts,
|
|
marker_color=colors[: len(repos)],
|
|
text=counts,
|
|
textposition="outside",
|
|
textfont={"color": WHITE, "size": 14},
|
|
)
|
|
)
|
|
fig.update_layout(
|
|
title={"text": title, "font": {"color": WHITE, "size": 16}},
|
|
paper_bgcolor="rgba(0,0,0,0)",
|
|
plot_bgcolor="rgba(0,0,0,0)",
|
|
font={"color": SLATE, "family": FONT},
|
|
xaxis={"gridcolor": CARD_BORDER, "color": GRAY},
|
|
yaxis={"gridcolor": CARD_BORDER, "color": GRAY, "dtick": 1},
|
|
margin={"l": 40, "r": 20, "t": 50, "b": 40},
|
|
height=300,
|
|
)
|
|
return dcc.Graph(figure=fig, config={"displayModeBar": False})
|
|
|
|
|
|
def before_after_chart(
|
|
labels: list[str], before: list[float], after: list[float], title: str
|
|
) -> dcc.Graph:
|
|
fig = go.Figure()
|
|
fig.add_trace(
|
|
go.Bar(
|
|
name="Before",
|
|
x=labels,
|
|
y=before,
|
|
marker_color=RED,
|
|
text=before,
|
|
textposition="outside",
|
|
textfont={"color": WHITE, "size": 12},
|
|
)
|
|
)
|
|
fig.add_trace(
|
|
go.Bar(
|
|
name="After",
|
|
x=labels,
|
|
y=after,
|
|
marker_color=GREEN,
|
|
text=after,
|
|
textposition="outside",
|
|
textfont={"color": WHITE, "size": 12},
|
|
)
|
|
)
|
|
fig.update_layout(
|
|
barmode="group",
|
|
title={"text": title, "font": {"color": WHITE, "size": 16}},
|
|
paper_bgcolor="rgba(0,0,0,0)",
|
|
plot_bgcolor="rgba(0,0,0,0)",
|
|
font={"color": SLATE, "family": FONT},
|
|
xaxis={"gridcolor": CARD_BORDER, "color": GRAY},
|
|
yaxis={"gridcolor": CARD_BORDER, "color": GRAY},
|
|
legend={"font": {"color": SLATE}},
|
|
margin={"l": 40, "r": 20, "t": 50, "b": 40},
|
|
height=320,
|
|
)
|
|
return dcc.Graph(figure=fig, config={"displayModeBar": False})
|
|
|
|
|
|
def build_goal_cards() -> html.Div:
|
|
return html.Div(
|
|
[
|
|
html.H2(
|
|
"Goal: Unstructured Case Study",
|
|
id="goal-unstructured",
|
|
style={**SECTION_H2, "color": PURPLE},
|
|
),
|
|
html.Div(
|
|
[
|
|
hero_card("CI Jobs", "301 → 33", GREEN),
|
|
hero_card("Docker Image", "-2.5 GB", GREEN),
|
|
hero_card("Infra Cost", "~90% ↓", ACCENT),
|
|
hero_card("DB Queries", "10s → 100ms", GREEN),
|
|
],
|
|
style={
|
|
**FLEX_ROW,
|
|
"marginBottom": "16px",
|
|
},
|
|
),
|
|
html.Div(
|
|
"Target: ~$1M/yr infrastructure bill",
|
|
style={
|
|
"color": GRAY,
|
|
"fontSize": "13px",
|
|
"textAlign": "center",
|
|
"marginBottom": "32px",
|
|
},
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_typeagent_chart() -> html.Div:
|
|
return html.Div(
|
|
[
|
|
html.H2(
|
|
"Strategy in Action: typeagent-py",
|
|
id="typeagent-charts",
|
|
style={**SECTION_H2, "color": PURPLE},
|
|
),
|
|
html.Div(
|
|
[
|
|
html.Div(
|
|
before_after_chart(
|
|
["Query Batching"],
|
|
[1.0],
|
|
[2.4],
|
|
"Metadata N+1 Fix (#232) — 2.1x-2.6x",
|
|
),
|
|
style={"flex": "1", **CARD},
|
|
),
|
|
html.Div(
|
|
before_after_chart(
|
|
["Indexing"],
|
|
[1.0],
|
|
[1.15],
|
|
"Batch SQLite INSERTs (#230) — 1.14x-1.16x",
|
|
),
|
|
style={"flex": "1", **CARD},
|
|
),
|
|
],
|
|
style={
|
|
**FLEX_ROW,
|
|
},
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def _repro_item(title: str, description: str, command: str) -> html.Div:
|
|
return html.Div(
|
|
[
|
|
html.Div(
|
|
title,
|
|
style={
|
|
"color": ACCENT,
|
|
"fontSize": "14px",
|
|
"fontWeight": "600",
|
|
"marginBottom": "4px",
|
|
},
|
|
),
|
|
html.Div(
|
|
description,
|
|
style={
|
|
"color": SLATE,
|
|
"fontSize": "13px",
|
|
"marginBottom": "4px",
|
|
},
|
|
),
|
|
html.Pre(
|
|
f"$ {command}",
|
|
style={
|
|
"color": GREEN,
|
|
"fontSize": "12px",
|
|
"fontFamily": MONO,
|
|
"padding": "8px 12px",
|
|
"background": "rgba(0,0,0,0.3)",
|
|
"borderRadius": "6px",
|
|
"margin": "0",
|
|
"overflowX": "auto",
|
|
},
|
|
),
|
|
],
|
|
style={
|
|
"marginBottom": "16px",
|
|
"paddingBottom": "16px",
|
|
"borderBottom": f"1px solid {CARD_BORDER}",
|
|
},
|
|
)
|
|
|
|
|
|
def build_ci_audit_tab(audit: dict) -> html.Div:
|
|
billing = audit.get("billing", {})
|
|
ops = audit.get("operational_before_after", {})
|
|
run_vol = audit.get("run_volume", {})
|
|
|
|
hero_row = html.Div(
|
|
[
|
|
hero_card(
|
|
"Workflows",
|
|
f"{ops.get('workflow_files', [0, 0])[0]} → {ops.get('workflow_files', [0, 0])[1]}",
|
|
GREEN,
|
|
),
|
|
hero_card(
|
|
"Required Checks",
|
|
f"{ops.get('required_checks', [0, 0])[0]} → {ops.get('required_checks', [0, 0])[1]}",
|
|
GREEN,
|
|
),
|
|
hero_card(
|
|
"Runs Eliminated/mo",
|
|
f"{run_vol.get('total_eliminated_monthly', 0):,}",
|
|
ACCENT,
|
|
),
|
|
hero_card(
|
|
"Annual Savings",
|
|
f"${billing.get('overage_saved_annual_usd', 0):,}",
|
|
GREEN,
|
|
),
|
|
],
|
|
style={
|
|
**FLEX_ROW,
|
|
},
|
|
)
|
|
|
|
charts_row = html.Div(
|
|
id="ci-charts",
|
|
children=[
|
|
html.Div(
|
|
before_after_chart(
|
|
["Workflow Files", "Required Checks"],
|
|
[
|
|
ops.get("workflow_files", [0])[0],
|
|
ops.get("required_checks", [0])[0],
|
|
],
|
|
[
|
|
ops.get("workflow_files", [0, 0])[1],
|
|
ops.get("required_checks", [0, 0])[1],
|
|
],
|
|
"Operational Simplification",
|
|
),
|
|
style={"flex": "1", **CARD},
|
|
),
|
|
html.Div(
|
|
before_after_chart(
|
|
["Monthly Run Volume"],
|
|
[run_vol.get("codeflash_feb", 0)],
|
|
[run_vol.get("codeflash_apr_projected", 0)],
|
|
f"codeflash Runs ({run_vol.get('codeflash_reduction_pct', 0)}% reduction)",
|
|
),
|
|
style={"flex": "1", **CARD},
|
|
),
|
|
],
|
|
style={
|
|
**FLEX_ROW,
|
|
},
|
|
)
|
|
|
|
findings_rows = []
|
|
for repo, items in audit.get("findings", {}).items():
|
|
for f in items:
|
|
findings_rows.append(
|
|
{
|
|
"Repo": repo,
|
|
"Finding": f["finding"],
|
|
"Impact": f["impact"],
|
|
"Fix": f["fix"],
|
|
"PR": f.get("pr") or "—",
|
|
}
|
|
)
|
|
|
|
findings_table = html.Div(
|
|
[
|
|
html.H2(
|
|
"Findings",
|
|
id="ci-findings",
|
|
style=SECTION_H2,
|
|
),
|
|
html.Div(
|
|
dash_table.DataTable(
|
|
id="findings-table",
|
|
columns=[
|
|
{"name": c, "id": c}
|
|
for c in ["Repo", "Finding", "Impact", "Fix", "PR"]
|
|
],
|
|
data=findings_rows,
|
|
style_header=TABLE_HEADER,
|
|
style_cell={
|
|
**TABLE_CELL,
|
|
"whiteSpace": "normal",
|
|
"maxWidth": "300px",
|
|
},
|
|
style_data=TABLE_DATA,
|
|
style_data_conditional=TABLE_DATA_CONDITIONAL,
|
|
page_size=20,
|
|
style_table={"overflowX": "auto"},
|
|
),
|
|
style=TABLE_WRAP,
|
|
),
|
|
],
|
|
style={"marginBottom": "32px"},
|
|
)
|
|
|
|
audit_prs = audit.get("prs_merged", [])
|
|
pr_rows = [
|
|
{
|
|
"PR": p["pr"],
|
|
"Repo": p["repo"],
|
|
"Date": p["date"],
|
|
"Title": p["title"],
|
|
}
|
|
for p in audit_prs
|
|
]
|
|
prs_table = html.Div(
|
|
[
|
|
html.H2(
|
|
"Audit PRs Merged",
|
|
id="ci-prs-merged",
|
|
style=SECTION_H2,
|
|
),
|
|
html.Div(
|
|
dash_table.DataTable(
|
|
id="audit-prs-table",
|
|
columns=[
|
|
{"name": c, "id": c}
|
|
for c in ["PR", "Repo", "Date", "Title"]
|
|
],
|
|
data=pr_rows,
|
|
style_header=TABLE_HEADER,
|
|
style_cell=TABLE_CELL,
|
|
style_data=TABLE_DATA,
|
|
style_data_conditional=TABLE_DATA_CONDITIONAL,
|
|
page_size=15,
|
|
style_table={"overflowX": "auto"},
|
|
),
|
|
style=TABLE_WRAP,
|
|
),
|
|
],
|
|
style={"marginBottom": "32px"},
|
|
)
|
|
|
|
billing_row = html.Div(
|
|
id="ci-billing",
|
|
children=[
|
|
hero_card(
|
|
"Overage Before (min/mo)",
|
|
f"{billing.get('overage_before_min', 0):,}",
|
|
RED,
|
|
),
|
|
hero_card(
|
|
"Overage After (min/mo)",
|
|
f"{billing.get('overage_after_min', 0):,}",
|
|
GREEN,
|
|
),
|
|
hero_card(
|
|
"Saved (min/mo)",
|
|
f"{billing.get('overage_saved_min', 0):,}",
|
|
ACCENT,
|
|
),
|
|
hero_card(
|
|
"Saved ($/mo)",
|
|
f"${billing.get('overage_saved_monthly_usd', 0):,}",
|
|
GREEN,
|
|
),
|
|
],
|
|
style={
|
|
**FLEX_ROW,
|
|
},
|
|
)
|
|
|
|
direct_actions = audit.get("direct_actions", [])
|
|
action_rows = []
|
|
for a in direct_actions:
|
|
if isinstance(a, dict):
|
|
action_rows.append(
|
|
{
|
|
"Action": a.get("action", ""),
|
|
"Repo": a.get("repo", ""),
|
|
"Date": a.get("date", ""),
|
|
}
|
|
)
|
|
actions_table = html.Div(
|
|
[
|
|
html.H2(
|
|
"Direct Actions (no PR)",
|
|
id="ci-actions",
|
|
style=SECTION_H2,
|
|
),
|
|
html.Div(
|
|
dash_table.DataTable(
|
|
id="actions-table",
|
|
columns=[
|
|
{"name": c, "id": c}
|
|
for c in ["Action", "Repo", "Date"]
|
|
],
|
|
data=action_rows,
|
|
style_header=TABLE_HEADER,
|
|
style_cell={
|
|
**TABLE_CELL,
|
|
"whiteSpace": "normal",
|
|
"maxWidth": "400px",
|
|
},
|
|
style_data=TABLE_DATA,
|
|
style_data_conditional=TABLE_DATA_CONDITIONAL,
|
|
style_table={"overflowX": "auto"},
|
|
),
|
|
style=TABLE_WRAP,
|
|
),
|
|
],
|
|
style={"marginBottom": "32px"},
|
|
)
|
|
|
|
how_to = html.Div(
|
|
[
|
|
html.H2(
|
|
"How to Reproduce",
|
|
id="ci-reproduce",
|
|
style=SECTION_H2,
|
|
),
|
|
html.Div(
|
|
[
|
|
_repro_item(
|
|
"Wildcard path triggers (PR #2025)",
|
|
"Added targeted paths: filters so E2E only runs on relevant code changes.",
|
|
"gh pr view 2025 -R codeflash-ai/codeflash --json files",
|
|
),
|
|
_repro_item(
|
|
"Broken claude-code-action (PRs #2026, #2587)",
|
|
"Bedrock OIDC regression in v1.0.90 — pinned to v1.0.89 on both repos.",
|
|
"grep claude-code-action .github/workflows/*.yml",
|
|
),
|
|
_repro_item(
|
|
"Dependabot scanning fixtures (PR #2027)",
|
|
"70% of Dependabot runs failing on code_to_optimize/ test fixtures.",
|
|
"cat .github/dependabot.yml",
|
|
),
|
|
_repro_item(
|
|
"13 ghost workflows (API action)",
|
|
"Deleted source files but workflows still registered in Actions.",
|
|
"gh workflow list -R codeflash-ai/codeflash --all | grep disabled",
|
|
),
|
|
_repro_item(
|
|
"17 workflows → single ci.yaml (PR #2044)",
|
|
"Consolidated into one file with gate job. Required checks: 13 → 1.",
|
|
"ls .github/workflows/",
|
|
),
|
|
_repro_item(
|
|
"No npm/Maven caching (PR #2050)",
|
|
"JS and Java CI ran without dependency caching, plus duplicate workflows.",
|
|
"grep -A2 actions/cache .github/workflows/ci.yaml",
|
|
),
|
|
_repro_item(
|
|
"Outdated actions, broken Windows (PR #2052)",
|
|
"Old action versions causing Windows CI failures.",
|
|
"grep 'uses:' .github/workflows/ci.yaml",
|
|
),
|
|
_repro_item(
|
|
"Stale codeflash.yaml (PR #2056)",
|
|
"Superseded by codeflash-optimize.yaml, still running.",
|
|
"# Verify codeflash.yaml no longer exists",
|
|
),
|
|
_repro_item(
|
|
"Internal deploy + Claude Code + PyPI (PRs #2587, #2588)",
|
|
"Deploy path included .github/workflows/**, Claude Code had no path filters, PyPI permanently disabled but still running.",
|
|
"gh pr view 2588 -R codeflash-ai/codeflash-internal --json files",
|
|
),
|
|
_repro_item(
|
|
"Disable Actions on 200+ forks (API action)",
|
|
"Forks had Actions enabled, burning 960 failing runs/month.",
|
|
'gh repo list codeflash-ai --fork --limit 300 --json name | jq -r ".[].name"',
|
|
),
|
|
],
|
|
),
|
|
],
|
|
style={"marginBottom": "32px"},
|
|
)
|
|
|
|
savings_breakdown = html.Div(
|
|
[
|
|
html.H2(
|
|
"Savings Breakdown",
|
|
id="ci-savings",
|
|
style=SECTION_H2,
|
|
),
|
|
html.Div(
|
|
[
|
|
html.Div(
|
|
[
|
|
html.Div(
|
|
"Enterprise included: 50,000 min/mo",
|
|
style={"color": SLATE, "fontSize": "14px"},
|
|
),
|
|
html.Div(
|
|
f"Before: {billing.get('overage_before_min', 0):,} min/mo",
|
|
style={"color": RED, "fontSize": "14px"},
|
|
),
|
|
html.Div(
|
|
f"After: {billing.get('overage_after_min', 0):,} min/mo",
|
|
style={"color": GREEN, "fontSize": "14px"},
|
|
),
|
|
html.Div(
|
|
f"Saved: {billing.get('overage_saved_min', 0):,} min/mo",
|
|
style={
|
|
"color": ACCENT,
|
|
"fontSize": "14px",
|
|
"fontWeight": "700",
|
|
},
|
|
),
|
|
html.Div(
|
|
"Rate: ~$0.008/min overage (Linux runners)",
|
|
style={
|
|
"color": GRAY,
|
|
"fontSize": "13px",
|
|
"marginTop": "8px",
|
|
},
|
|
),
|
|
html.Div(
|
|
f"{billing.get('overage_saved_min', 0):,} min x $0.008 = ${billing.get('overage_saved_monthly_usd', 0):,}/mo x 12 = ${billing.get('overage_saved_annual_usd', 0):,}/yr",
|
|
style={
|
|
"color": GREEN,
|
|
"fontSize": "14px",
|
|
"fontWeight": "700",
|
|
"fontFamily": MONO,
|
|
"marginTop": "4px",
|
|
},
|
|
),
|
|
],
|
|
style={**CARD},
|
|
),
|
|
],
|
|
),
|
|
],
|
|
style={"marginBottom": "32px"},
|
|
)
|
|
|
|
return html.Div(
|
|
[
|
|
html.Div(
|
|
f"Audit: {audit.get('audit_date', '?')} — Completed: {audit.get('completion_date', '?')}",
|
|
style={
|
|
"color": GRAY,
|
|
"fontSize": "13px",
|
|
"marginBottom": "20px",
|
|
},
|
|
),
|
|
hero_row,
|
|
charts_row,
|
|
billing_row,
|
|
savings_breakdown,
|
|
findings_table,
|
|
actions_table,
|
|
how_to,
|
|
prs_table,
|
|
]
|
|
)
|
|
|
|
|
|
def build_today_tab(data: dict) -> html.Div:
|
|
summary = data["summary"]
|
|
open_prs = data["open_prs"]
|
|
merged_prs = data["merged_prs"]
|
|
notes = data["notes"]
|
|
return html.Div(
|
|
[
|
|
html.Div(
|
|
[
|
|
hero_card("Open PRs", summary["total_open"], BLUE),
|
|
hero_card(
|
|
"Merged (7d)", summary["total_merged_7d"], GREEN
|
|
),
|
|
hero_card("Drafts", summary["draft_count"], AMBER),
|
|
hero_card(
|
|
"Active Repos", summary["repos_with_open_prs"], PURPLE
|
|
),
|
|
],
|
|
style={
|
|
**FLEX_ROW,
|
|
},
|
|
),
|
|
html.H2(
|
|
"Meeting Notes",
|
|
id="meeting-notes",
|
|
style=SECTION_H2,
|
|
),
|
|
notes_section(notes),
|
|
build_goal_cards(),
|
|
build_typeagent_chart(),
|
|
html.Div(
|
|
[
|
|
html.Div(
|
|
[
|
|
html.H2(
|
|
"Open PRs",
|
|
id="open-prs",
|
|
style=SECTION_H2,
|
|
),
|
|
pr_table(open_prs, "open-pr-table"),
|
|
],
|
|
style={"flex": "1", "minWidth": "0"},
|
|
),
|
|
html.Div(
|
|
[
|
|
html.H2(
|
|
"Merged (Last 7 Days)",
|
|
id="merged-prs",
|
|
style=SECTION_H2,
|
|
),
|
|
pr_table(merged_prs, "merged-pr-table"),
|
|
],
|
|
style={"flex": "1", "minWidth": "0"},
|
|
),
|
|
],
|
|
style={
|
|
**FLEX_ROW,
|
|
"gap": "24px",
|
|
},
|
|
),
|
|
html.Div(
|
|
[
|
|
html.Div(
|
|
repo_breakdown_chart(open_prs, "Open PRs by Repo"),
|
|
style={"flex": "1", **CARD},
|
|
),
|
|
html.Div(
|
|
repo_breakdown_chart(
|
|
merged_prs, "Merged PRs by Repo (7d)"
|
|
),
|
|
style={"flex": "1", **CARD},
|
|
),
|
|
],
|
|
style={
|
|
**FLEX_ROW,
|
|
},
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def _read_data() -> dict:
|
|
if DATA_FILE.exists():
|
|
return json.loads(DATA_FILE.read_text(encoding="utf-8"))
|
|
return load_data()
|
|
|
|
|
|
def serve_layout():
|
|
data = _read_data()
|
|
return html.Div(
|
|
[
|
|
html.Div(style=GRID_OVERLAY),
|
|
html.Div(
|
|
[
|
|
html.H1(
|
|
"Codeflash Weekly Eng Meeting",
|
|
style={
|
|
"color": WHITE,
|
|
"fontSize": "28px",
|
|
"fontWeight": "700",
|
|
"marginBottom": "4px",
|
|
},
|
|
),
|
|
html.Div(
|
|
f"Generated {data['generated_at'][:10]}",
|
|
style={
|
|
"color": GRAY,
|
|
"fontSize": "13px",
|
|
"marginBottom": "12px",
|
|
},
|
|
),
|
|
html.Div(
|
|
"Weekly eng meeting format — click any section header to expand/collapse. This is a living document that improves every week.",
|
|
style={
|
|
"color": WHITE,
|
|
"fontSize": "18px",
|
|
"fontWeight": "500",
|
|
"lineHeight": "1.7",
|
|
"marginBottom": "28px",
|
|
"padding": "20px 24px",
|
|
"background": "rgba(255,210,39,0.08)",
|
|
"borderLeft": f"3px solid {ACCENT}",
|
|
"borderRadius": "8px",
|
|
},
|
|
),
|
|
dcc.Tabs(
|
|
id="tabs",
|
|
value="today",
|
|
children=[
|
|
dcc.Tab(
|
|
label="This Week",
|
|
value="today",
|
|
style=TAB_STYLE,
|
|
selected_style=TAB_SELECTED,
|
|
),
|
|
dcc.Tab(
|
|
label="CI Audit",
|
|
value="ci_audit",
|
|
style=TAB_STYLE,
|
|
selected_style=TAB_SELECTED,
|
|
),
|
|
],
|
|
style={
|
|
"marginBottom": "24px",
|
|
"borderBottom": f"1px solid {CARD_BORDER}",
|
|
},
|
|
),
|
|
dcc.Location(id="url", refresh=False),
|
|
dcc.Store(id="data-store", data=data),
|
|
dcc.Interval(
|
|
id="refresh-interval",
|
|
interval=30_000,
|
|
n_intervals=0,
|
|
),
|
|
html.Div(id="tab-content"),
|
|
],
|
|
style={
|
|
"maxWidth": "1200px",
|
|
"margin": "0 auto",
|
|
"padding": "40px 24px",
|
|
"position": "relative",
|
|
"zIndex": "1",
|
|
},
|
|
),
|
|
],
|
|
style={
|
|
"backgroundColor": BG,
|
|
"minHeight": "100vh",
|
|
"fontFamily": FONT,
|
|
"color": SLATE,
|
|
},
|
|
)
|
|
|
|
|
|
app.layout = serve_layout
|
|
|
|
CI_AUDIT_ANCHORS_JS = json.dumps(sorted(CI_AUDIT_ANCHORS))
|
|
|
|
app.clientside_callback(
|
|
f"""
|
|
function(hash) {{
|
|
if (!hash) return window.dash_clientside.no_update;
|
|
var anchor = hash.replace('#', '');
|
|
if (!anchor) return window.dash_clientside.no_update;
|
|
var ciAnchors = {CI_AUDIT_ANCHORS_JS};
|
|
var tab = ciAnchors.indexOf(anchor) >= 0 ? 'ci_audit' : 'today';
|
|
setTimeout(function() {{
|
|
var el = document.getElementById(anchor);
|
|
if (el) el.scrollIntoView({{behavior: 'smooth', block: 'start'}});
|
|
}}, 300);
|
|
return tab;
|
|
}}
|
|
""",
|
|
Output("tabs", "value"),
|
|
Input("url", "hash"),
|
|
prevent_initial_call=False,
|
|
)
|
|
|
|
|
|
@app.callback(
|
|
Output("data-store", "data"),
|
|
Input("refresh-interval", "n_intervals"),
|
|
prevent_initial_call=True,
|
|
)
|
|
def refresh_data(_n: int):
|
|
from dash.exceptions import PreventUpdate
|
|
|
|
if not _notes_changed():
|
|
raise PreventUpdate
|
|
return load_data()
|
|
|
|
|
|
@app.callback(
|
|
Output("tab-content", "children"),
|
|
Input("tabs", "value"),
|
|
Input("data-store", "data"),
|
|
)
|
|
def render_tab(tab: str, data):
|
|
if not isinstance(data, dict):
|
|
data = _read_data()
|
|
if tab == "ci_audit":
|
|
ci_audit = data.get("ci_audit")
|
|
return (
|
|
build_ci_audit_tab(ci_audit)
|
|
if ci_audit
|
|
else html.Div(
|
|
"No CI audit data found.",
|
|
style={"color": GRAY, "padding": "16px"},
|
|
)
|
|
)
|
|
return build_today_tab(data)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(port=8052, debug=True)
|