from datetime import datetime import re import markdown from jinja2 import Environment, FileSystemLoader, select_autoescape from weasyprint import HTML from cbh.api.reports.dto import StackType, ToolScore, ToolType from cbh.api.reports.models import ReportModel from cbh.core.config import settings _STACK_TYPE_LABELS = { StackType.RECOMMENDED: "Recommended", StackType.FASTEST: "Fastest", StackType.CHEAPEST: "Cheapest", } _TOOL_TYPE_LABELS = { ToolType.RECOMMENDED: "Recommended", ToolType.ALTERNATIVE: "Alternative", } _TOOL_SCORE_LABELS = { ToolScore.GREAT: "Great", ToolScore.GOOD: "Good", ToolScore.AVERAGE: "Average", ToolScore.POOR: "Poor", } _TOOL_SCORE_CLASSES = { ToolScore.GREAT: "great", ToolScore.GOOD: "good", ToolScore.AVERAGE: "average", ToolScore.POOR: "poor", } _MARKDOWN_EXTENSIONS = ["extra", "sane_lists", "nl2br"] def _render_markdown(value: str | None) -> str: if not value: return "" return markdown.markdown( value, extensions=_MARKDOWN_EXTENSIONS, output_format="html5" ) def _get_template_env() -> Environment: templates_path = settings.BASE_DIR / "cbh" / "templates" / "report" env = Environment( loader=FileSystemLoader(str(templates_path)), autoescape=select_autoescape(["html", "xml"]), ) env.globals["stack_type_label"] = lambda t: _STACK_TYPE_LABELS.get(t, str(t)) env.globals["tool_type_label"] = lambda t: _TOOL_TYPE_LABELS.get(t, str(t)) env.globals["tool_score_label"] = lambda s: _TOOL_SCORE_LABELS.get(s, str(s)) env.globals["tool_score_class"] = lambda s: _TOOL_SCORE_CLASSES.get(s, "good") return env def _resolve_recipient_name(report: ReportModel) -> str: account = report.account return account.name or account.email or "Founder" def _normalize_stack(report: ReportModel) -> list: normalized = [] for group in report.stack or []: if isinstance(group, dict): continue if not getattr(group, "tools", None): continue normalized.append(group) return normalized def download_report_obj(report: ReportModel) -> bytes: """ Render a report as a styled PDF document and return raw PDF bytes. """ env = _get_template_env() template = env.get_template("base.html") html_content = template.render( report=report, recipient_name=_resolve_recipient_name(report), generated_at=datetime.utcnow().strftime("%B %d, %Y"), stack=_normalize_stack(report), review_html=_render_markdown(report.review), deep_analysis_html=_render_markdown(report.deepAnalysis), monetization_html=_render_markdown(report.monetization), risks_html=_render_markdown(report.risks), marketing_plan_html=_render_markdown(report.marketingPlan), next_steps_html=_render_markdown(report.nextSteps), ) return HTML(string=html_content).write_pdf() def prepare_filename(name: str) -> str: safe_name = re.sub(r"[^A-Za-z0-9._-]+", "_", name).strip("_") or "report" return f"{safe_name}.pdf"