| from __future__ import annotations |
|
|
| |
| _BG = "#161616" |
| _L1 = "#262626" |
| _L2 = "#393939" |
| _L3 = "#525252" |
| _T1 = "#f4f4f4" |
| _T2 = "#c6c6c6" |
| _T3 = "#8d8d8d" |
| _T4 = "#6f6f6f" |
| _BLUE = "#4589ff" |
| _BLUE_DIM = "#001d6c" |
| _BLUE_LNK = "#78a9ff" |
|
|
| |
| _ERR_BG = "#2d0709"; _ERR_BD = "#ff8389"; _ERR_FG = "#ff8389" |
| _WARN_BG = "#302200"; _WARN_BD = "#f1c21b"; _WARN_FG = "#f1c21b" |
| _OK_BG = "#071908"; _OK_BD = "#42be65"; _OK_FG = "#42be65" |
| _INFO_BG = _BLUE_DIM; _INFO_BD = _BLUE; _INFO_FG = _BLUE |
|
|
| _FONT = "'IBM Plex Sans', system-ui, -apple-system, sans-serif" |
| _MONO = "'IBM Plex Mono', 'SFMono-Regular', Consolas, monospace" |
|
|
| |
| _SVG_ERROR = ( |
| "<svg width='20' height='20' viewBox='0 0 32 32' fill='currentColor'>" |
| "<path d='M16 2C8.2 2 2 8.2 2 16s6.2 14 14 14 14-6.2 14-14S23.8 2 16 2z" |
| "m5.4 21L16 17.6 10.6 23 9 21.4l5.4-5.4L9 10.6 10.6 9 16 14.4 21.4 9 23 10.6" |
| "l-5.4 5.4 5.4 5.4L21.4 23z'/></svg>" |
| ) |
| _SVG_WARNING = ( |
| "<svg width='20' height='20' viewBox='0 0 32 32' fill='currentColor'>" |
| "<path d='M16 2L2 30h28L16 2zm-1.33 10.67h2.67V20h-2.67z" |
| "M16 26a1.33 1.33 0 110-2.67A1.33 1.33 0 0116 26z'/></svg>" |
| ) |
| _SVG_CHECK = ( |
| "<svg width='20' height='20' viewBox='0 0 32 32' fill='currentColor'>" |
| "<path d='M16 2C8.2 2 2 8.2 2 16s6.2 14 14 14 14-6.2 14-14S23.8 2 16 2z" |
| "m-3 20.6l-5.6-5.6 1.4-1.4 4.2 4.2 9.2-9.2 1.4 1.4L13 22.6z'/></svg>" |
| ) |
| _SVG_INFO = ( |
| "<svg width='20' height='20' viewBox='0 0 32 32' fill='currentColor'>" |
| "<path d='M17 22v-8h-4v2h2v6h-2v2h6v-2zM15 8a1.5 1.5 0 100 3 1.5 1.5 0 000-3z" |
| "M16 2C8.2 2 2 8.2 2 16s6.2 14 14 14 14-6.2 14-14S23.8 2 16 2z'/></svg>" |
| ) |
|
|
| _NOTIFICATION: dict[str, dict] = { |
| "approved": {"border": _OK_BD, "bg": _OK_BG, "color": _OK_FG, "icon": _SVG_CHECK, "label": "APPROVED"}, |
| "needs_human_review": {"border": _WARN_BD, "bg": _WARN_BG, "color": _WARN_FG, "icon": _SVG_WARNING, "label": "NEEDS HUMAN REVIEW"}, |
| "rejected": {"border": _ERR_BD, "bg": _ERR_BG, "color": _ERR_FG, "icon": _SVG_ERROR, "label": "REJECTED"}, |
| "refused": {"border": _ERR_BD, "bg": _ERR_BG, "color": _ERR_FG, "icon": _SVG_ERROR, "label": "REFUSED"}, |
| } |
| _DEFAULT_NOTIFICATION = {"border": _L3, "bg": _L1, "color": _T3, "icon": _SVG_INFO, "label": "UNKNOWN"} |
|
|
|
|
| def _notification_html(action: str, reason: str) -> str: |
| cfg = _NOTIFICATION.get(action, _DEFAULT_NOTIFICATION) |
| return ( |
| f"<div style='display:flex;align-items:flex-start;gap:12px;" |
| f"background:{cfg['bg']};border-left:4px solid {cfg['border']};" |
| f"padding:14px 16px;font-family:{_FONT}'>" |
| f"<span style='color:{cfg['color']};flex-shrink:0;margin-top:1px'>{cfg['icon']}</span>" |
| f"<div>" |
| f"<p style='font-size:12px;font-weight:600;letter-spacing:0.32px;text-transform:uppercase;" |
| f"color:{cfg['color']};margin:0 0 4px'>{cfg['label']}</p>" |
| f"<p style='font-size:14px;color:{_T2};margin:0;line-height:1.5'>{reason}</p>" |
| f"</div></div>" |
| ) |
|
|
|
|
| def _severity_pill(sev: str) -> str: |
| dark_colors = { |
| "CRITICAL": (_ERR_FG, _ERR_BG), |
| "HIGH": ("#ffb3b8", "#3b0d0e"), |
| "MEDIUM": (_WARN_FG, _WARN_BG), |
| "LOW": (_BLUE_LNK, "#001141"), |
| } |
| fg, bg = dark_colors.get(sev, (_T3, _L2)) |
| return ( |
| f"<span style='font-family:{_MONO};font-size:11px;font-weight:500;" |
| f"background:{bg};color:{fg};padding:1px 6px;letter-spacing:0.16px'>{sev}</span>" |
| ) |
|
|
|
|
| def render_findings_html(rule_ids: list[str], summary: dict) -> str: |
| failed = summary.get("failed", len(rule_ids)) |
| crit = int(summary.get("CRITICAL", 0)) |
| high = int(summary.get("HIGH", 0)) |
| med = int(summary.get("MEDIUM", 0)) |
|
|
| rows = "".join( |
| f"<tr><td style='padding:6px 0;border-bottom:1px solid {_L2};" |
| f"font-family:{_MONO};font-size:12px;color:{_T2}'>{rid}</td></tr>" |
| for rid in rule_ids |
| ) |
| table = ( |
| f"<table style='width:100%;border-collapse:collapse'>" |
| f"<thead><tr><th style='text-align:left;padding:0 0 8px;" |
| f"font-family:{_FONT};font-size:11px;font-weight:600;letter-spacing:0.32px;" |
| f"text-transform:uppercase;color:{_T3};border-bottom:1px solid {_L3}'>" |
| f"Rule ID</th></tr></thead><tbody>{rows}</tbody></table>" |
| ) if rule_ids else f"<p style='color:{_T4};font-size:13px;margin:8px 0'>No findings</p>" |
|
|
| pills = " ".join(filter(None, [ |
| (_severity_pill("CRITICAL") + f" <b style='font-size:13px;color:{_T1}'>{crit}</b>") if crit else "", |
| (_severity_pill("HIGH") + f" <b style='font-size:13px;color:{_T1}'>{high}</b>") if high else "", |
| (_severity_pill("MEDIUM") + f" <b style='font-size:13px;color:{_T1}'>{med}</b>") if med else "", |
| ])) |
|
|
| return ( |
| f"<div style='font-family:{_FONT};background:{_L1};padding:16px'>" |
| f"<div style='display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px'>" |
| f"<span style='font-size:28px;font-weight:300;color:{_T1}'>{failed}</span>" |
| f"<span style='font-size:11px;letter-spacing:0.32px;text-transform:uppercase;color:{_T3}'>failed checks</span>" |
| f"</div>" |
| f"<div style='display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap'>{pills}</div>" |
| f"{table}</div>" |
| ) |
|
|
|
|
| def _details_html( |
| parse_ok: bool, |
| changed_ratio: float, |
| resolved: list[str], |
| unresolved: list[str], |
| explanation: str, |
| risk_notes: list[str], |
| ) -> str: |
| def _row(label: str, value: str) -> str: |
| return ( |
| f"<tr>" |
| f"<td style='width:160px;padding:8px 0;border-bottom:1px solid {_L2};" |
| f"font-size:11px;font-weight:600;letter-spacing:0.32px;text-transform:uppercase;" |
| f"color:{_T3};vertical-align:top;font-family:{_FONT}'>{label}</td>" |
| f"<td style='padding:8px 0 8px 16px;border-bottom:1px solid {_L2};" |
| f"font-size:13px;color:{_T2};font-family:{_MONO}'>{value}</td>" |
| f"</tr>" |
| ) |
|
|
| def _pill_list(items: list[str], ok: bool) -> str: |
| if not items: |
| return f"<span style='color:{_T4}'>—</span>" |
| fg = _OK_FG if ok else _ERR_FG |
| bg = _OK_BG if ok else _ERR_BG |
| pills = "".join( |
| f"<span style='display:inline-block;margin:2px 4px 2px 0;" |
| f"background:{bg};color:{fg};padding:2px 8px;font-size:11px;font-weight:500'>{r}</span>" |
| for r in items |
| ) |
| return f"<div style='display:flex;flex-wrap:wrap;gap:2px'>{pills}</div>" |
|
|
| risks_html = "" |
| if risk_notes: |
| items = "".join(f"<li style='margin-bottom:4px;color:{_T2}'>{n}</li>" for n in risk_notes) |
| risks_html = ( |
| f"<div style='margin-top:16px;padding:12px;" |
| f"background:{_WARN_BG};border-left:3px solid {_WARN_BD}'>" |
| f"<p style='font-size:11px;font-weight:600;letter-spacing:0.32px;" |
| f"text-transform:uppercase;color:{_WARN_FG};margin:0 0 8px;font-family:{_FONT}'>Risk Notes</p>" |
| f"<ul style='margin:0;padding-left:16px;font-size:13px'>{items}</ul>" |
| f"</div>" |
| ) |
|
|
| expl_html = "" |
| if explanation: |
| expl_html = ( |
| f"<div style='margin-top:16px;padding:12px 16px;" |
| f"background:{_INFO_BG};border-left:3px solid {_INFO_BD}'>" |
| f"<p style='font-size:11px;font-weight:600;letter-spacing:0.32px;" |
| f"text-transform:uppercase;color:{_INFO_FG};margin:0 0 6px;font-family:{_FONT}'>Explanation</p>" |
| f"<p style='font-size:13px;color:{_T2};margin:0;line-height:1.6;font-family:{_FONT}'>{explanation}</p>" |
| f"</div>" |
| ) |
|
|
| table = ( |
| f"<table style='width:100%;border-collapse:collapse;font-family:{_FONT}'>" |
| f"{_row('Parse OK', 'Yes' if parse_ok else 'No')}" |
| f"{_row('Changed lines', f'{changed_ratio:.0%}')}" |
| f"{_row('Resolved', _pill_list(resolved, ok=True))}" |
| f"{_row('Unresolved', _pill_list(unresolved, ok=False))}" |
| f"</table>" |
| ) |
|
|
| return ( |
| f"<div style='background:{_L1};padding:16px;font-family:{_FONT}'>" |
| f"{table}{expl_html}{risks_html}" |
| f"</div>" |
| ) |
|
|
|
|
| def render_result(data: dict) -> tuple[str, str, str, str, str, str]: |
| decision = data.get("decision") or {} |
| action = decision.get("action", "unknown") |
| reason = decision.get("reason", "") |
| original = data.get("original_file", "") |
| patch_obj = decision.get("patch") or {} |
| fixed = patch_obj.get("fixed_file") or data.get("fixed_file", "") |
|
|
| pre_rules: list[str] = data.get("pre_rule_ids") or [] |
| post_rules: list[str] = data.get("post_rule_ids") or [] |
| pre_summary: dict = data.get("pre_summary") or {} |
| post_summary:dict = data.get("post_summary") or {} |
|
|
| validation = decision.get("validation") or data.get("validation") or {} |
| resolved: list[str] = validation.get("targeted_resolved") or [] |
| unresolved: list[str] = validation.get("targeted_unresolved") or [] |
| parse_ok: bool = validation.get("parse_ok", False) |
| changed_ratio: float = validation.get("changed_lines_ratio", 0.0) |
| explanation = patch_obj.get("explanation") or reason |
| risk_notes: list[str] = patch_obj.get("risk_notes") or [] |
|
|
| return ( |
| original, |
| fixed, |
| _notification_html(action, reason), |
| render_findings_html(pre_rules, pre_summary), |
| render_findings_html(post_rules, post_summary), |
| _details_html(parse_ok, changed_ratio, resolved, unresolved, explanation, risk_notes), |
| ) |
|
|
|
|
| def error_html(label: str, exc: Exception) -> str: |
| return _notification_html("rejected", f"<b>{label}:</b> {exc}") |
|
|