SpeakLab / controllers /shuffler_controller.py
seba3y's picture
Upload 30 files
a695f69 verified
Raw
History Blame Contribute Delete
2.4 kB
"""
ShufflerController — mirrors the JS makeShuffler() no-repeat logic.
The seen-list for each named pool is kept inside a dict that lives in
gr.BrowserState (localStorage). The state dict shape:
{
"jam": {"seen": ["...", ...]},
"tt_easy": {"seen": [...]},
"tt_medium": {"seen": [...]},
"tt_hard": {"seen": [...]},
"imp_all": {"seen": [...]},
"imp_0": {"seen": [...]}, # per-category index
...
"iv_all": {"seen": [...]},
"iv_0": {"seen": [...]},
...
}
"""
import json
import random
from pydantic import BaseModel
from models.session_models import ShufflerState
def _serialise(item) -> str:
if isinstance(item, str):
return item
if isinstance(item, BaseModel):
return json.dumps(item.model_dump(), ensure_ascii=False, sort_keys=True)
return json.dumps(item, ensure_ascii=False)
def next_item(pool: list, key: str, raw_state: dict) -> tuple:
"""
Pick the next unseen item from pool.
Returns (item, updated_raw_state).
"""
if not pool:
return None, raw_state
state_dict = raw_state.get(key, {}) if isinstance(raw_state, dict) else {}
shuffler = ShufflerState.model_validate(state_dict)
seen_set = set(shuffler.seen)
unseen = [i for i, item in enumerate(pool) if _serialise(item) not in seen_set]
if not unseen:
# Full cycle complete — reset
shuffler.seen = []
seen_set = set()
unseen = list(range(len(pool)))
idx = random.choice(unseen)
picked = pool[idx]
shuffler.seen.append(_serialise(picked))
updated = dict(raw_state) if isinstance(raw_state, dict) else {}
updated[key] = shuffler.model_dump()
return picked, updated
def reset_key(key: str, raw_state: dict) -> dict:
"""Reset the seen list for a single key."""
updated = dict(raw_state) if isinstance(raw_state, dict) else {}
updated[key] = ShufflerState().model_dump()
return updated
def imp_pool_key(cat: str, categories: list) -> str:
if cat == "All":
return "imp_all"
idx = next((i for i, c in enumerate(categories) if c.category == cat), 0)
return f"imp_{idx}"
def iv_pool_key(cat: str, categories: list) -> str:
if cat == "All":
return "iv_all"
idx = next((i for i, c in enumerate(categories) if c.category == cat), 0)
return f"iv_{idx}"