"""Renders a ReportContent (structured, from the LLM) into the Markdown document the API/UI serve alongside the JSON - see spec section 6G for the 16-section layout this follows. """ from __future__ import annotations from app.prompts.report_generation import Finding, InferredProject, ReportContent def _render_findings(findings: list[Finding]) -> str: if not findings: return "_No findings for this section from the current evidence._\n" lines = [] for f in findings: lines.append(f"- **{f.headline}** _(confidence: {f.confidence.value.replace('_', ' ')})_") lines.append(f" {f.summary}") if f.date: lines.append(f" _Date: {f.date}_") return "\n".join(lines) + "\n" def _render_projects(projects: list[InferredProject]) -> str: if not projects: return "_No inferred strategic projects from the current evidence._\n" lines = [] for p in projects: lines.append( f"- **{p.project_name}** _({p.status.value.replace('_', ' ')}, confidence {p.confidence:.0%})_" ) lines.append(f" {p.summary}") if p.alternative_explanations: lines.append(f" Alternative explanations: {'; '.join(p.alternative_explanations)}") return "\n".join(lines) + "\n" def _render_list(items: list[str]) -> str: if not items: return "_None noted._\n" return "\n".join(f"- {item}" for item in items) + "\n" def render_report_markdown( content: ReportContent, *, company_name: str, generated_at: str, model_provider: str, model_name: str, sources: list[dict], ) -> str: parts = [ f"# Competitive Intelligence Report: {company_name}", f"_Generated {generated_at} · {model_provider}/{model_name}_", "", "## 1. Executive Summary", content.executive_summary, "", "## 2. Company Overview", content.company_overview, "", "## 3. Products and Service Landscape", _render_findings(content.products_and_services), "## 4. Recent Developments", _render_findings(content.recent_developments), "## 5. Strategic Initiatives", _render_findings(content.strategic_initiatives), "## 6. Key Project Signals", _render_projects(content.key_inferred_projects), "## 7. Competitive Positioning", content.market_positioning, "", content.competitor_comparison, "", "## 8. SWOT Analysis", "**Strengths**", _render_list(content.swot.strengths), "**Weaknesses**", _render_list(content.swot.weaknesses), "**Opportunities**", _render_list(content.swot.opportunities), "**Threats**", _render_list(content.swot.threats), "## 9. Hiring Signals", _render_findings(content.hiring_signals), "## 10. Product and Technology Signals", _render_findings(content.technology_signals + content.patent_signals), "## 11. Customer Sentiment", content.customer_sentiment, "", "## 12. Financial and Regulatory Signals", _render_findings(content.financial_signals + content.regulatory_and_legal_signals), "## 13. Risks and Opportunities", "**Risks**", _render_list(content.risks), "**Opportunities**", _render_list(content.opportunities), "## 14. Important Unknowns", _render_list(content.unknowns_and_missing_data), "## 15. Sources", _render_sources(sources), "## 16. Methodology and Limitations", content.methodology, "", content.limitations, ] return "\n".join(str(p) for p in parts) def _render_sources(sources: list[dict]) -> str: if not sources: return "_No sources recorded for this report._\n" lines = [ f"- [{s.get('title') or s.get('url')}]({s.get('url')}) — retrieved {s.get('retrieved_date')}" for s in sources ] return "\n".join(lines) + "\n"