Moves the typing app's lesson plan and progress from client-side YAML/localStorage into the backend, and adds a reward system that ties passing a lesson to unlocking part of the music library. Lock state is always recomputed live from curriculum x progress x the live library, never persisted separately. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
35 lines
1.2 KiB
Python
35 lines
1.2 KiB
Python
"""The typing game's live in-memory state, built once at startup from config."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
from musicmouse.config import TippenConfig
|
|
from musicmouse.tippen.curriculum import Curriculum, load_curriculum
|
|
from musicmouse.tippen.progress import TypingProgress, load_progress, save_progress
|
|
|
|
__all__ = ["TippenRuntime", "build_tippen_runtime"]
|
|
|
|
|
|
@dataclass
|
|
class TippenRuntime:
|
|
curriculum: Curriculum
|
|
progress_path: Path
|
|
progress: TypingProgress
|
|
|
|
def save(self) -> None:
|
|
save_progress(self.progress_path, self.progress)
|
|
|
|
|
|
def build_tippen_runtime(config: TippenConfig) -> TippenRuntime:
|
|
"""Raises :class:`musicmouse.tippen.curriculum.CurriculumError` on a broken
|
|
curriculum file - a startup-time failure, same gate as a broken ``config.yml``.
|
|
Progress never raises; see ``musicmouse.tippen.progress.load_progress``.
|
|
"""
|
|
curriculum = load_curriculum(config.curriculum_file)
|
|
progress = load_progress(config.progress_file, curriculum)
|
|
return TippenRuntime(
|
|
curriculum=curriculum, progress_path=config.progress_file, progress=progress
|
|
)
|