134 lines
4.0 KiB
Rust
134 lines
4.0 KiB
Rust
// SPDX-License-Identifier: Prosperity-3.0.0
|
|
// Copyright Scientific Computing Studio
|
|
// Source: https://git.scient.ing/education/coursebank
|
|
|
|
//! The on-disk layout of a course directory.
|
|
//!
|
|
//! Two of these directories hold fragments of the course file rather than files
|
|
//! of their own kind: `lectures/` and `objectives/` are merged into one
|
|
//! [`crate::course::CourseFile`] on load, along with `references.yaml`. See
|
|
//! [`crate::course::fragment`].
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use crate::course::COURSE_FILE;
|
|
use crate::error::{Error, Result};
|
|
|
|
/// The standard directory layout of a course, resolved from a root.
|
|
#[derive(Debug, Clone)]
|
|
pub struct Layout {
|
|
/// The course root.
|
|
pub root: PathBuf,
|
|
}
|
|
|
|
impl Layout {
|
|
/// Builds a layout from a course root directory.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `root` - the course directory.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// The layout.
|
|
pub fn new(root: impl Into<PathBuf>) -> Layout {
|
|
Layout { root: root.into() }
|
|
}
|
|
|
|
/// Path to `course.yaml`.
|
|
pub fn course_file(&self) -> PathBuf {
|
|
self.root.join(COURSE_FILE)
|
|
}
|
|
|
|
/// Path to `references.yaml`, the bibliography when it is kept out of
|
|
/// `course.yaml`.
|
|
///
|
|
/// Optional: absent means the course keeps its `references:` section in the
|
|
/// course file, which is how an unsplit course is arranged.
|
|
pub fn references_file(&self) -> PathBuf {
|
|
self.root.join(crate::course::fragment::REFERENCES_FILE)
|
|
}
|
|
|
|
/// Directory holding one file per lecture.
|
|
pub fn lectures(&self) -> PathBuf {
|
|
self.root.join("lectures")
|
|
}
|
|
|
|
/// Directory holding one file per learning objective.
|
|
pub fn objectives(&self) -> PathBuf {
|
|
self.root.join("objectives")
|
|
}
|
|
|
|
/// Directory holding item bank YAML files.
|
|
pub fn banks(&self) -> PathBuf {
|
|
self.root.join("banks")
|
|
}
|
|
|
|
/// Directory holding assessment records.
|
|
pub fn assessments(&self) -> PathBuf {
|
|
self.root.join("assessments")
|
|
}
|
|
|
|
/// Directory holding response tables and derived statistics.
|
|
pub fn data(&self) -> PathBuf {
|
|
self.root.join("data")
|
|
}
|
|
|
|
/// Directory holding generated reports.
|
|
pub fn reports(&self) -> PathBuf {
|
|
self.root.join("reports")
|
|
}
|
|
|
|
/// Directory holding generated exports such as QTI packages.
|
|
pub fn build(&self) -> PathBuf {
|
|
self.root.join("build")
|
|
}
|
|
|
|
/// Directory holding emitted JSON Schema files for editor validation.
|
|
pub fn schema(&self) -> PathBuf {
|
|
self.root.join("schema")
|
|
}
|
|
|
|
/// Directory holding Typst export templates and their configuration.
|
|
///
|
|
/// Unlike the other directories, this one is *not* created by
|
|
/// [`Layout::create_all`]. Its absence is meaningful: a course with no
|
|
/// `templates/` directory uses the templates compiled into the binary, and
|
|
/// creating an empty one on `init` would suggest a customization step is
|
|
/// required when it is not. `coursebank template dump` creates it on demand.
|
|
pub fn templates(&self) -> PathBuf {
|
|
self.root.join("templates")
|
|
}
|
|
|
|
/// Directory holding sealed administrations.
|
|
///
|
|
/// Deliberately not `assessments/`:
|
|
/// [`crate::assessment::AssessmentFile::load_all`] parses every `.yaml` in
|
|
/// that directory, and a seal is not an assessment record.
|
|
pub fn seals(&self) -> PathBuf {
|
|
self.root.join(crate::seal::SEAL_DIR)
|
|
}
|
|
|
|
/// Creates every directory in the layout.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns [`Error::Io`] if a directory cannot be created.
|
|
pub fn create_all(&self) -> Result<()> {
|
|
for dir in [
|
|
self.root.clone(),
|
|
self.lectures(),
|
|
self.objectives(),
|
|
self.banks(),
|
|
self.assessments(),
|
|
self.data(),
|
|
self.reports(),
|
|
self.build(),
|
|
self.schema(),
|
|
] {
|
|
std::fs::create_dir_all(&dir).map_err(|e| Error::io(&dir, e))?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|