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