Source code for coursebanks.models

"""Shared dictionary contracts for validated banks and workflow results.

These types describe existing dictionaries; they do not construct or validate
values. Use ``core.load_bank`` for files or ``core.parse_bank`` for typed YAML
data. Runtime dictionaries retain additional authoring metadata.
"""

from typing import NotRequired, TypedDict


[docs] class Question(TypedDict): """Describe a validated single-answer question with ordered choices. Attributes ---------- format Optional rendering mode, plain or html. Omission means plain text. shuffle_choices Optional override for the bank's choice-shuffling default. id Unique question identifier following the schema's identifier rules. title Nonempty short title used in instructor materials and QTI items. area Nonempty coverage area used to balance question selection. topic Nonempty topic describing the question's subject. difficulty Difficulty label: Basic, Moderate, or Challenging. assessments Unique, nonblank assessment names restricting eligibility. An empty list permits any assessment. Names are matched exactly, including case and whitespace. minutes Finite positive estimate of the time needed to answer the question. points Finite positive grading weight. stem Nonempty question text, interpreted according to format. choices Ordered mapping of at least two valid choice identifiers to nonempty answer text. Choice text is unique after trimming outer whitespace. answer Identifier of the single correct choice, present in choices. explanation Nonempty instructor explanation included in QTI feedback. Notes ----- Instances are ordinary dictionaries, accessed by key. TypedDict supplies static contracts, not runtime validation. Use parse_bank or load_bank to enforce constraints, and revalidate after editing. Loading normalizes omitted eligibility to an empty list and retains authoring extensions. """ format: NotRequired[str] shuffle_choices: NotRequired[bool] id: str title: str area: str topic: str difficulty: str assessments: list[str] minutes: float points: float stem: str choices: dict[str, str] answer: str explanation: str
[docs] class Bank(TypedDict): """Describe a validated schema-version-1 question bank. Attributes ---------- schema_version Schema version, currently the integer 1. course Nonempty course identifier used to scope question usage history. bank_id Stable bank identifier following the schema's identifier rules. Together with course, it identifies a bank's usage records. title Nonempty display title used for the QTI assessment. default_shuffle_choices Optional default controlling choice shuffling during QTI export. Omission means False. questions Nonempty ordered list of validated questions. IDs are unique, and stems are unique after lowercasing and collapsing whitespace. Notes ----- Instances are ordinary dictionaries. Type annotations do not enforce schema constraints; use parse_bank or load_bank and revalidate edits. Additional authoring metadata is retained in runtime dictionaries. """ schema_version: int course: str bank_id: str title: str default_shuffle_choices: NotRequired[bool] questions: list[Question]
[docs] class QTIReport(TypedDict): """Describe successful structural verification of a QTI archive. Attributes ---------- count Number of question items found in the assessment document. ids Question identifiers in document order. assessment_file Verified ZIP member containing the items. For a Canvas Common Cartridge export, this is the object bank resolved through the QTI wrapper's source-bank reference. Notes ----- Returned by verify_qti after its structural and scoring checks pass. This report does not certify full QTI conformance, Canvas acceptance, or rendered appearance. """ count: int ids: list[str] assessment_file: str
[docs] class BuildReport(QTIReport): """Describe archive verification and provenance for a completed build. Attributes ---------- course Course identifier from the validated source bank. bank_id Identifier of the validated source bank. difficulty Counts of questions for each difficulty present in the bank. sha256 Hexadecimal SHA-256 digest of the exact generated archive bytes. Notes ----- The build function returns this dictionary and saves its contents to validation.json alongside the archive and instructor artifacts. """ course: str bank_id: str difficulty: dict[str, int] sha256: str
[docs] class UsageRecord(TypedDict): """Describe one administered question, scoped by course and bank. Attributes ---------- course Course identifier associated with the administration. bank_id Bank identifier associated with the administration. term Label identifying when the question was administered. assessment Name of the assessment on which the question was administered. id Identifier of the administered question. Notes ----- Values are dictionary entries, not object attributes. History readers normalize the legacy exam column to assessment. Proposed selections do not become usage records in the history file until explicitly recorded. """ course: str bank_id: str term: str assessment: str id: str
[docs] class PlannedUse(UsageRecord): """Describe proposed question use with coverage metadata. Attributes ---------- area Coverage area from the selected question. difficulty Difficulty label from the selected question. Notes ----- Inheritance shares the usage-record dictionary structure; it does not indicate that the question has been administered. Creating a plan does not append records to usage history. """ area: str difficulty: str