Analytics
+Stroke logs and session summaries for every student, across all classes.
+ + + + +No students match your search.
+ +diff --git a/.gitignore b/.gitignore index f3540f7..31a1bd9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules/ .dev.vars *.log .DS_Store +*.env # build outputs workers/api/dist/ diff --git a/data/parse_uji.py b/data/parse_uji.py index f8a9e94..edea11b 100644 --- a/data/parse_uji.py +++ b/data/parse_uji.py @@ -19,6 +19,7 @@ import json import re import string +import sys from dataclasses import dataclass, asdict from pathlib import Path @@ -113,7 +114,25 @@ def parse_all(path: Path = SOURCE) -> list[Sample]: if __name__ == "__main__": + if not SOURCE.is_file() or SOURCE.stat().st_size == 0: + print( + f"parse_uji: missing or empty {SOURCE.name} at {SOURCE}\n" + "Download the UJIpenchars2 text export and save it as ujiv2.txt in the repo root.\n" + "(ujiv2.txt is gitignored; it is not committed to this repository.)", + file=sys.stderr, + ) + sys.exit(1) + samples = parse_all() + if not samples: + print( + f"parse_uji: no samples parsed from {SOURCE}.\n" + "Check that the file is UTF-8 UJIpenchars2 format (WORD / NUMSTROKES / POINTS lines).\n" + "Only ASCII alphanumeric WORD labels are kept.", + file=sys.stderr, + ) + sys.exit(1) + writers = sorted({s.writer for s in samples}) by_label: dict[str, int] = {} for s in samples: diff --git a/frontend/analytics-dashboard.html b/frontend/analytics-dashboard.html new file mode 100644 index 0000000..23dd4f5 --- /dev/null +++ b/frontend/analytics-dashboard.html @@ -0,0 +1,33 @@ + + +
+ + +Stroke logs and session summaries for every student, across all classes.
+ + + + +No students match your search.
+ +Add a class, then enroll students. Sessions are started from the Class tab.
+ + + +Teachers sign in first, then manage sessions and analytics.
+?classId=)Enter the session PIN to end practice and return to the console.
+ + +