feat: export improvements — Phase A + Phase B #75
@@ -133,6 +133,42 @@ def _get_outcome_label(session: Session) -> str | None:
|
||||
return OUTCOME_LABELS.get(outcome, str(outcome).replace("_", " ").title())
|
||||
|
||||
|
||||
def _build_summary_fields(session: Session) -> dict[str, str]:
|
||||
"""Build auto-populated summary fields from session data.
|
||||
|
||||
Empty fields are left blank — users fill them in via the editable preview.
|
||||
"""
|
||||
tree_name = session.tree_snapshot.get("name", "Troubleshooting Session")
|
||||
tree_desc = session.tree_snapshot.get("description", "")
|
||||
issue = f"{tree_name}: {tree_desc}" if tree_desc else tree_name
|
||||
|
||||
if session.completed_at:
|
||||
status = "Resolved" if getattr(session, "outcome", None) == "resolved" else \
|
||||
f"Completed — {_get_outcome_label(session) or 'Unknown'}"
|
||||
else:
|
||||
step_count = len(session.decisions) if session.decisions else 0
|
||||
status = f"In Progress — paused at step {step_count}" if step_count else "In Progress"
|
||||
|
||||
_raw_notes = getattr(session, 'outcome_notes', None)
|
||||
resolution = (_raw_notes if isinstance(_raw_notes, str) else '').strip()
|
||||
|
||||
_raw_next = getattr(session, 'next_steps', None)
|
||||
next_steps = (_raw_next if isinstance(_raw_next, str) else '').strip()
|
||||
|
||||
return {
|
||||
"issue": issue,
|
||||
"impact": "",
|
||||
"status": status,
|
||||
"resolution": resolution,
|
||||
"next_steps": next_steps,
|
||||
}
|
||||
|
||||
|
||||
def _escape_markdown_table(value: str) -> str:
|
||||
"""Escape value for use in a markdown table cell."""
|
||||
return value.replace("|", "\\|").replace("\n", " ")
|
||||
|
||||
|
||||
def generate_markdown_export(session: Session, options: SessionExport) -> str:
|
||||
"""Generate markdown export."""
|
||||
lines = []
|
||||
@@ -157,6 +193,22 @@ def generate_markdown_export(session: Session, options: SessionExport) -> str:
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
|
||||
if options.include_summary:
|
||||
summary = _build_summary_fields(session)
|
||||
esc = _escape_markdown_table
|
||||
lines.append("## Summary")
|
||||
lines.append("")
|
||||
lines.append("| Field | Details |")
|
||||
lines.append("|-------|---------|")
|
||||
lines.append(f"| Issue | {esc(summary['issue'])} |")
|
||||
lines.append(f"| Impact | {esc(summary['impact'])} |")
|
||||
lines.append(f"| Status | {esc(summary['status'])} |")
|
||||
lines.append(f"| Resolution | {esc(summary['resolution'])} |")
|
||||
lines.append(f"| Next Steps | {esc(summary['next_steps'])} |")
|
||||
lines.append("")
|
||||
lines.append("---")
|
||||
lines.append("")
|
||||
|
||||
# Scratchpad / Evidence section
|
||||
scratchpad = getattr(session, 'scratchpad', '') or ''
|
||||
if scratchpad.strip():
|
||||
@@ -252,6 +304,17 @@ def generate_text_export(session: Session, options: SessionExport) -> str:
|
||||
lines.append(f"Outcome: {outcome_label}")
|
||||
lines.append("")
|
||||
|
||||
if options.include_summary:
|
||||
summary = _build_summary_fields(session)
|
||||
lines.append("SUMMARY")
|
||||
lines.append("-" * 20)
|
||||
lines.append(f"Issue: {summary['issue']}")
|
||||
lines.append(f"Impact: {summary['impact']}")
|
||||
lines.append(f"Status: {summary['status']}")
|
||||
lines.append(f"Resolution: {summary['resolution']}")
|
||||
lines.append(f"Next Steps: {summary['next_steps']}")
|
||||
lines.append("")
|
||||
|
||||
# Scratchpad / Evidence section
|
||||
scratchpad = getattr(session, 'scratchpad', '') or ''
|
||||
if scratchpad.strip():
|
||||
@@ -350,6 +413,17 @@ def generate_html_export(session: Session, options: SessionExport) -> str:
|
||||
html_parts.append(f'<p><strong>Outcome:</strong> {html.escape(outcome_label)}</p>')
|
||||
html_parts.append('</div>')
|
||||
|
||||
if options.include_summary:
|
||||
summary = _build_summary_fields(session)
|
||||
html_parts.append('<h2>Summary</h2>')
|
||||
html_parts.append('<table style="width: 100%; border-collapse: collapse; margin-bottom: 20px;">')
|
||||
for label, value in [("Issue", summary["issue"]), ("Impact", summary["impact"]),
|
||||
("Status", summary["status"]), ("Resolution", summary["resolution"]),
|
||||
("Next Steps", summary["next_steps"])]:
|
||||
html_parts.append(f'<tr><td style="padding: 6px 12px; border: 1px solid #ddd; font-weight: bold; width: 120px;">{html.escape(label)}</td>')
|
||||
html_parts.append(f'<td style="padding: 6px 12px; border: 1px solid #ddd;">{html.escape(value)}</td></tr>')
|
||||
html_parts.append('</table>')
|
||||
|
||||
# Scratchpad / Evidence section
|
||||
scratchpad = getattr(session, 'scratchpad', '') or ''
|
||||
if scratchpad.strip():
|
||||
@@ -427,6 +501,16 @@ def generate_psa_export(session: Session, options: SessionExport) -> str:
|
||||
lines.append(f"Outcome: {outcome_label}")
|
||||
lines.append("")
|
||||
|
||||
if options.include_summary:
|
||||
summary = _build_summary_fields(session)
|
||||
lines.append("--- SUMMARY ---")
|
||||
lines.append(f"Issue: {summary['issue']}")
|
||||
lines.append(f"Impact: {summary['impact']}")
|
||||
lines.append(f"Status: {summary['status']}")
|
||||
lines.append(f"Resolution: {summary['resolution']}")
|
||||
lines.append(f"Next Steps: {summary['next_steps']}")
|
||||
lines.append("")
|
||||
|
||||
# Problem section
|
||||
lines.append("--- PROBLEM ---")
|
||||
lines.append(tree_description if tree_description else "No description provided.")
|
||||
|
||||
Reference in New Issue
Block a user