Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Library Source Material - CDE 14808227 #118

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/114))
[#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;
184 changes: 184 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,184 @@
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 14808227 v1.00`**
///
/// This metadata element is defined by the caDSR as "The cellular source of
/// the double stranded DNA fragments analyzed by high-throughput sequencing.".
///
/// Link:
/// <https://cadsr.cancer.gov/onedata/dmdirect/NIH/NCI/CO/CDEDD?filter=CDEDD.ITEM_ID=14808227%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`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be Bulk Nuclei (no dash).

///
/// * **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`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be Bulk Tissue (no dash).

///
/// * **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`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be Not Reported (no dash).

///
/// * **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")]
Copy link
Collaborator

@claymcleod claymcleod Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For all of these, please make sure that the permissible value exactly matches what is in the caDSR field: both for the comment above and for the serde rename.

NotReported,

/// `Other`
///
/// * **VM Long Name**: Other Library strategy
/// * **VM Public ID**: 6273371
/// * **Concept Code**: C17649
/// * **Begin Date**: 05/11/2018
///
/// Different than the one(s) previously specified or mentioned.
#[serde(rename = "Other")]
Other,
}

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"),
LibrarySourceMaterial::Other => write!(f, "Other"),
}
}
}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be 0..6 and just _ to map to Other.

}
}
}

#[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"
);
assert_eq!(LibrarySourceMaterial::Other.to_string(), "Other");
}

#[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\""
);
assert_eq!(
serde_json::to_string(&LibrarySourceMaterial::Other).unwrap(),
"\"Other\""
);
}
}
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::Other,
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,9 +56,13 @@ pub struct Metadata {
#[schema(value_type = field::unowned::sample::AgeAtCollection, nullable = true)]
age_at_collection: Option<field::unowned::sample::AgeAtCollection>,

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

/// The strategy for constructing the sequencing library.
#[schema(value_type = field::unowned::sample::LibraryStrategy, nullable = true)]
library_strategy: Option<field::unowned::sample::LibraryStrategy>,
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)]
Expand Down Expand Up @@ -209,6 +213,42 @@ impl Metadata {
self.library_strategy.as_ref()
}

/// Gets the harmonized library strategy 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
Loading
Loading