From 4450c01a17da96bf0ebd911363c9804bcc31f051 Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Sat, 21 Jun 2025 15:08:15 +0200 Subject: [PATCH] feat: add tumor staging mapping --- .../mapper/KpaTumorausbreitungDataMapper.java | 118 ++++++++++++++++++ .../KpaTumorausbreitungDataMapperTest.java | 87 +++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 src/main/java/dev/pcvolkmer/onco/datamapper/mapper/KpaTumorausbreitungDataMapper.java create mode 100644 src/test/java/dev/pcvolkmer/onco/datamapper/mapper/KpaTumorausbreitungDataMapperTest.java diff --git a/src/main/java/dev/pcvolkmer/onco/datamapper/mapper/KpaTumorausbreitungDataMapper.java b/src/main/java/dev/pcvolkmer/onco/datamapper/mapper/KpaTumorausbreitungDataMapper.java new file mode 100644 index 0000000..30502f7 --- /dev/null +++ b/src/main/java/dev/pcvolkmer/onco/datamapper/mapper/KpaTumorausbreitungDataMapper.java @@ -0,0 +1,118 @@ +package dev.pcvolkmer.onco.datamapper.mapper; + +import dev.pcvolkmer.mv64e.mtb.*; +import dev.pcvolkmer.onco.datamapper.ResultSet; +import dev.pcvolkmer.onco.datamapper.datacatalogues.TumorausbreitungCatalogue; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Mapper class to load and map prozedur data from database table 'dk_dnpm_uf_tumorausbreitung' + * + * @author Paul-Christian Volkmer + * @since 0.1 + */ +public class KpaTumorausbreitungDataMapper implements SubformDataMapper { + + private final TumorausbreitungCatalogue catalogue; + + public KpaTumorausbreitungDataMapper(final TumorausbreitungCatalogue catalogue) { + this.catalogue = catalogue; + } + + /** + * Loads and maps Prozedur related by database id + * + * @param id The database id of the procedure data set + * @return The loaded data set + */ + @Override + public TumorStaging getById(final int id) { + var data = catalogue.getById(id); + return this.map(data); + } + + @Override + public List getByParentId(final int parentId) { + return catalogue.getAllByParentId(parentId) + .stream() + .map(this::map) + .sorted(Comparator.comparing(TumorStaging::getDate)) + .collect(Collectors.toList()); + } + + private TumorStaging map(final ResultSet resultSet) { + var diseases = catalogue.getDiseases(resultSet.getProcedureId()); + + if (diseases.size() != 1) { + throw new IllegalStateException(String.format("No unique disease for procedure %s", resultSet.getProcedureId())); + } + + var builder = TumorStaging.builder(); + builder + .date(resultSet.getDate("zeitpunkt")) + .method(getTumorStagingMethodCoding(resultSet.getString("typ"))) + .otherClassifications(List.of(Coding.builder().code(resultSet.getString("wert")).build())) + .tnmClassification(getTnmClassification(resultSet)) + ; + + + return builder.build(); + } + + private TumorStagingMethodCoding getTumorStagingMethodCoding(final String value) { + if (value == null || !Arrays.stream(TumorStagingMethodCodingCode.values()).map(TumorStagingMethodCodingCode::toValue).collect(Collectors.toSet()).contains(value)) { + return null; + } + + var resultBuilder = TumorStagingMethodCoding.builder(); + try { + resultBuilder.code(TumorStagingMethodCodingCode.forValue(value)); + } catch (IOException e) { + throw new IllegalStateException("No valid code found"); + } + + return resultBuilder.build(); + } + + private TnmClassification getTnmClassification(final ResultSet resultSet) { + var tnpmClassificationBuilder = TnmClassification.builder(); + + var hasContent = false; + + var tnmt = resultSet.getString("tnmt"); + if (tnmt != null && !tnmt.isBlank()) { + tnpmClassificationBuilder.tumor( + Coding.builder().code(String.format("%s%s", resultSet.getString("tnmtprefix"), tnmt)).build() + ); + hasContent = true; + } + + var tnmn = resultSet.getString("tnmn"); + if (tnmn != null && !tnmn.isBlank()) { + tnpmClassificationBuilder.nodes( + Coding.builder().code(String.format("%s%s", resultSet.getString("tnmnprefix"), tnmn)).build() + ); + hasContent = true; + } + + var tnmm = resultSet.getString("tnmm"); + if (tnmm != null && !tnmm.isBlank()) { + tnpmClassificationBuilder.metastasis( + Coding.builder().code(String.format("%s%s", resultSet.getString("tnmmprefix"), tnmm)).build() + ); + hasContent = true; + } + + if (hasContent) { + return tnpmClassificationBuilder.build(); + } + + return null; + } + +} diff --git a/src/test/java/dev/pcvolkmer/onco/datamapper/mapper/KpaTumorausbreitungDataMapperTest.java b/src/test/java/dev/pcvolkmer/onco/datamapper/mapper/KpaTumorausbreitungDataMapperTest.java new file mode 100644 index 0000000..2da9fc7 --- /dev/null +++ b/src/test/java/dev/pcvolkmer/onco/datamapper/mapper/KpaTumorausbreitungDataMapperTest.java @@ -0,0 +1,87 @@ +package dev.pcvolkmer.onco.datamapper.mapper; + +import dev.pcvolkmer.mv64e.mtb.TumorStaging; +import dev.pcvolkmer.mv64e.mtb.TumorStagingMethodCoding; +import dev.pcvolkmer.mv64e.mtb.TumorStagingMethodCodingCode; +import dev.pcvolkmer.onco.datamapper.ResultSet; +import dev.pcvolkmer.onco.datamapper.datacatalogues.TumorausbreitungCatalogue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class KpaTumorausbreitungDataMapperTest { + + TumorausbreitungCatalogue catalogue; + + KpaTumorausbreitungDataMapper dataMapper; + + @BeforeEach + void setUp(@Mock TumorausbreitungCatalogue catalogue) { + this.catalogue = catalogue; + this.dataMapper = new KpaTumorausbreitungDataMapper(catalogue); + } + + @Test + void shouldMapResultSet(@Mock ResultSet resultSet) { + var testData = Map.of( + "id", "1", + "zeitpunkt", new java.sql.Date(Date.from(Instant.parse("2000-01-01T12:00:00Z")).getTime()), + "typ", "pathologic", + "wert", "tumor-free", + "tnmtprefix", "p", + "tnmt", "0", + "tnmnprefix", "p", + "tnmn", "0", + "tnmmprefix", "p", + "tnmm", "0" + ); + + doAnswer(invocationOnMock -> { + var columnName = invocationOnMock.getArgument(0, String.class); + return testData.get(columnName); + }).when(resultSet).getString(anyString()); + + doAnswer(invocationOnMock -> { + var columnName = invocationOnMock.getArgument(0, String.class); + return testData.get(columnName); + }).when(resultSet).getDate(anyString()); + + when(resultSet.getProcedureId()).thenReturn(1); + + doAnswer(invocationOnMock -> List.of(resultSet)) + .when(catalogue) + .getAllByParentId(anyInt()); + + doAnswer(invocationOnMock -> List.of(resultSet)) + .when(catalogue) + .getDiseases(anyInt()); + + var actualList = this.dataMapper.getByParentId(1); + assertThat(actualList).hasSize(1); + + var actual = actualList.get(0); + assertThat(actual).isInstanceOf(TumorStaging.class); + assertThat(actual.getDate()).isEqualTo(new java.sql.Date(Date.from(Instant.parse("2000-01-01T12:00:00Z")).getTime())); + assertThat(actual.getMethod()).isEqualTo(TumorStagingMethodCoding.builder().code(TumorStagingMethodCodingCode.PATHOLOGIC).build()); + assertThat(actual.getOtherClassifications()).hasSize(1); + assertThat(actual.getOtherClassifications().get(0).getCode()).isEqualTo("tumor-free"); + assertThat(actual.getTnmClassification().getTumor().getCode()).isEqualTo("p0"); + assertThat(actual.getTnmClassification().getNodes().getCode()).isEqualTo("p0"); + assertThat(actual.getTnmClassification().getMetastasis().getCode()).isEqualTo("p0"); + } + +}