| """ |
| 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") |
|
|
| |
|
|
| 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 |
|
|
|
|
| |
|
|
| 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() |
| ) |
|
|
| |
| 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() |
|
|