Eishaan's picture
Add presentation board output and readable evidence table
54d8e0c
Raw
History Blame Contribute Delete
5.27 kB
from __future__ import annotations
import html
import itertools
from typing import Iterable
from .models import EvidenceItem
_counter = itertools.count(1)
def reset_evidence_counter() -> None:
global _counter
_counter = itertools.count(1)
def make_evidence(
*,
category: str,
finding: str,
source_name: str,
source_url: str,
source_type: str,
resolution_or_scope: str,
confidence: str,
limitation: str,
design_implication: str,
verification_needed: str,
output_label: str,
) -> EvidenceItem:
if confidence not in {"high", "medium", "low"}:
raise ValueError(f"Invalid confidence: {confidence}")
if output_label not in {
"computed",
"public_data",
"cad_derived",
"user_input",
"ai_interpretation",
"site_visit_required",
"professional_verification_required",
}:
raise ValueError(f"Invalid output label: {output_label}")
return EvidenceItem(
id=f"E{next(_counter):03d}",
category=category,
finding=finding,
source_name=source_name,
source_url=source_url,
source_type=source_type,
resolution_or_scope=resolution_or_scope,
confidence=confidence, # type: ignore[arg-type]
limitation=limitation,
design_implication=design_implication,
verification_needed=verification_needed,
output_label=output_label, # type: ignore[arg-type]
)
def evidence_to_markdown_table(items: Iterable[EvidenceItem]) -> str:
rows = list(items)
if not rows:
return "_No evidence rows were generated._"
header = (
"| ID | Category | Finding | Source | Confidence | Limitation | "
"Design implication | Verify on site |\n"
"|---|---|---|---|---|---|---|---|"
)
body = []
for item in rows:
source = _md(item.source_name)
if item.source_url:
source = f"[{source}]({item.source_url})"
body.append(
"| {id} | {category} | {finding} | {source} | {confidence} | "
"{limitation} | {implication} | {verify} |".format(
id=_md(item.id),
category=_md(item.category),
finding=_md(item.finding),
source=source,
confidence=_md(item.confidence),
limitation=_md(item.limitation),
implication=_md(item.design_implication),
verify=_md(item.verification_needed),
)
)
return header + "\n" + "\n".join(body)
def evidence_to_dataframe_rows(items: Iterable[EvidenceItem]) -> list[dict[str, str]]:
return [
{
"ID": item.id,
"Category": item.category,
"Finding": item.finding,
"Source": item.source_name,
"Source URL": item.source_url,
"Type": item.source_type,
"Scope / resolution": item.resolution_or_scope,
"Confidence": item.confidence,
"Limitation": item.limitation,
"Design implication": item.design_implication,
"Verify": item.verification_needed,
"Label": item.output_label,
}
for item in items
]
def evidence_to_html_table(items: Iterable[EvidenceItem]) -> str:
rows = list(items)
if not rows:
return "<div class='evidence-shell'><p>No evidence rows were generated.</p></div>"
body = []
for item in rows:
source = html.escape(item.source_name or "Not available")
if item.source_url:
safe_url = html.escape(item.source_url, quote=True)
source = f"<a href='{safe_url}' target='_blank' rel='noopener noreferrer'>{source}</a>"
body.append(
"<tr>"
f"<td class='evidence-id'>{html.escape(item.id)}</td>"
f"<td>{html.escape(item.category)}</td>"
f"<td>{html.escape(item.finding)}</td>"
f"<td>{source}<small>{html.escape(item.source_type)}</small></td>"
f"<td><span class='confidence confidence-{html.escape(item.confidence)}'>{html.escape(item.confidence)}</span></td>"
f"<td>{html.escape(item.resolution_or_scope)}</td>"
f"<td>{html.escape(item.limitation)}</td>"
f"<td>{html.escape(item.design_implication)}</td>"
f"<td>{html.escape(item.verification_needed)}</td>"
"</tr>"
)
return (
"<div class='evidence-shell'>"
"<div class='evidence-header'>"
"<div><span>Evidence Audit</span><strong>Every board claim must trace to a source, calculation, or verification rule.</strong></div>"
f"<b>{len(rows)} rows</b>"
"</div>"
"<div class='evidence-scroll'>"
"<table class='evidence-html-table'>"
"<thead><tr>"
"<th>ID</th><th>Category</th><th>Finding</th><th>Source</th><th>Confidence</th>"
"<th>Scope</th><th>Limitation</th><th>Design implication</th><th>Verify</th>"
"</tr></thead>"
"<tbody>"
+ "".join(body)
+ "</tbody></table></div></div>"
)
def _md(text: object) -> str:
escaped = html.escape("" if text is None else str(text))
return escaped.replace("|", "\\|").replace("\n", " ")