Skip to content
Merged
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
26 changes: 23 additions & 3 deletions bindings/python/tests/test_codelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def test_validate_snomed(self):
codelist.validate_codes()
self.assertIn("Code 11 is an invalid length for type SNOMED", str(e.exception))

def test_truncate_icd10_to_3_digits(self):
def test_truncate_icd10_to_3_digits_first(self):
codelist = CodeList(
name="Test Codelist",
codelist_type="ICD10",
Expand All @@ -227,7 +227,27 @@ def test_truncate_icd10_to_3_digits(self):
self.assertEqual(len(codelist.entries()), 3)
self.assertIn(("A01", "Typhoid fever, intestinal", None), codelist.entries())
self.assertIn(("A02", "Salmonella infections", None), codelist.entries())
self.assertIn(("A03", "Random infections, unspecified", "A0311 truncated to 3 digits"), codelist.entries())
self.assertIn(("A03", "Random infections, unspecified", "A0311 truncated to 3 digits, term first encountered"), codelist.entries())

def test_truncate_icd10_to_3_digits_drop_term(self):
codelist = CodeList(
name="Test Codelist",
codelist_type="ICD10",
source="Manually created",
)
# codelist of various lengths
codelist.add_entry("A01.1", "Typhoid fever, intestinal more complex")
codelist.add_entry("A01", "Typhoid fever, intestinal")
codelist.add_entry("A02", "Salmonella infections")
codelist.add_entry("A0311", "Random infections, unspecified")

codelist.truncate_to_3_digits(term_management="drop_term")

## May be in different order so test entries to account for that
self.assertEqual(len(codelist.entries()), 3)
self.assertIn(("A01", "Typhoid fever, intestinal", None), codelist.entries())
self.assertIn(("A02", "Salmonella infections", None), codelist.entries())
self.assertIn(("A03", None, "Truncated to 3 digits, term discarded"), codelist.entries())

def test_invalid_term_management_arg_for_truncate(self):
codelist = CodeList(
Expand All @@ -238,7 +258,7 @@ def test_invalid_term_management_arg_for_truncate(self):
codelist.add_entry("A01.1", "Typhoid fever, intestinal more complex")
with self.assertRaises(ValueError) as e:
codelist.truncate_to_3_digits(term_management="invalid")
self.assertEqual(str(e.exception), "invalid is not known. Valid values are 'first'")
self.assertEqual(str(e.exception), "invalid is not known. Valid values are 'first', 'drop_term'")

def test_cannot_truncate_snomed(self):
codelist = CodeList(
Expand Down
82 changes: 76 additions & 6 deletions rust/codelist-rs/src/codelist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,12 +412,19 @@ impl CodeList {

// The term and comment that goes with it to make the
// entry depends on the term_management
let comment = match term_management {
TermManagement::First => Some(format!("{code} truncated to 3 digits")),
let (term, comment) = match term_management {
TermManagement::DropTerm => {
(None, Some("Truncated to 3 digits, term discarded".to_string()))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks good, with clear comments for each term management

}

TermManagement::First => (
term.clone(),
Some(format!("{code} truncated to 3 digits, term first encountered")),
),
};

// We'll add this one later
adds.push((truncated_code, term.clone(), comment));
adds.push((truncated_code, term, comment));
}

// Add the new three-digit codes
Expand Down Expand Up @@ -479,6 +486,7 @@ impl CodeList {

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum TermManagement {
DropTerm,
First,
}

Expand All @@ -488,6 +496,7 @@ impl FromStr for TermManagement {
/// Map TermManagement from a string
fn from_str(s: &str) -> Result<Self, CodeListError> {
match s.to_lowercase().as_str() {
"drop_term" => Ok(TermManagement::DropTerm),
"first" => Ok(TermManagement::First),
_ => Err(CodeListError::TermManagementNotKnown { term_management: s.to_string() }),
}
Expand Down Expand Up @@ -989,15 +998,76 @@ mod tests {
}

#[test]
fn test_truncate_to_3_digits_icd10_4_digits() -> Result<(), CodeListError> {
fn test_truncate_to_3_digits_icd10_4_digits_drop_term() -> Result<(), CodeListError> {
let metadata: Metadata = Default::default();

let mut expected_codelist =
CodeList::new("test_codelist".to_string(), CodeListType::ICD10, metadata.clone(), None);
expected_codelist.add_entry(
"B01".to_string(),
None,
Some("Truncated to 3 digits, term discarded".to_string()),
)?;

let mut observed_codelist =
CodeList::new("test_codelist".to_string(), CodeListType::ICD10, metadata, None);

observed_codelist.add_entry(
"B012".to_string(),
Some("Varicella pneumonia".to_string()),
None,
)?;

observed_codelist.truncate_to_3_digits(TermManagement::DropTerm)?;

assert_eq!(observed_codelist, expected_codelist);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice clear tests, I like how you set it up as observed vs expected codelist, it's very easy to read


Ok(())
}

#[test]
fn test_truncate_to_3_digits_3_and_4_digits_drop_term() -> Result<(), CodeListError> {
let metadata: Metadata = Default::default();

let mut expected_codelist =
CodeList::new("test_codelist".to_string(), CodeListType::ICD10, metadata.clone(), None);
expected_codelist.add_entry(
"B01".to_string(),
Some("Varicella [chickenpox]".to_string()),
None,
)?;

let mut observed_codelist =
CodeList::new("test_codelist".to_string(), CodeListType::ICD10, metadata, None);

observed_codelist.add_entry(
"B01".to_string(),
Some("Varicella [chickenpox]".to_string()),
None,
)?;
observed_codelist.add_entry(
"B012".to_string(),
Some("Varicella pneumonia".to_string()),
None,
)?;

observed_codelist.truncate_to_3_digits(TermManagement::DropTerm)?;

assert_eq!(observed_codelist, expected_codelist);

Ok(())
}

#[test]
fn test_truncate_to_3_digits_icd10_4_digits_first() -> Result<(), CodeListError> {
let metadata: Metadata = Default::default();

let mut expected_codelist =
CodeList::new("test_codelist".to_string(), CodeListType::ICD10, metadata.clone(), None);
expected_codelist.add_entry(
"B01".to_string(),
Some("Varicella pneumonia".to_string()),
Some("B012 truncated to 3 digits".to_string()),
Some("B012 truncated to 3 digits, term first encountered".to_string()),
)?;

let mut observed_codelist =
Expand All @@ -1017,7 +1087,7 @@ mod tests {
}

#[test]
fn test_truncate_to_3_digits_3_and_4_digits() -> Result<(), CodeListError> {
fn test_truncate_to_3_digits_3_and_4_digits_first() -> Result<(), CodeListError> {
let metadata: Metadata = Default::default();

let mut expected_codelist =
Expand Down
2 changes: 1 addition & 1 deletion rust/codelist-rs/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ pub enum CodeListError {
#[error("{codelist_type} cannot be truncated to 3 digits.")]
CodeListNotTruncatable { codelist_type: String },

#[error("{term_management} is not known. Valid values are 'first'")]
#[error("{term_management} is not known. Valid values are 'first', 'drop_term'")]
TermManagementNotKnown { term_management: String },

#[error("{codelist_type} cannot be transformed by having X added to the end of it")]
Expand Down
Loading