MUSA Graderv0.1.0

API reference · grader/results.py · 334 lines

grader.results

Grading session persistence and exports (design.md §28-29).

A session is a directory on disk. Everything the UI shows can be rebuilt from it, so the app can be closed and reopened mid-review.

Summary

NameKindDescription
GradingSessionclassOne grading run and everything decided in it, persisted under results/<session_id>/.
list_sessions()functionEvery saved session under results/, in reverse directory-name order (newest first for each assignment).
new_session_id()function<assignment_id>_<YYYYmmdd>_<HHMMSS>.

Constants

NameValueNotes
RESULTS_ROOTPath(__file__).resolve().parent.parent / 'results'
SESSION_FILE'grading_session.json'
GRADES_CSV'grades.csv'
SUMMARY_JSON'summary.json'
FLAGGED_CSV'flagged_submissions.csv'
FEEDBACK_DIR'feedback'
EXECUTED_DIR'executed_notebooks'
RAW_DIR'raw_results'

Classes

class GradingSession #[source]

dataclass

One grading run and everything decided in it, persisted under results/<session_id>/.

Holds a SubmissionResult per student keyed by student id, plus the rubric version and settings that produced them. save writes the session JSON, the CSVs, the class report and per-student feedback; load reads it back.

Fields

NameTypeDefaultNotes
session_idstrrequired
assignment_idstrrequired
assignment_namestrrequired
rubric_versionstrrequired
rubric_pathstrrequired
sourcestr''
execution_modestr'docker'
created_atstrfield(default_factory=utc_now_iso)
updated_atstrfield(default_factory=utc_now_iso)
settingsdict[str, Any]field(default_factory=dict)
resultsdict[str, SubmissionResult]field(default_factory=dict)
rootPath | NoneNone

Methods

ordered_results() -> list[SubmissionResult] #[source]

Results sorted by student id.

upsert(result: SubmissionResult) -> None #[source]

Add or replace one student's result.

summary() -> dict[str, Any] #[source]

The class summary (mean, median, distribution, common issues, rubric breakdown).

queue() -> list[dict[str, Any]] #[source]

The review queue: submissions that still need a person, most urgent first.

student_ids() -> list[str] #[source]

No description.

neighbour(student_id: str, offset: int) -> str | None #[source]

No description.

set_override(student_id: str, rubric_id: str, manual_score: float | None, reason: str = '', enabled: bool = True) -> bool #[source]

Override one rubric item's score. The automatic score is kept alongside it; pass enabled=False to remove an override. Returns False if the student or item does not exist.

mark_reviewed(student_id: str, reviewed: bool = True) -> bool #[source]

Mark a submission as reviewed, which removes it from the queue.

collect_overrides() -> dict[str, dict[str, dict[str, Any]]] #[source]

Snapshot manual work so a regrade can restore it (design.md §31).

apply_overrides(snapshot: dict[str, dict[str, Any]]) -> int #[source]

No description.

to_dict() -> dict[str, Any] #[source]

No description.

save(root: str | Path | None = None) -> Path #[source]

Write the session and all its exports to disk; returns the session directory.

store_executed_notebook(student_id: str, notebook_path: str | Path) -> str | None #[source]

Keep the executed notebook alongside the results (design.md §28).

classmethod load(root: str | Path) -> 'GradingSession' #[source]

Read a session back from its directory.

grades_csv() -> str #[source]

grades.csv: one row per student.

rubric_csv() -> str #[source]

Long-format per-item scores, useful for item analysis.

flagged_csv() -> str #[source]

flagged_submissions.csv: the review queue.

feedback_zip() -> bytes #[source]

Every student's markdown feedback, zipped.

archive_zip() -> bytes #[source]

The whole session directory, for handing over or archiving.

Functions

list_sessions(root: str | Path = RESULTS_ROOT) -> list[dict[str, Any]] #[source]

Every saved session under results/, in reverse directory-name order (newest first for each assignment).

new_session_id(assignment_id: str) -> str #[source]

<assignment_id>_<YYYYmmdd>_<HHMMSS>.