Skip to content

sweetbean.stimulus_spec.io

IO helpers: build declarative SweetBean trials from JSON + CSV.

build_html_from_files(spec_json_path, timeline_csv_path, out_html_path)

Compile and write a runnable jsPsych HTML from declarative files.

Source code in sweetbean/stimulus_spec/io.py
108
109
110
111
112
113
114
115
116
117
118
119
def build_html_from_files(
    spec_json_path: str | Path,
    timeline_csv_path: str | Path,
    out_html_path: str | Path,
) -> Path:
    """Compile and write a runnable jsPsych HTML from declarative files."""
    trials = trial_specs_from_files(spec_json_path, timeline_csv_path)
    compiled = [compile_trial(t) for t in trials]
    exp = Experiment([Block(compiled)])
    out = Path(out_html_path)
    exp.to_html(str(out))
    return out

load_spec_library(path)

Load reusable trial-spec templates from JSON file.

Source code in sweetbean/stimulus_spec/io.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def load_spec_library(path: str | Path) -> dict[str, dict[str, Any]]:
    """Load reusable trial-spec templates from JSON file."""
    p = Path(path)
    payload = json.loads(p.read_text(encoding="utf-8"))
    raw_specs = payload.get("specs")
    if not isinstance(raw_specs, dict) or not raw_specs:
        raise ValueError("Spec file must contain a non-empty 'specs' object.")
    out: dict[str, dict[str, Any]] = {}
    for spec_id, raw in raw_specs.items():
        if not isinstance(spec_id, str) or not spec_id.strip():
            raise ValueError("All spec ids must be non-empty strings.")
        if not isinstance(raw, dict):
            raise ValueError(f"Spec '{spec_id}' must be a JSON object.")
        out[spec_id] = raw
    return out

load_timeline_rows(path)

Load timeline rows from CSV.

Required column: spec_id

Source code in sweetbean/stimulus_spec/io.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def load_timeline_rows(path: str | Path) -> list[dict[str, Any]]:
    """Load timeline rows from CSV.

    Required column: `spec_id`
    """
    p = Path(path)
    rows: list[dict[str, Any]] = []
    with p.open("r", encoding="utf-8", newline="") as f:
        reader = csv.DictReader(f)
        if "spec_id" not in (reader.fieldnames or []):
            raise ValueError("Timeline CSV must contain a 'spec_id' column.")
        for row_idx, row in enumerate(reader, start=1):
            clean = {k: _decode_cell(v or "") for k, v in row.items() if k}
            sid = str(clean.get("spec_id", "")).strip()
            if not sid:
                raise ValueError(f"Missing spec_id at timeline row {row_idx}.")
            clean["spec_id"] = sid
            rows.append(clean)
    if not rows:
        raise ValueError("Timeline CSV is empty.")
    return rows

trial_specs_from_files(spec_json_path, timeline_csv_path)

Materialize validated TrialSpec list from JSON templates + timeline CSV.

Source code in sweetbean/stimulus_spec/io.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def trial_specs_from_files(
    spec_json_path: str | Path,
    timeline_csv_path: str | Path,
) -> list[TrialSpec]:
    """Materialize validated TrialSpec list from JSON templates + timeline CSV."""
    specs = load_spec_library(spec_json_path)
    timeline = load_timeline_rows(timeline_csv_path)

    out: list[TrialSpec] = []
    for row in timeline:
        sid = row["spec_id"]
        if sid not in specs:
            raise KeyError(f"Timeline references unknown spec_id '{sid}'.")
        resolved = _resolve_templates(specs[sid], row)
        out.append(TrialSpec.model_validate(resolved))
    return out

write_prompt_schema_markdown(out_path, *, verbosity=1)

Generate and write markdown docs for Trial/Stimulus/Response specs.

Source code in sweetbean/stimulus_spec/io.py
122
123
124
125
126
127
128
129
130
131
def write_prompt_schema_markdown(
    out_path: str | Path, *, verbosity: int = 1
) -> Path:
    """Generate and write markdown docs for Trial/Stimulus/Response specs."""
    out = Path(out_path)
    out.write_text(
        build_prompt_schema_markdown(verbosity=verbosity) + "\n",
        encoding="utf-8",
    )
    return out