wsi-annotation-tests 0.1.3

DICOM Annotation Test Suite for WSI ANN, SEG, SR, and Parametric Map
Documentation
use std::path::Path;

use serde_json::{Value, json};

use crate::conversion_matrix::{ConversionMatrixResult, run_conversion_matrices};
use crate::matrix::run_core_matrix;
use crate::probe::ViewerProbe;
use crate::shim::{FixtureSet, ReferenceShim};

use super::tagged;

pub(super) const DEFINITION_VERSION: u32 = 1;

#[derive(Debug, Clone, Copy)]
pub(super) struct BaselineStatus {
    matrix: bool,
    conversion: bool,
}

impl BaselineStatus {
    pub(super) const fn new(matrix: bool, conversion: bool) -> Self {
        Self { matrix, conversion }
    }

    pub(super) const fn is_ok(self) -> bool {
        self.matrix && self.conversion
    }
}

pub(super) struct BaselineRun {
    pub(super) fixtures: FixtureSet,
    pub(super) conversion: ConversionMatrixResult,
    pub(super) observations: Vec<Value>,
    pub(super) status: BaselineStatus,
}

pub(super) fn run(
    reference: &ReferenceShim,
    probe: &ViewerProbe,
    run_directory: &Path,
) -> Result<BaselineRun, String> {
    let fixtures = reference
        .generate_core(&run_directory.join("fixtures"))
        .map_err(|error| error.to_string())?;
    let matrix = run_core_matrix(
        &fixtures,
        reference,
        probe,
        &run_directory.join("roundtrips"),
    )?;
    let conversion = run_conversion_matrices(
        &fixtures,
        reference,
        probe,
        &run_directory.join("conversions"),
    )?;
    let status = BaselineStatus::new(matrix.is_ok(), conversion.is_ok());
    let mut observations = matrix
        .observations
        .iter()
        .map(|observation| tagged("matrix", observation))
        .collect::<Result<Vec<_>, _>>()?;
    observations.extend(
        conversion
            .observations
            .iter()
            .map(|observation| tagged("conversion", observation))
            .collect::<Result<Vec<_>, _>>()?,
    );
    observations.push(qualification_observation(reference, &fixtures)?);
    Ok(BaselineRun {
        fixtures,
        conversion,
        observations,
        status,
    })
}

fn qualification_observation(
    reference: &ReferenceShim,
    fixtures: &FixtureSet,
) -> Result<Value, String> {
    let qualification = reference
        .qualify_pydcm(
            &fixtures.source,
            &fixtures.ann["2D_VOLUME"],
            &fixtures.seg["BINARY"],
        )
        .map_err(|error| error.to_string())?;
    let mut observation = tagged("qualification", &qualification)?;
    observation["case_id"] = json!("pydcm-qualification");
    observation["status"] = json!(if qualification.qualified {
        "qualified"
    } else {
        "unqualified"
    });
    Ok(observation)
}

#[cfg(test)]
mod tests {
    use super::BaselineStatus;

    #[test]
    fn status_requires_matrix_and_conversion() {
        assert!(BaselineStatus::new(true, true).is_ok());
        assert!(!BaselineStatus::new(false, true).is_ok());
        assert!(!BaselineStatus::new(true, false).is_ok());
    }
}