cass.actions¶
Orchestration layer: config discovery, pull flows, setup checks. The CLI and viewer call into this package and never below it directly.
cass.actions ¶
Actions — business logic for cass.
Pull orchestration and configuration.
config ¶
Config discovery, loading, and writing for cass.
Searches for cass.toml starting from the current directory and walking
upward, unless set_config_path points at an explicit file (--config).
Config is loaded lazily on first access via get_config().
CanvasModuleSpec
dataclass
¶
Desired state for a Canvas module declared in cass.toml.
CanvasAssignmentSpec
dataclass
¶
CanvasAssignmentSpec(
name: str,
points: float = 0.0,
submission_types: list[str] = (
lambda: ["online_url"]
)(),
due_at: str = "",
published: bool = False,
group: str = "",
)
Desired state for a Canvas assignment declared in cass.toml.
CanvasQuizRef
dataclass
¶
A quiz file declared in cass.toml, resolved relative to the config file.
parse_canvas_course_url ¶
Extract (base_url, course_id) from a Canvas course URL.
set_config_path ¶
Point config discovery at an explicit cass.toml (--config).
Pass None to return to walking up from the working directory.
Raises:
| Type | Description |
|---|---|
SystemExit
|
If path does not exist. |
find_project_root ¶
Find the project root.
With config_path (or a path set via set_config_path) the root is that
file's directory. Otherwise walk up from start (default: cwd) looking
for cass.toml.
config_file ¶
Return the config file in use (explicit path or <root>/cass.toml).
config_file_path ¶
Return the path to the config file, or None if it doesn't exist.
read_config_data ¶
Load a TOML config file into a mutable dict.
format_toml_value ¶
Render a scalar or flat list as a TOML value.
update_config ¶
update_config(
path: Path,
*,
canvas_base_url: str | None = None,
canvas_course_id: int | None = None,
canvas_time_zone: str | None = None,
) -> None
Update Canvas settings in cass.toml while preserving other data.
A leftover [classroom] table from older versions is removed on every
rewrite.
write_config ¶
Write a cass.toml file.
doctor ¶
Prerequisite checks for cass setup (cass init doctor).
pull ¶
Pull orchestration — fetch Canvas data into the local database.
pull_students ¶
CLI step: pull the Canvas roster.
pull_assignments ¶
CLI step: pull Canvas assignments.
pull_submissions ¶
CLI step: pull Canvas submissions for every stored assignment.
pull_all ¶
Run a full pull: course name, students, assignments, submissions.
Shared by cass pull and the viewer's progress page. Progress is
reported through on_progress(step, detail) with steps students,
assignments, submissions, and done.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
when local Canvas edits would be overwritten. |
quiz_responses ¶
Quiz responses — every student's answers to a classic quiz or survey.
Canvas's student analysis report is wide: one "<id>: <text>" column per
question, each followed by its score column. parse_student_analysis
reshapes it to one QuizResponse per student x attempt x question, and
add_timing fills in what the report lacks: each student's due date
(after overrides and extensions) and whether Canvas closed the attempt itself.
QuizResponse
dataclass
¶
QuizResponse(
student: str,
user_id: int | None,
sis_id: str,
section: str,
attempt: int | None,
submitted_at: str,
question_id: int,
position: int,
question: str,
answer: str,
score: float | None,
due_at: str | None = None,
late: bool | None = None,
started_at: str | None = None,
auto_submitted: bool | None = None,
)
One student's answer to one question on one attempt.
Timing fields are None when unknown: started_at and
auto_submitted exist only for a student's latest attempt.
row ¶
Values for columns (default RESPONSE_COLUMNS) as CSV strings.
parse_student_analysis ¶
Reshape a student analysis report CSV to one row per answer.
The latest-only report has no attempt column; its responses get
attempt=None until add_timing fills it in. Submission times are
normalized to ISO 8601 UTC (...Z).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Report CSV as downloaded from Canvas. |
required |
Returns:
| Type | Description |
|---|---|
list[QuizResponse]
|
Responses in report order, questions in report column order. |
add_timing ¶
add_timing(
responses: list[QuizResponse],
*,
due_by_user: dict[int, str | None],
quiz_submissions: list[CanvasQuizSubmission],
default_due: str | None = None,
) -> list[QuizResponse]
Fill due dates, lateness, and auto-submission flags in place.
due_at: the student's own due date (cached_due_date, which respects overrides and extensions, and may beNone), elsedefault_duefor students missing from due_by_user.late:submitted_at > due_atwhen both are known.attempt: the latest finished attempt number, for latest-only reports.started_at/auto_submitted: from the student's latest quiz submission, on that attempt only.auto_submittedmeans Canvas closed the attempt (finished_at >= end_at): a time limit ran out, or an attempt left open was force-submitted when the course concluded.
Returns:
| Type | Description |
|---|---|
list[QuizResponse]
|
The same list, for chaining. |
fetch_quiz_responses ¶
fetch_quiz_responses(
client: CanvasClient,
quiz: CanvasQuiz,
*,
all_attempts: bool = False,
) -> list[QuizResponse]
Download, reshape, and time-stamp every student's answers to a quiz.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
CanvasClient
|
Canvas client for the quiz's course. |
required |
quiz
|
CanvasQuiz
|
Classic quiz or survey. |
required |
all_attempts
|
bool
|
Include every attempt, not only each student's latest. |
False
|
quizzes ¶
Quiz authoring — quiz files, specs, and Canvas settings diffs.
A quiz file is TOML: settings at the top level, questions as [[questions]].
load_quiz_file validates it into a QuizSpec; dump_quiz_file writes
one back so export and create --from round-trip.
AnswerSpec
dataclass
¶
One answer on a question; correct becomes Canvas weight 100.
QuestionSpec
dataclass
¶
QuestionSpec(
text: str,
type: str = "essay_question",
name: str = "",
points: float | None = None,
answers: list[AnswerSpec] = list(),
)
One question in a quiz file.
QuizSpec
dataclass
¶
QuizSpec(
title: str,
quiz_type: str,
group: str = "",
points: float | None = None,
description: str = "",
unlock_at: str = "",
due_at: str = "",
lock_at: str = "",
attempts: int = 1,
time_limit: int = 0,
hide_results: str = "",
scoring_policy: str = "keep_highest",
shuffle_answers: bool = False,
one_question_at_a_time: bool = False,
published: bool = False,
questions: list[QuestionSpec] = list(),
)
Desired settings and questions for a Canvas Classic quiz.
default_question_points ¶
Points a question gets when the file leaves them out.
load_quiz_file ¶
Read and validate a quiz TOML file.
Raises:
| Type | Description |
|---|---|
SystemExit
|
Naming the file and offending key for any problem. |
dump_quiz_file ¶
Render spec as quiz-file TOML, writing it to path when given.
quiz_spec_from_canvas ¶
quiz_spec_from_canvas(
quiz: CanvasQuiz,
questions: list[CanvasQuizQuestion],
group_name: str,
tz: str,
) -> QuizSpec
Build a spec from a live quiz, with times in the course zone.
quiz_form_fields ¶
quiz_form_fields(
spec: QuizSpec,
tz: str,
*,
assignment_group_id: int | None = None,
) -> dict[str, object]
Canvas quiz fields for spec (Canvas names, without the quiz[] wrapper).
Empty strings clear a field on Canvas; time_limit 0 is sent as "".
quiz_settings_diff ¶
quiz_settings_diff(
spec: QuizSpec,
live: CanvasQuiz,
tz: str,
*,
group_id: int | None = None,
) -> dict[str, object]
Canvas fields whose live value differs from spec.
Times compare by instant, so an offset-only difference is not a change. Questions are never compared.
strip_html ¶
Drop tags, unescape entities, and collapse whitespace.
Block-level tags become spaces so adjacent paragraphs do not run together;
inline tags such as <b> vanish without adding a space.
sync_quizzes ¶
sync_quizzes(
client: CanvasClient,
specs: list[QuizSpec],
group_ids: dict[str, int],
tz: str,
*,
apply: bool,
) -> list[tuple[str, str, str]]
Reconcile quiz specs against live quizzes, matched by title.
Questions on an existing quiz are never compared or modified.
Returns:
| Type | Description |
|---|---|
list[tuple[str, str, str]]
|
|
list[tuple[str, str, str]]
|
fields), or skip. |