Spaces:
Running
Running
| """Export & Handover tab — Jira JSON, CSV, Markdown download.""" | |
| import json | |
| import csv | |
| import io | |
| import streamlit as st | |
| from frontend.utils.terminology import label, ac_badge, subdomain_label | |
| def _to_jira_json(result: dict) -> str: | |
| issues: list[dict] = [] | |
| phases = result.get("phases") or [] | |
| for phase in phases: | |
| for epic in (phase.get("epics") or []): | |
| epic_issue = { | |
| "externalId": epic.get("epic_id", ""), | |
| "issueType": "Epic", | |
| "summary": epic.get("title", ""), | |
| "description": epic.get("description", ""), | |
| "labels": [subdomain_label(epic.get("subdomain_group", ""))], | |
| "customField_EpicName": epic.get("title", ""), | |
| "priority": "High" if phase.get("phase_number") == 1 else "Medium", | |
| } | |
| issues.append(epic_issue) | |
| for feat in (epic.get("features") or []): | |
| for story in (feat.get("user_stories") or []): | |
| role = story.get("role") or story.get("as_a") or "User" | |
| want = story.get("want") or story.get("i_want") or "" | |
| so_that = story.get("so_that") or "" | |
| acs = story.get("acceptance_criteria") or [] | |
| story_issue = { | |
| "externalId": f"{epic.get('epic_id', '')}-{feat.get('title', '')[:10]}-{role[:5]}", | |
| "issueType": "Story", | |
| "summary": f"As a {role}, I want {want}", | |
| "description": f"As a {role}, I want {want}, so that {so_that}", | |
| "acceptanceCriteria": "\n".join(f"- {ac}" for ac in acs), | |
| "epicLink": epic.get("epic_id", ""), | |
| } | |
| issues.append(story_issue) | |
| return json.dumps({"projects": [{"issues": issues}]}, indent=2) | |
| def _to_csv(result: dict) -> str: | |
| buf = io.StringIO() | |
| writer = csv.writer(buf) | |
| writer.writerow([ | |
| "Phase", | |
| "Initiative ID", | |
| "Initiative Title", | |
| "Capability Area", | |
| "Delivery Sprints", | |
| "Regulatory Anchor", | |
| "Innovation Driver", | |
| "Delivery Standards Count", | |
| ]) | |
| for phase in (result.get("phases") or []): | |
| for epic in (phase.get("epics") or []): | |
| writer.writerow([ | |
| f"Phase {phase.get('phase_number', '')} — {phase.get('phase_name', '')}", | |
| epic.get("epic_id", ""), | |
| epic.get("title", ""), | |
| subdomain_label(epic.get("subdomain_group", "")), | |
| epic.get("estimated_sprints", ""), | |
| epic.get("governance_reference", ""), | |
| epic.get("trend_alignment", ""), | |
| len(epic.get("acceptance_criteria") or []), | |
| ]) | |
| return buf.getvalue() | |
| def _to_markdown(result: dict) -> str: | |
| lines: list[str] = [ | |
| f"# Enterprise Architecture Strategic Roadmap — {result.get('org_type', '')}\n" | |
| ] | |
| for phase in (result.get("phases") or []): | |
| lines.append(f"\n## Phase {phase.get('phase_number')}: {phase.get('phase_name', '')}") | |
| lines.append(f"Duration: {phase.get('duration_months', 0)} months\n") | |
| for epic in (phase.get("epics") or []): | |
| lines.append(f"### {epic.get('epic_id', '')} — {epic.get('title', '')}") | |
| if epic.get("subdomain_group"): | |
| lines.append(f"*{label('subdomain')}: {subdomain_label(epic['subdomain_group'])}*\n") | |
| if epic.get("description"): | |
| lines.append(epic["description"] + "\n") | |
| if epic.get("governance_reference"): | |
| lines.append(f"**{label('governance_reference')}:** {epic['governance_reference']}") | |
| if epic.get("trend_alignment"): | |
| lines.append(f"**{label('trend_alignment')}:** {epic['trend_alignment']}") | |
| lines.append(f"**Delivery Sprints:** {epic.get('estimated_sprints', '—')}\n") | |
| acs = epic.get("acceptance_criteria") or [] | |
| if acs: | |
| lines.append(f"**{label('acceptance_criteria')}:**") | |
| for ac in acs: | |
| lines.append(f"- [ ] {ac_badge(ac)}") | |
| lines.append("") | |
| for feat in (epic.get("features") or []): | |
| lines.append(f"#### {label('feature')}: {feat.get('title', '')}") | |
| for story in (feat.get("user_stories") or []): | |
| role = story.get("role") or "User" | |
| want = story.get("want") or "" | |
| so_that = story.get("so_that") or "" | |
| lines.append( | |
| f"- **{label('user_story')}** — " | |
| f"As a **{role}**, I want {want}, so that {so_that}" | |
| ) | |
| return "\n".join(lines) | |
| def render_export_tab(result: dict) -> None: | |
| st.subheader("Export & Handover") | |
| fmt = st.radio("Format", ["Jira JSON", "CSV", "Markdown"], horizontal=True) | |
| if fmt == "Jira JSON": | |
| content = _to_jira_json(result) | |
| mime = "application/json" | |
| filename = "ea_roadmap_jira.json" | |
| elif fmt == "CSV": | |
| content = _to_csv(result) | |
| mime = "text/csv" | |
| filename = "ea_roadmap.csv" | |
| else: | |
| content = _to_markdown(result) | |
| mime = "text/markdown" | |
| filename = "ea_roadmap.md" | |
| st.download_button( | |
| label=f"Download {fmt}", | |
| data=content, | |
| file_name=filename, | |
| mime=mime, | |
| width='stretch', | |
| type="primary", | |
| ) | |
| st.divider() | |
| st.caption("Preview (first 3000 characters):") | |
| st.code( | |
| content[:3000] + ("…" if len(content) > 3000 else ""), | |
| language="json" if fmt == "Jira JSON" else "markdown", | |
| ) | |