123 lines
5.1 KiB
Rust
123 lines
5.1 KiB
Rust
//! # coursebank
|
|
//!
|
|
//! A tool for running the assessment side of a course as version-controlled data.
|
|
//!
|
|
//! The premise is that the artifacts you already produce (questions, exams,
|
|
//! grading exports) are worth treating as a dataset rather than as a pile of
|
|
//! documents. Once they are, several things you cannot otherwise do become
|
|
//! routine: knowing which questions actually discriminate, catching a poorly worded
|
|
//! item from the pattern of who chose which distractor, telling a student which
|
|
//! misconception their specific wrong answer indicates, and never accidentally
|
|
//! reusing the same question three terms in a row.
|
|
//!
|
|
//! ## The four kinds of file
|
|
//!
|
|
//! | File | Holds | Written by |
|
|
//! |:--|:--|:--|
|
|
//! | `course.yaml` | identity, policy, objectives, lectures | you |
|
|
//! | `banks/*.yaml` | items, with design intent and pooled statistics | you, then `calibrate` |
|
|
//! | `assessments/*.yaml` | what was given, to whom, when | `assemble`, then you |
|
|
//! | `data/*.parquet` | one row per student per item | `ingest` |
|
|
//!
|
|
//! Three of the four are hand-editable YAML meant to be reviewed in a pull request.
|
|
//! Only the response data is machine-only, and it is stored in an open columnar
|
|
//! format so pandas, polars, R, and DuckDB can all read it without this tool.
|
|
//!
|
|
//! ## The loop
|
|
//!
|
|
//! ```text
|
|
//! author items ──▶ validate ──▶ lint ──▶ assemble ──▶ export ──┐
|
|
//! ▲ │
|
|
//! │ administer
|
|
//! │ │
|
|
//! calibrate ◀── analyze ◀── ingest ◀───────────────────────────┘
|
|
//! │
|
|
//! └──▶ report (students and cohort)
|
|
//! ```
|
|
//!
|
|
//! The arrow back from `calibrate` to authoring is the point of the whole design.
|
|
//! Statistics written onto the item are there the next time you consider using it,
|
|
//! and they accumulate across terms: twenty-four students tells you very little,
|
|
//! but ninety-six across four terms tells you something real.
|
|
//!
|
|
//! ## Design commitments
|
|
//!
|
|
//! Assessment records are the single source of truth for reuse history. There
|
|
//! is no separate ledger file, because a ledger duplicates what the records must
|
|
//! already get right and then drifts from it. [`assessment::History`] derives usage
|
|
//! by scanning the records.
|
|
//!
|
|
//! **Fingerprints cover only what a student saw.** Retag an item's metadata and its
|
|
//! pooled statistics stay valid; reword the stem and they are marked stale. See
|
|
//! [`item::Item::fingerprint`].
|
|
//!
|
|
//! **Validation reports everything at once.** Fixing one typo per run is not a
|
|
//! workflow. [`error::Error::Invalid`] carries a list.
|
|
//!
|
|
//! **Validation and linting are separate.** [`bank::BankFile::validate`] enforces
|
|
//! what must be true; [`lint`] advises on what is usually a mistake, and every rule
|
|
//! has a code you can silence.
|
|
//!
|
|
//! **Small samples are labelled as such.** Every statistic computed from a class of
|
|
//! twenty-five is reported with the caveat it deserves rather than three decimal
|
|
//! places of false precision.
|
|
//!
|
|
//! ## Dependency posture
|
|
//!
|
|
//! Deliberately small: serde, a YAML parser, clap, thiserror, and csv, plus arrow
|
|
//! and parquet behind a default-on feature that can be switched off. Dates, PRNG,
|
|
//! hashing, ZIP writing, and the psychometrics are implemented here rather than
|
|
//! pulled in — see [`date`], [`rng`], [`hash`], [`zipfile`], [`irt`]. For a tool
|
|
//! whose job is to still open a course repository in five years, that tradeoff
|
|
//! favours fewer moving parts.
|
|
//!
|
|
//! ## Where to start
|
|
//!
|
|
//! This page describes the shape of the crate. For a walkthrough, [`guide`] holds
|
|
//! setup, authoring, and tutorials, starting with [`guide::setup`].
|
|
|
|
#![warn(missing_docs)]
|
|
#![forbid(unsafe_code)]
|
|
// A broken link in a tutorial is a silent lie about the API, so it fails the build
|
|
// rather than warning into a log nobody reads.
|
|
#![deny(rustdoc::broken_intra_doc_links)]
|
|
#![warn(rustdoc::invalid_codeblock_attributes)]
|
|
#![warn(rustdoc::invalid_html_tags)]
|
|
#![warn(rustdoc::bare_urls)]
|
|
#![warn(rustdoc::private_intra_doc_links)]
|
|
// Lets `cargo doc` mark feature-gated items with the feature that enables them, on
|
|
// a nightly toolchain or on docs.rs. Ignored elsewhere.
|
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
|
|
pub mod analysis;
|
|
pub mod authoring;
|
|
pub mod data;
|
|
pub mod error;
|
|
pub mod export;
|
|
pub mod guide;
|
|
pub mod model;
|
|
pub mod util;
|
|
|
|
pub use util::{date, hash, markup, rng, yaml, zipfile};
|
|
|
|
pub use model::{assessment, bank, catalog, course, item, taxonomy};
|
|
|
|
pub use authoring::{jsonschema, lint, select};
|
|
|
|
#[cfg(feature = "parquet")]
|
|
pub use data::store_parquet;
|
|
pub use data::{canvas, gradescope, responses, store};
|
|
|
|
pub use analysis::{calibrate, classical, irt, students};
|
|
|
|
pub use export::{qti, report, typst};
|
|
|
|
pub use catalog::Catalog;
|
|
pub use course::{CourseFile, Layout, SCHEMA_VERSION};
|
|
pub use error::{Error, Result};
|
|
pub use item::Item;
|
|
pub use taxonomy::{CognitiveProcess, ErrorType, Flag, Format, Level, Status};
|
|
|
|
/// Version of package.
|
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|