feat: support readings and lecture rendering

This commit is contained in:
2026-08-08 16:24:50 -04:00
parent 468af3a815
commit f475c630e0
10 changed files with 1535 additions and 11 deletions
+106 -1
View File
@@ -291,7 +291,12 @@ fn lecture_schema() -> Value {
"date": date("Date delivered."),
"unit": { "type": "string", "description": "Unit id." },
"slides_url": { "type": "string" },
"readings": string_array("Readings assigned with this lecture.")
"readings": {
"type": "array",
"description": "Readings assigned with this lecture, in the order you assign \
them. A plain string is the pre-schema form and still loads.",
"items": reading_schema()
}
}
})
}
@@ -306,6 +311,13 @@ fn objective_schema() -> Value {
"text": text("The objective as a student would read it. Start with a verb."),
"unit": { "type": "string" },
"lectures": string_array("Lecture ids that cover this."),
"order": {
"type": "integer",
"minimum": 1,
"description": "Position in teaching order, low first. A lecture page numbers \
objectives by this; without it they sort by id, which puts \
an objective before its own prerequisite."
},
"level_ceiling": level(),
"prerequisites": string_array(
"Objective ids that must come first. Cycles are rejected."
@@ -320,6 +332,93 @@ fn objective_schema() -> Value {
})
}
/// The schema for one cited work.
fn reference_schema() -> Value {
json!({
"type": "object",
"required": ["title"],
"additionalProperties": false,
"properties": {
"label": text("Short form a reading list shows, such as KKW. One work per label."),
"kind": {
"type": "string",
"enum": strings(&[
"book", "chapter", "article", "preprint", "thesis",
"website", "software", "dataset", "video", "other"
]),
"description": "Kind of work, following BibTeX entry types."
},
"role": {
"type": "string",
"enum": strings(&["required", "supplemental"]),
"description": "required for a course text; supplemental for background."
},
"title": text("Full title."),
"authors": string_array("Authors as `Family, Given`, in printed order."),
"year": { "type": "integer", "description": "Year of publication." },
"edition": { "type": "string", "description": "Edition as printed: 7th." },
"publisher": { "type": "string" },
"container": { "type": "string", "description": "Journal, edited volume, or series." },
"volume": { "type": "string" },
"issue": { "type": "string" },
"pages": { "type": "string", "description": "Pages of the work, not of a reading." },
"doi": { "type": "string", "description": "Bare DOI: 10.1038/nature12373." },
"isbn": { "type": "string" },
"url": { "type": "string", "description": "Canonical URL for the whole work." },
"base_url": {
"type": "string",
"description": "Prefix a reading's `path` is joined to, so the citation key \
appears once instead of once per reading."
},
"note": { "type": "string", "description": "Access notes: reserve shelf, license." }
}
})
}
/// The schema for one reading: a location inside a reference, and what it is for.
fn reading_schema() -> Value {
json!({
"oneOf": [
{ "type": "string", "description": "The pre-schema form: a citation, unparsed." },
reading_mapping_schema()
]
})
}
/// The mapping form of a reading.
fn reading_mapping_schema() -> Value {
json!({
"type": "object",
"required": ["ref"],
"additionalProperties": false,
"properties": {
"ref": text("Citation key into `references`."),
"locator": text("Where inside the work: §6.1, pp. 212-219, ch. 3."),
"path": {
"type": "string",
"description": "Joined to the reference's base_url to reach this location."
},
"url": {
"type": "string",
"description": "Full URL, when base_url does not cover the location."
},
"role": {
"type": "string",
"enum": strings(&["assigned", "supplemental"]),
"description": "supplemental means offered but not separately assessed."
},
"objectives": string_array(
"Objective ids this reading serves. A student who misses one of these is \
pointed here, so the list is what makes study guidance specific."
),
"summary": text("What the section contains."),
"focus": text("What to take from it. This is the sentence a student report quotes."),
"skip": text("What to gloss, and why it is out of scope."),
"text": text("A pre-schema citation string, held unparsed.")
}
})
}
/// The schema for one shared stimulus.
fn stimulus_schema() -> Value {
json!({
@@ -373,6 +472,12 @@ fn course_schema() -> Value {
"type": "object",
"description": "Shared passages, figures, or data that several items refer to.",
"additionalProperties": stimulus_schema()
},
"references": {
"type": "object",
"description": "Works the course cites, by citation key. Readings point in \
here, so an edition change is one edit.",
"additionalProperties": reference_schema()
}
}
})