Source code for design_research_experiments.reporting

"""Lightweight reporting helpers for study summaries."""

from __future__ import annotations

from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any

from .conditions import Condition
from .study import RunResult, Study


[docs] def render_markdown_summary(study: Study, run_results: Sequence[RunResult]) -> str: """Render a compact markdown summary for one completed study.""" status_counts: dict[str, int] = {} for run_result in run_results: key = run_result.status.value status_counts[key] = status_counts.get(key, 0) + 1 lines = [ f"# Study Summary: {study.title}", "", f"- Study ID: `{study.study_id}`", f"- Runs: `{len(run_results)}`", f"- Conditions: `{len(study.factors)}` factors, `{len(study.blocks)}` blocks", "- Primary outcomes: " f"{', '.join(study.primary_outcomes) if study.primary_outcomes else 'none'}", "", "## Status Counts", ] for status_name in sorted(status_counts): lines.append(f"- {status_name}: {status_counts[status_name]}") lines.extend( [ "", "## Hypotheses", ] ) if not study.hypotheses: lines.append("- none") else: for hypothesis in study.hypotheses: lines.append(f"- `{hypothesis.hypothesis_id}`: {hypothesis.statement}") return "\n".join(lines)
[docs] def render_significance_brief(analysis_rows: Sequence[Mapping[str, Any]]) -> str: """Render a short significance/effect-size brief from analysis outputs.""" lines = ["## Significance Brief"] if not analysis_rows: lines.append("- No analysis rows provided.") return "\n".join(lines) for row in analysis_rows: test_name = row.get("test", "test") outcome = row.get("outcome", "outcome") p_value = row.get("p_value", "n/a") effect_size = row.get("effect_size", "n/a") lines.append(f"- {test_name} on `{outcome}`: p={p_value}, effect_size={effect_size}.") return "\n".join(lines)
[docs] def render_methods_scaffold(study: Study) -> str: """Render a methods-section scaffold for manuscript drafting.""" lines = [ "## Methods", "", f"Study ID: `{study.study_id}`", f"Design: `{study.design_spec.get('kind', 'unknown')}`", f"Replicates: `{study.run_budget.replicates}`", f"Seed policy: `{study.seed_policy.strategy}`", "", "### Factors", ] for factor in study.factors: lines.append(f"- `{factor.name}` ({factor.kind.value}): {factor.description}") lines.extend(["", "### Blocks"]) for block in study.blocks: lines.append(f"- `{block.name}` with levels {list(block.levels)}") return "\n".join(lines)
[docs] def render_codebook(study: Study, conditions: Sequence[Condition]) -> str: """Render a simple codebook of factors, blocks, and condition IDs.""" lines = ["## Codebook", "", "### Condition IDs"] for condition in conditions: factor_repr = condition.factor_assignments block_repr = condition.block_assignments lines.append(f"- `{condition.condition_id}` -> factors={factor_repr}, blocks={block_repr}") lines.extend(["", "### Outcomes"]) for outcome in study.outcomes: source = f"{outcome.source_table}.{outcome.column}" lines.append(f"- `{outcome.name}` from `{source}` ({outcome.aggregation})") return "\n".join(lines)
[docs] def write_markdown_report(output_dir: str | Path, filename: str, content: str) -> Path: """Write markdown report content into the study artifacts directory.""" path = Path(output_dir) / "artifacts" / filename path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content, encoding="utf-8") return path