""" scripts/generate_curriculum.py Expands the seed curriculum with additional generated items and writes data/T3.1_Math_Tutor/curriculum_full.json Run once before starting the app: python3 scripts/generate_curriculum.py """ from __future__ import annotations import json, random, sys from pathlib import Path SEED_PATH = Path("data/T3.1_Math_Tutor/curriculum_seed.json") OUT_PATH = Path("data/T3.1_Math_Tutor/curriculum_full.json") # ── Generators ──────────────────────────────────────────────────────────────── def _counting_items(n_start: int) -> list[dict]: items = [] for count in range(1, 11): diff = max(1, round(count / 2)) for label in ["apple","star","ball","flower"]: items.append({ "id": f"cnt_gen_{count}_{label}", "skill": "counting", "difficulty": diff, "answer_int": count, "stem_en": f"How many {label}s do you see?", "stem_fr": f"Combien de {label}s vois-tu ?", "stem_kin": f"Ubona {label} zingahe?", "stem_sw": f"Unaona {label} ngapi?", "visual": f"{label}_{count}", }) return items def _addition_items() -> list[dict]: items = [] for a in range(0, 8): for b in range(0, 8): if a + b > 10: continue diff = max(1, min(9, a + b)) items.append({ "id": f"add_gen_{a}_{b}", "skill": "addition", "difficulty": diff, "answer_int": a + b, "stem_en": f"{a} + {b} = ?", "stem_fr": f"{a} + {b} = ?", "stem_kin": f"{a} + {b} = ?", "stem_sw": f"{a} + {b} = ?", "visual": "", }) return items def _subtraction_items() -> list[dict]: items = [] for a in range(1, 11): for b in range(0, a + 1): diff = max(2, min(9, a)) items.append({ "id": f"sub_gen_{a}_{b}", "skill": "subtraction", "difficulty": diff, "answer_int": a - b, "stem_en": f"{a} - {b} = ?", "stem_fr": f"{a} - {b} = ?", "stem_kin": f"{a} - {b} = ?", "stem_sw": f"{a} - {b} = ?", "visual": "", }) return items def _number_sense_items() -> list[dict]: items = [] for n in range(1, 10): items.append({ "id": f"ns_gen_after_{n}", "skill": "number_sense", "difficulty": max(1, n//2), "answer_int": n + 1, "stem_en": f"What number comes after {n}?", "stem_fr": f"Quel nombre vient après {n} ?", "stem_kin": f"Ni umubare ukomeza {n}?", "stem_sw": f"Nambari gani inakuja baada ya {n}?", "visual": "", }) items.append({ "id": f"ns_gen_before_{n+1}", "skill": "number_sense", "difficulty": max(1, n//2), "answer_int": n, "stem_en": f"What number comes before {n+1}?", "stem_fr": f"Quel nombre vient avant {n+1} ?", "stem_kin": f"Ni umubare uba imbere ya {n+1}?", "stem_sw": f"Nambari gani inakuja kabla ya {n+1}?", "visual": "", }) return items # ── Main ────────────────────────────────────────────────────────────────────── def main(): with open(SEED_PATH, "r", encoding="utf-8") as f: seed = json.load(f) existing_ids = {item["id"] for item in seed} generated = ( _counting_items(len(seed)) + _addition_items() + _subtraction_items() + _number_sense_items() ) # De-duplicate against seed new_items = [i for i in generated if i["id"] not in existing_ids] full = seed + new_items random.shuffle(full) OUT_PATH.parent.mkdir(parents=True, exist_ok=True) with open(OUT_PATH, "w", encoding="utf-8") as f: json.dump(full, f, ensure_ascii=False, indent=2) print(f"✅ Generated {len(full)} items ({len(seed)} seed + {len(new_items)} new)") print(f" Saved to {OUT_PATH}") if __name__ == "__main__": main()