"""render_automatic_eda_markdown — chapter-based EDA report as one Markdown file. Public ``eda``-group entry point that serializes an AutomaticEDA document (a list of chapters, or an ``eda`` TableProfile from which the canonical chapters are built) into a single self-contained Markdown file optimised to be **pasted into an LLM**: plain text, Markdown tables (every row dumped — there are no pages to cut), figures reduced to caption + underlying data, no binaries. It mirrors ``render_automatic_eda_pdf`` / ``render_automatic_eda_pptx`` but for text output; unlike those it writes no manifest (KISS — Markdown is a single text artefact). dict-no-throw: never raises. Returns ``{path, n_chars, chapters, note}``; on a fatal error ``path`` is None and ``note`` explains why. """ from __future__ import annotations from datascience.automatic_eda import build_document, render_md from datascience.automatic_eda.model import as_chapter, as_chapters def _coerce_chapters(chapters_or_profile, meta: dict) -> list: """Accept chapters OR an eda profile and return a list of Chapter.""" arg = chapters_or_profile if isinstance(arg, (list, tuple)): return as_chapters(list(arg)) if isinstance(arg, dict): if "blocks" in arg and "columns" not in arg: ch = as_chapter(arg) return [ch] if ch is not None else [] return build_document(arg, (meta or {}).get("ctx")) return [] def render_automatic_eda_markdown(chapters_or_profile, out_path: str, meta: dict = None) -> dict: """Render an AutomaticEDA document into a single self-contained Markdown file. Args: chapters_or_profile: a list of chapters (``Chapter`` dataclasses or dicts) or an ``eda`` TableProfile dict (chapters built via ``build_document(profile, meta['ctx'])``). out_path: filesystem path for the ``.md`` (parent dirs are created). meta: optional dict. Recognised keys: ``title``, ``ctx`` (dict with ``dataset_name``/``source_origin``/``storage``/``n_rows``/``n_cols``), ``generated_at``, ``embed_figures`` (export PNGs beside the .md, default False — off keeps the Markdown self-contained). Returns: dict (never raises): ``{path: str|None, n_chars: int, chapters: list[{id, version}], note: str}``. On a fatal error ``path`` is None and ``note`` explains the cause. """ meta = dict(meta or {}) chapters = _coerce_chapters(chapters_or_profile, meta) return render_md(chapters, out_path, meta)