feat: improve worksheet

This commit is contained in:
2026-08-10 01:52:15 -04:00
parent f475c630e0
commit 327ac371e4
17 changed files with 1469 additions and 49 deletions
+78 -4
View File
@@ -571,6 +571,77 @@ fn asset_schema() -> Value {
})
}
/// The worked solution, and for an open-response item how it is graded.
fn solution_schema() -> Value {
json!({
"type": "object",
"additionalProperties": false,
"description": "The answer, the reasoning, and the rubric. Rendered in the solutions \
document and the answer key, never in a question paper.",
"properties": {
"model_answer": {
"type": "string",
"description": "For an open-response item, the response a full-credit student \
writes; for a choice item, an optional one-line statement of the key."
},
"explanation": {
"type": "string",
"description": "The worked reasoning a student learns from. The body of the \
solutions entry."
},
"rubric": { "type": "array", "items": rubric_criterion_schema() },
"accepted": {
"type": "array",
"items": { "type": "string" },
"description": "Responses a short constructed answer is accepted as."
},
"review": {
"type": "array",
"items": citation_schema(),
"description": "Where to look again after missing this item."
}
}
})
}
/// One rubric line for an open-response item.
fn rubric_criterion_schema() -> Value {
json!({
"type": "object",
"required": ["description"],
"additionalProperties": false,
"properties": {
"description": text("What earns the points on this line."),
"points": { "type": "number", "minimum": 0.0 }
}
})
}
/// A citation into the reference registry, written as an object or a bare string.
fn citation_schema() -> Value {
json!({
"oneOf": [
{ "type": "string", "description": "A citation, unparsed." },
citation_mapping_schema()
]
})
}
/// The object form of a citation.
fn citation_mapping_schema() -> Value {
json!({
"type": "object",
"additionalProperties": false,
"properties": {
"ref": text("Citation key into `references`."),
"locator": { "type": "string", "description": "Where inside the work: §6.1, pp. 4-9." },
"path": { "type": "string", "description": "Joined to the reference base_url." },
"url": { "type": "string" },
"text": { "type": "string" }
}
})
}
/// The schema for authored design intent.
fn design_schema() -> Value {
json!({
@@ -738,9 +809,10 @@ fn item_identity_properties() -> Value {
"cognitive_process": cognitive_process(),
"format": {
"type": "string",
"enum": ["single_best_answer", "multiple_response", "true_false"],
"description": "single_best_answer requires exactly one keyed option; \
multiple_response requires at least two."
"enum": ["single_best_answer", "multiple_response", "true_false", "open_response"],
"description": "single_best_answer keys exactly one option; multiple_response keys \
two or more; open_response takes no options and is graded from its \
solution."
},
"bonus": { "type": "boolean" },
"points": { "type": "number", "exclusiveMinimum": 0.0 },
@@ -767,8 +839,10 @@ fn item_content_properties() -> Value {
"type": "array",
"minItems": 2,
"maxItems": 8,
"description": "Absent for an open_response item; at least two for any choice format.",
"items": option_schema()
},
"solution": solution_schema(),
"learning_objectives": string_array(
"Objective ids this item measures. Reports aggregate on these, so an item with none \
contributes to nothing."
@@ -807,7 +881,7 @@ fn item_schema() -> Value {
}
json!({
"type": "object",
"required": ["id", "level", "stem", "options"],
"required": ["id", "level", "stem"],
"additionalProperties": false,
"properties": Value::Object(properties)
})