Skip to content

Commit

Permalink
feat: adds library source material to sample
Browse files Browse the repository at this point in the history
This is a squashed commit of the following commits from @grugna:

* add Library Source Material - CDE 14808227
* adds PR link
* typos
* typos
* typos
* adding missing documentation
* fmt format
* resolve comments
* Update swagger.yml
* Update CHANGELOG.md
* Update packages/ccdi-models/src/sample/metadata.rs
* Update metadata.rs
* remove value 'Other'
* update to new CDE 15235975

Co-authored-by: e-t-k <[email protected]>
Co-authored-by: claymcleod <[email protected]>
  • Loading branch information
3 people committed Nov 6, 2024
1 parent 8b0bbd5 commit 276b463
Show file tree
Hide file tree
Showing 12 changed files with 358 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Versioning](https://semver.org/spec/v2.0.0.html).
their responses ([link to
discussion](https://github.com/CBIIT/ccdi-federation-api/discussions/79),
[#95](https://github.com/CBIIT/ccdi-federation-api/pull/95)).
- Adds Library Source Material ([link to
discussion](https://github.com/CBIIT/ccdi-federation-api/discussions/119),
[#118](https://github.com/CBIIT/ccdi-federation-api/pull/118))

### Revised

Expand Down
2 changes: 2 additions & 0 deletions packages/ccdi-cde/src/v1/sample.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
//! sample.

mod disease_phase;
mod library_source_material;
mod library_strategy;
mod tumor_classification;
mod tumor_tissue_morphology;

pub use disease_phase::DiseasePhase;
pub use library_source_material::LibrarySourceMaterial;
pub use library_strategy::LibraryStrategy;
pub use tumor_classification::TumorClassification;
pub use tumor_tissue_morphology::TumorTissueMorphology;
164 changes: 164 additions & 0 deletions packages/ccdi-cde/src/v1/sample/library_source_material.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use introspect::Introspect;
use rand::distributions::Standard;
use rand::prelude::Distribution;
use serde::Deserialize;
use serde::Serialize;
use utoipa::ToSchema;

use crate::CDE;

/// **`caDSR CDE 15235975 v1.00`**
///
/// This metadata element is defined by the caDSR as "TThe cell or cellular component that makes up the sample that has been prepared for testing or research purposes.".
///
/// Link:
/// <https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=15235975%20and%20ver_nr=1>
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, ToSchema, Introspect)]
#[schema(as = cde::v1::sample::LibrarySourceMaterial)]
pub enum LibrarySourceMaterial {
/// `Bulk Cells`
///
/// * **VM Long Name**: Bulk Cell Specimen
/// * **VM Public ID**: 7592130
/// * **Concept Code**: C178223
/// * **Begin Date**: 03/12/2024
///
/// A biospecimen consisting of multiple cells intended to be analyzed as a pool.
#[serde(rename = "Bulk Cells")]
BulkCells,

/// `Bulk Nuclei`
///
/// * **VM Long Name**: Bulk Nucleus Specimen
/// * **VM Public ID**: 7592129
/// * **Concept Code**: C178224
/// * **Begin Date**: 02/28/2024
///
/// A biospecimen consisting of multiple nuclei intended to be analyzed as a pool.
#[serde(rename = "Bulk Nuclei")]
BulkNuclei,

/// `Bulk Tissue`
///
/// * **VM Long Name**: Bulk Tissue Specimen
/// * **VM Public ID**: 7592128
/// * **Concept Code**: C178225
/// * **Begin Date**: 02/28/2024
///
/// A biospecimen either derived from a whole tissue specimen or tissue section, which may consist of heterogeneous cells or tissues.
#[serde(rename = "Bulk Tissue")]
BulkTissue,

/// `Single-cells`
///
/// * **VM Long Name**: Single Cell Suspension
/// * **VM Public ID**: 14838800
/// * **Concept Code**: C204464
/// * **Begin Date**: 03/12/2024
///
/// A dilute suspension of cells intended to be further fractionated for assays focused on single-cells.
#[serde(rename = "Single-cells")]
SingleCells,

/// `Single-nuclei`
///
/// * **VM Long Name**: Single Nucleus Suspension
/// * **VM Public ID**: 14838802
/// * **Concept Code**: C204465
/// * **Begin Date**: 03/12/2024
///
/// A dilute suspension comprised of isolated intact cell nuclei intended to be further fractionated for assays focused on single-nuclei.
#[serde(rename = "Single-nuclei")]
SingleNuclei,

/// `Not Reported`
///
/// * **VM Long Name**: Not Reported
/// * **VM Public ID**: 5612322
/// * **Concept Code**: C43234
/// * **Begin Date**: 03/01/2024
///
/// Not provided or available.
#[serde(rename = "Not Reported")]
NotReported,
}

impl CDE for LibrarySourceMaterial {}

impl std::fmt::Display for LibrarySourceMaterial {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LibrarySourceMaterial::BulkCells => write!(f, "Bulk Cells"),
LibrarySourceMaterial::BulkNuclei => write!(f, "Bulk Nuclei"),
LibrarySourceMaterial::BulkTissue => write!(f, "Bulk Tissue"),
LibrarySourceMaterial::SingleCells => write!(f, "Single-cells"),
LibrarySourceMaterial::SingleNuclei => write!(f, "Single-nuclei"),
LibrarySourceMaterial::NotReported => write!(f, "Not Reported"),
}
}
}

impl Distribution<LibrarySourceMaterial> for Standard {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> LibrarySourceMaterial {
match rng.gen_range(0..5) {
0 => LibrarySourceMaterial::BulkCells,
1 => LibrarySourceMaterial::BulkNuclei,
2 => LibrarySourceMaterial::BulkTissue,
3 => LibrarySourceMaterial::SingleCells,
4 => LibrarySourceMaterial::SingleNuclei,
_ => LibrarySourceMaterial::NotReported,
}
}
}

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

#[test]
fn it_converts_to_string_correctly() {
assert_eq!(LibrarySourceMaterial::BulkCells.to_string(), "Bulk Cells");
assert_eq!(LibrarySourceMaterial::BulkNuclei.to_string(), "Bulk Nuclei");
assert_eq!(LibrarySourceMaterial::BulkTissue.to_string(), "Bulk Tissue");
assert_eq!(
LibrarySourceMaterial::SingleCells.to_string(),
"Single-cells"
);
assert_eq!(
LibrarySourceMaterial::SingleNuclei.to_string(),
"Single-nuclei"
);
assert_eq!(
LibrarySourceMaterial::NotReported.to_string(),
"Not Reported"
);
}

#[test]
fn it_serializes_to_json_correctly() {
assert_eq!(
serde_json::to_string(&LibrarySourceMaterial::BulkCells).unwrap(),
"\"Bulk Cells\""
);
assert_eq!(
serde_json::to_string(&LibrarySourceMaterial::BulkNuclei).unwrap(),
"\"Bulk Nuclei\""
);
assert_eq!(
serde_json::to_string(&LibrarySourceMaterial::BulkTissue).unwrap(),
"\"Bulk Tissue\""
);
assert_eq!(
serde_json::to_string(&LibrarySourceMaterial::SingleCells).unwrap(),
"\"Single-cells\""
);
assert_eq!(
serde_json::to_string(&LibrarySourceMaterial::SingleNuclei).unwrap(),
"\"Single-nuclei\""
);
assert_eq!(
serde_json::to_string(&LibrarySourceMaterial::NotReported).unwrap(),
"\"Not Reported\""
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub fn get_field_descriptions() -> Vec<description::Description> {
crate::sample::metadata::Diagnosis::description(),
cde::v1::sample::DiseasePhase::description(),
cde::v1::sample::LibraryStrategy::description(),
cde::v1::sample::LibrarySourceMaterial::description(),
cde::v2::sample::PreservationMethod::description(),
cde::v2::sample::TissueType::description(),
cde::v1::sample::TumorClassification::description(),
Expand Down Expand Up @@ -106,6 +107,28 @@ impl description::r#trait::Description for cde::v1::sample::LibraryStrategy {
}
}

impl description::r#trait::Description for cde::v1::sample::LibrarySourceMaterial {
fn description() -> description::Description {
// SAFETY: these two unwraps are tested statically below in the test
// that constructs the description using `get_fields()`.
let entity = Self::entity().unwrap();
let members = Self::members().map(|member| member.unwrap());

description::Description::Harmonized(Harmonized::new(
Kind::Enum,
String::from("library_source_material"),
entity.description().to_string(),
"https://github.com/CBIIT/ccdi-federation-api/wiki/Sample-Metadata-Fields#library_source_material"
.parse::<Url>().unwrap(),
Some(Standard::new(
entity.standard_name().to_string(),
crate::Url::from(entity.standard_url().clone()),
)),
members,
))
}
}

impl description::r#trait::Description for cde::v2::sample::PreservationMethod {
fn description() -> description::Description {
// SAFETY: these two unwraps are tested statically below in the test
Expand Down
9 changes: 9 additions & 0 deletions packages/ccdi-models/src/metadata/field/unowned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,15 @@ pub mod sample {
ccdi_cde as cde
);

unowned_field!(
LibrarySourceMaterial,
field::unowned::sample::LibrarySourceMaterial,
cde::v1::sample::LibrarySourceMaterial,
cde::v1::sample::LibrarySourceMaterial,
cde::v1::sample::LibrarySourceMaterial::NotReported,
ccdi_cde as cde
);

unowned_field!(
PreservationMethod,
field::unowned::sample::PreservationMethod,
Expand Down
45 changes: 43 additions & 2 deletions packages/ccdi-models/src/sample/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ pub struct Metadata {
#[schema(value_type = field::unowned::sample::AgeAtCollection, nullable = true)]
age_at_collection: Option<field::unowned::sample::AgeAtCollection>,

/// The strategy for constructing the sequencing library.
/// The library source material.
#[schema(value_type = field::unowned::sample::LibraryStrategy, nullable = true)]
library_strategy: Option<field::unowned::sample::LibraryStrategy>,

/// The strategy for constructing the sequencing library.
#[schema(value_type = field::unowned::sample::LibrarySourceMaterial, nullable = true)]
library_source_material: Option<field::unowned::sample::LibrarySourceMaterial>,

/// The method used to maintain the sample or biospecimen in a viable state.
#[schema(value_type = field::unowned::sample::PreservationMethod, nullable = true)]
preservation_method: Option<field::unowned::sample::PreservationMethod>,
Expand Down Expand Up @@ -209,6 +213,42 @@ impl Metadata {
self.library_strategy.as_ref()
}

/// Gets the harmonized library source material for the [`Metadata`].
///
/// # Examples
///
/// ```
/// use ccdi_cde as cde;
/// use ccdi_models as models;
///
/// use models::metadata::field::unowned::sample::LibrarySourceMaterial;
/// use models::sample::metadata::Builder;
///
/// let metadata = Builder::default()
/// .library_source_material(LibrarySourceMaterial::new(
/// cde::v1::sample::LibrarySourceMaterial::BulkCells,
/// None,
/// None,
/// None,
/// ))
/// .build();
///
/// assert_eq!(
/// metadata.library_source_material(),
/// Some(&LibrarySourceMaterial::new(
/// cde::v1::sample::LibrarySourceMaterial::BulkCells,
/// None,
/// None,
/// None,
/// ))
/// );
/// ```
pub fn library_source_material(
&self,
) -> Option<&field::unowned::sample::LibrarySourceMaterial> {
self.library_source_material.as_ref()
}

/// Gets the harmonized preservation method for the [`Metadata`].
///
/// # Examples
Expand Down Expand Up @@ -565,6 +605,7 @@ impl Metadata {
)),
disease_phase: rand::random(),
library_strategy: rand::random(),
library_source_material: rand::random(),
preservation_method: rand::random(),
tissue_type: rand::random(),
tumor_classification: rand::random(),
Expand Down Expand Up @@ -624,7 +665,7 @@ mod tests {
let metadata = builder::Builder::default().build();
assert_eq!(
&serde_json::to_string(&metadata).unwrap(),
"{\"age_at_diagnosis\":null,\"diagnosis\":null,\"disease_phase\":null,\"tissue_type\":null,\"tumor_classification\":null,\"tumor_tissue_morphology\":null,\"age_at_collection\":null,\"library_strategy\":null,\"preservation_method\":null,\"identifiers\":null,\"depositions\":null}"
"{\"age_at_diagnosis\":null,\"diagnosis\":null,\"disease_phase\":null,\"tissue_type\":null,\"tumor_classification\":null,\"tumor_tissue_morphology\":null,\"age_at_collection\":null,\"library_strategy\":null,\"library_source_material\":null,\"preservation_method\":null,\"identifiers\":null,\"depositions\":null}"
);
}
}
30 changes: 30 additions & 0 deletions packages/ccdi-models/src/sample/metadata/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ pub struct Builder {
/// The strategy for constructing the sequencing library.
library_strategy: Option<field::unowned::sample::LibraryStrategy>,

/// The library source material.
library_source_material: Option<field::unowned::sample::LibrarySourceMaterial>,

/// The preservation method for this sample or biospecimen.
preservation_method: Option<field::unowned::sample::PreservationMethod>,

Expand Down Expand Up @@ -231,6 +234,32 @@ impl Builder {
self
}

/// Sets the `library_source_material` field of the [`Builder`].
/// # Examples
///
/// ```
/// use ccdi_cde as cde;
/// use ccdi_models as models;
///
/// use models::metadata::field::unowned::sample::LibrarySourceMaterial;
/// use models::sample::metadata::Builder;
///
/// let field = LibrarySourceMaterial::new(
/// cde::v1::sample::LibrarySourceMaterial::BulkCells,
/// None,
/// None,
/// None,
/// );
/// let builder = Builder::default().library_source_material(field);
/// ```
pub fn library_source_material(
mut self,
field: field::unowned::sample::LibrarySourceMaterial,
) -> Self {
self.library_source_material = Some(field);
self
}

/// Sets the `preservation_method` field of the [`Builder`].
///
/// # Examples
Expand Down Expand Up @@ -408,6 +437,7 @@ impl Builder {
diagnosis: self.diagnosis,
disease_phase: self.disease_phase,
library_strategy: self.library_strategy,
library_source_material: self.library_source_material,
preservation_method: self.preservation_method,
tissue_type: self.tissue_type,
tumor_classification: self.tumor_classification,
Expand Down
2 changes: 2 additions & 0 deletions packages/ccdi-openapi/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ use utoipa::openapi;
models::sample::metadata::Diagnosis,
cde::v1::sample::DiseasePhase,
cde::v1::sample::LibraryStrategy,
cde::v1::sample::LibrarySourceMaterial,
cde::v2::sample::PreservationMethod,
cde::v2::sample::TissueType,
cde::v1::sample::TumorClassification,
Expand Down Expand Up @@ -168,6 +169,7 @@ use utoipa::openapi;
field::unowned::sample::Diagnosis,
field::unowned::sample::DiseasePhase,
field::unowned::sample::LibraryStrategy,
field::unowned::sample::LibrarySourceMaterial,
field::unowned::sample::PreservationMethod,
field::unowned::sample::TissueType,
field::unowned::sample::TumorClassification,
Expand Down
Loading

0 comments on commit 276b463

Please sign in to comment.