orgstate / delivery /decisions /render.py
Legal-i's picture
Initial OrgState deploy via Stage 150 free-tier stack
d2d1903 verified
"""
delivery.decisions.render β€” render a DecisionQueue to Markdown / HTML.
Triage-focused: the table foregrounds urgency / status / owner so an
operations owner can scan the queue and decide what to pick up next.
"""
from __future__ import annotations
from ..evidence.render import _esc, _fmt_pct, _fmt_score
from .queue import STATUS_ORDER, DecisionQueue, DecisionQueueItem
def _status_summary_line(queue: DecisionQueue) -> str:
parts = [f"{queue.status_counts.get(s, 0)} {s}" for s in STATUS_ORDER]
return ", ".join(parts)
# --- markdown -------------------------------------------------------------
def _filter_phrase(queue: DecisionQueue) -> str:
if queue.status_filter is None:
return "all statuses"
return f"status = {queue.status_filter}"
def render_markdown(queue: DecisionQueue) -> str:
lines = [
f"# Decision Queue β€” `{queue.tenant_id}`",
"",
f"- **Filter:** {_filter_phrase(queue)}",
f"- **Counts:** {_status_summary_line(queue)}",
f"- **Returned:** {len(queue.items)}",
"",
]
if not queue.items:
lines += [
"No decisions match the current filter.",
"",
]
return "\n".join(lines).rstrip() + "\n"
lines += [
"| Urgency | Status | Entity | Recommendation | Owner | Confidence | Run | Updated |",
"|---:|---|---|---|---|---:|---|---|",
]
for it in queue.items:
lines.append(
f"| {_fmt_score(it.urgency)} "
f"| {it.status} "
f"| `{it.entity_id}` ({it.entity_type}) "
f"| {it.recommendation} "
f"| {it.owner or 'β€”'} "
f"| {_fmt_pct(it.confidence)} "
f"| `{it.run_id}` "
f"| {it.updated_at or 'β€”'} |"
)
lines.append("")
return "\n".join(lines).rstrip() + "\n"
# --- html -----------------------------------------------------------------
_HTML_HEAD = (
"<!doctype html><html><head><meta charset=\"utf-8\">"
"<title>Decision Queue</title>"
"<style>"
"body{font-family:-apple-system,Segoe UI,Arial,sans-serif;"
"max-width:1100px;margin:32px auto;padding:0 16px;line-height:1.5;color:#222}"
"h1{font-size:1.6em;margin-bottom:0.2em}"
"table{border-collapse:collapse;width:100%;margin:0.6em 0}"
"th,td{border:1px solid #ddd;padding:6px 8px;text-align:left;font-size:0.94em}"
"th{background:#f4f4f4}"
".status{display:inline-block;padding:1px 8px;border-radius:3px;"
"font-size:0.85em;font-weight:600;color:#fff}"
".status-open{background:#a00}"
".status-in_progress{background:#d35400}"
".status-resolved{background:#2c8a3e}"
".status-dismissed{background:#777}"
".meta{color:#555;font-size:0.93em}"
".counts{margin-bottom:1em}"
".counts span{margin-right:12px}"
"code{background:#f4f4f4;padding:1px 4px;border-radius:3px}"
"</style></head><body>"
)
_HTML_FOOT = "</body></html>"
def _status_badge(status: str) -> str:
cls = status if status in STATUS_ORDER else "open"
return f'<span class="status status-{_esc(cls)}">{_esc(status)}</span>'
def _counts_html(queue: DecisionQueue) -> str:
spans = [
f"<span><strong>{queue.status_counts.get(s, 0)}</strong> {_esc(s)}</span>"
for s in STATUS_ORDER
]
return '<p class="counts">' + "".join(spans) + "</p>"
def _row_html(it: DecisionQueueItem) -> str:
return (
"<tr>"
f"<td>{_esc(_fmt_score(it.urgency))}</td>"
f"<td>{_status_badge(it.status)}</td>"
f"<td><code>{_esc(it.entity_id)}</code> "
f'<span class="meta">({_esc(it.entity_type)})</span></td>'
f"<td>{_esc(it.recommendation)}</td>"
f"<td>{_esc(it.owner or 'β€”')}</td>"
f"<td>{_esc(_fmt_pct(it.confidence))}</td>"
f"<td><code>{_esc(it.run_id)}</code></td>"
f"<td>{_esc(it.updated_at or 'β€”')}</td>"
"</tr>"
)
def render_html(queue: DecisionQueue) -> str:
parts = [
_HTML_HEAD,
f"<h1>Decision Queue &mdash; <code>{_esc(queue.tenant_id)}</code></h1>",
'<p class="meta">'
f"Filter: {_esc(_filter_phrase(queue))} &middot; "
f"returned {len(queue.items)}"
"</p>",
_counts_html(queue),
]
if not queue.items:
parts.append("<p>No decisions match the current filter.</p>")
else:
parts.append(
"<table><thead><tr>"
"<th>Urgency</th><th>Status</th><th>Entity</th>"
"<th>Recommendation</th><th>Owner</th><th>Confidence</th>"
"<th>Run</th><th>Updated</th>"
"</tr></thead><tbody>"
)
for it in queue.items:
parts.append(_row_html(it))
parts.append("</tbody></table>")
parts.append(_HTML_FOOT)
return "".join(parts)