"""Teacher-authored feedback, independent of the generated correction data.""" from pathlib import Path from copienator import atomic_update_json, read_json FILENAME = "custom_remarks.json" def copy_key(copy_id: str) -> str: if not copy_id.strip().isascii() or not copy_id.strip().isdecimal(): raise ValueError("Numéro de copie invalide.") return str(int(copy_id)) def load_remarks(root: Path) -> dict[str, dict[str, str]]: data = read_json(root / FILENAME, default={}) validate(data) return data def validate(data): if not isinstance(data, dict) or any( not isinstance(key, str) or copy_key(key) != key or not isinstance(labels, dict) or any(not isinstance(label, str) or not isinstance(text, str) for label, text in labels.items()) for key, labels in data.items() ): raise ValueError(f"Format invalide dans {FILENAME}.") def remarks_for_copy(root: Path, copy_id: str) -> dict[str, str]: return load_remarks(root).get(copy_key(copy_id), {}) def set_remark(root: Path, copy_id: str, label: str, text: str) -> str: key = copy_key(copy_id) text = text.strip() previous = "" def update(data): nonlocal previous validate(data) labels = data.setdefault(key, {}) previous = labels.get(label, "") if text: labels[label] = text else: labels.pop(label, None) if not labels: data.pop(key, None) atomic_update_json(root / FILENAME, update, default_factory=dict) return previous def apply_custom_remarks(root: Path, copy_id: str, labels_data: dict) -> None: """Append after checkbox actions; replace our own entries on repeated calls.""" remarks = remarks_for_copy(root, copy_id) for label, content in labels_data.items(): result = content["result"] feedback = [item for item in result.get("feedback", []) if not item.get("custom_remark")] if remarks.get(label): feedback.append({"text": remarks[label], "box_2d": None, "custom_remark": True}) result["feedback"] = feedback