1
0
mirror of https://github.com/pcvolkmer/mv64e-onkostar-data.git synced 2025-09-13 07:52:52 +00:00

feat: add MSI mapping as far as possible

This commit is contained in:
2025-08-21 10:45:36 +02:00
parent 7a448c0f91
commit 09843349d9
9 changed files with 336 additions and 6 deletions

View File

@@ -140,6 +140,28 @@ public class ResultSet {
throw new IllegalArgumentException("Cannot convert " + raw.getClass() + " to Integer");
}
/**
* Get column value as Double and cast value if possible
*
* @param columnName The name of the column
* @return The column value as Integer
*/
public Double getDouble(String columnName) {
var raw = this.rawData.get(columnName);
if (raw == null) {
return null;
} else if (raw instanceof Integer) {
return ((Integer) raw).doubleValue();
} else if (raw instanceof Long) {
return ((Long) raw).doubleValue();
} else if (raw instanceof Double) {
return ((Double) raw);
}
throw new IllegalArgumentException("Cannot convert " + raw.getClass() + " to Integer");
}
/**
* Get column value as Date and cast value if possible
*

View File

@@ -95,6 +95,8 @@ public class DataCatalogueFactory {
return MolekulargenetikCatalogue.create(jdbcTemplate);
} else if (c == MolekulargenuntersuchungCatalogue.class) {
return MolekulargenuntersuchungCatalogue.create(jdbcTemplate);
} else if (c == MolekulargenMsiCatalogue.class) {
return MolekulargenMsiCatalogue.create(jdbcTemplate);
} else if (c == RebiopsieCatalogue.class) {
return RebiopsieCatalogue.create(jdbcTemplate);
} else if (c == ReevaluationCatalogue.class) {

View File

@@ -0,0 +1,46 @@
/*
* This file is part of mv64e-onkostar-data
*
* Copyright (C) 2025 Paul-Christian Volkmer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package dev.pcvolkmer.onco.datamapper.datacatalogues;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Load raw result sets from database table 'dk_molekluargenmsi'
*
* @author Paul-Christian Volkmer
* @since 0.1
*/
public class MolekulargenMsiCatalogue extends AbstractSubformDataCatalogue {
private MolekulargenMsiCatalogue(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
protected String getTableName() {
return "dk_molekluargenmsi";
}
public static MolekulargenMsiCatalogue create(JdbcTemplate jdbcTemplate) {
return new MolekulargenMsiCatalogue(jdbcTemplate);
}
}

View File

@@ -0,0 +1,113 @@
/*
* This file is part of mv64e-onkostar-data
*
* Copyright (C) 2025 Paul-Christian Volkmer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package dev.pcvolkmer.onco.datamapper.mapper;
import dev.pcvolkmer.mv64e.mtb.Msi;
import dev.pcvolkmer.mv64e.mtb.MsiMethodCoding;
import dev.pcvolkmer.mv64e.mtb.MsiMethodCodingCode;
import dev.pcvolkmer.mv64e.mtb.Reference;
import dev.pcvolkmer.onco.datamapper.ResultSet;
import dev.pcvolkmer.onco.datamapper.datacatalogues.MolekulargenMsiCatalogue;
/**
* Mapper class to load and map prozedur data from database table 'dk_molekluargenetik'
*
* @author Paul-Christian Volkmer
* @since 0.1
*/
public class KpaMolekulargenetikMsiDataMapper extends AbstractSubformDataMapper<Msi> {
public KpaMolekulargenetikMsiDataMapper(
final MolekulargenMsiCatalogue molekulargenMsiCatalogue
) {
super(molekulargenMsiCatalogue);
}
/**
* Loads and maps Prozedur related by database id
*
* @param id The database id of the procedure data set
* @return The loaded Procedure
*/
@Override
public Msi getById(final int id) {
return this.map(catalogue.getById(id));
}
@Override
protected Msi map(ResultSet resultSet) {
var builder = Msi.builder();
if (!resultSet.getString("komplexerbiomarker").equals("MSI")) {
return null;
}
builder
.id(resultSet.getString("id"))
.patient(resultSet.getPatientReference())
.method(getMethodCode(resultSet))
.specimen(Reference.builder().id(resultSet.getString("hauptprozedur_id")).type("Specimen").build())
// Aktuell nicht in Onkostar vorhanden!
//.interpretation()
// In Onkostar nur für "Sequenzierung" bzw "BIOINFORMATIC" als Prozentwert angegeben => "0" als Fallback?
.value(getSeqProzentwert(resultSet))
;
return builder.build();
}
private MsiMethodCoding getMethodCode(final ResultSet resultSet) {
var builder = MsiMethodCoding.builder()
.system("dnpm-dip/mtb/msi/method");
var analysemethoden = resultSet.getMerkmalList("analysemethoden");
// Achtung: Immer nur eine Methode wird betrachtet! In Onkostar sind gleichzeitig mehrere Angaben möglich!
if (analysemethoden == null) {
return null;
} else if (analysemethoden.contains("S")) {
builder.code(MsiMethodCodingCode.BIOINFORMATIC);
builder.display(MsiMethodCodingCode.BIOINFORMATIC.toString());
} else if (analysemethoden.contains("P")) {
builder.code(MsiMethodCodingCode.PCR);
builder.display(MsiMethodCodingCode.PCR.toString());
} else if (analysemethoden.contains("I")) {
builder.code(MsiMethodCodingCode.IHC);
builder.display(MsiMethodCodingCode.IHC.toString());
} else {
return null;
}
return builder.build();
}
private double getSeqProzentwert(final ResultSet resultSet) {
var analysemethoden = resultSet.getMerkmalList("analysemethoden");
// Achtung: Immer nur eine Methode wird betrachtet! In Onkostar sind gleichzeitig mehrere Angaben möglich!
if (analysemethoden != null && analysemethoden.contains("S")) {
return resultSet.getDouble("seqprozentwert");
}
return 0;
}
}

View File

@@ -38,12 +38,12 @@ import java.util.stream.Collectors;
* @author Paul-Christian Volkmer
* @since 0.1
*/
public class KpaMolekulargenetikDataMapper implements DataMapper<SomaticNgsReport> {
public class KpaMolekulargenetikNgsDataMapper implements DataMapper<SomaticNgsReport> {
private final MolekulargenetikCatalogue catalogue;
private final MolekulargenuntersuchungCatalogue untersuchungCatalogue;
public KpaMolekulargenetikDataMapper(
public KpaMolekulargenetikNgsDataMapper(
final MolekulargenetikCatalogue catalogue,
final MolekulargenuntersuchungCatalogue untersuchungCatalogue,
final PropertyCatalogue propertyCatalogue

View File

@@ -124,7 +124,9 @@ public class MtbDataMapper implements DataMapper<Mtb> {
catalogueFactory.catalogue(HistologieCatalogue.class)
);
var kpaMolekulargenetikDataMapper = new KpaMolekulargenetikDataMapper(molekulargenetikCatalogue, catalogueFactory.catalogue(MolekulargenuntersuchungCatalogue.class), propertyCatalogue);
var kpaMolekulargenetikNgsDataMapper = new KpaMolekulargenetikNgsDataMapper(molekulargenetikCatalogue, catalogueFactory.catalogue(MolekulargenuntersuchungCatalogue.class), propertyCatalogue);
var kpaMolekulargenetikMsiDataMapper = new KpaMolekulargenetikMsiDataMapper(catalogueFactory.catalogue(MolekulargenMsiCatalogue.class));
var kpaVorbefundeDataMapper = new KpaVorbefundeDataMapper(
catalogueFactory.catalogue(VorbefundeCatalogue.class),
@@ -196,7 +198,16 @@ public class MtbDataMapper implements DataMapper<Mtb> {
)
// NGS Berichte
.ngsReports(
kpaMolekulargenetikDataMapper.getAllByKpaId(kpaId)
kpaMolekulargenetikNgsDataMapper.getAllByKpaId(kpaId)
)
// MSI Befunde
.msiFindings(
kpaMolekulargenetikNgsDataMapper.getAllByKpaId(kpaId).stream()
.map(ngs -> Integer.parseInt(ngs.getId()))
.flatMap(ngsId -> kpaMolekulargenetikMsiDataMapper.getByParentId(ngsId).stream())
// Filtere alle MSI: Nur mit Angabe Interpretation!
.filter(msi -> msi.getInterpretation() != null)
.collect(Collectors.toList())
)
;

View File

@@ -55,6 +55,13 @@ class ResultSetTest {
assertThat(data.getLong("int")).isEqualTo(42L);
}
@Test
void shouldReturnDoubleValues() {
var data = getTestData();
assertThat(data.getDouble("int")).isEqualTo(42);
}
@Test
void shouldReturnDateValues() {
var data = getTestData();

View File

@@ -0,0 +1,126 @@
/*
* This file is part of mv64e-onkostar-data
*
* Copyright (C) 2025 Paul-Christian Volkmer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package dev.pcvolkmer.onco.datamapper.datacatalogues;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.ArrayList;
import java.util.HashMap;
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.*;
@ExtendWith(MockitoExtension.class)
class MolekulargenMsiCatalogueTest {
JdbcTemplate jdbcTemplate;
MolekulargenMsiCatalogue catalogue;
@BeforeEach
void setUp(@Mock JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.catalogue = MolekulargenMsiCatalogue.create(jdbcTemplate);
}
@Test
void shouldUseCorrectQuery(@Mock Map<String, Object> resultSet) {
doAnswer(invocationOnMock -> List.of(resultSet))
.when(jdbcTemplate)
.queryForList(anyString(), anyInt());
this.catalogue.getById(1);
var captor = ArgumentCaptor.forClass(String.class);
verify(this.jdbcTemplate).queryForList(captor.capture(), anyInt());
assertThat(captor.getValue())
.isEqualTo("SELECT patient.patienten_id, dk_molekluargenmsi.*, prozedur.patient_id, prozedur.hauptprozedur_id FROM dk_molekluargenmsi JOIN prozedur ON (prozedur.id = dk_molekluargenmsi.id) JOIN patient ON (patient.id = prozedur.patient_id) WHERE geloescht = 0 AND prozedur.id = ?");
}
@Test
void shouldUseCorrectSubformQuery(@Mock Map<String, Object> resultSet) {
doAnswer(invocationOnMock -> List.of(resultSet))
.when(jdbcTemplate)
.queryForList(anyString(), anyInt());
this.catalogue.getAllByParentId(1);
var captor = ArgumentCaptor.forClass(String.class);
verify(this.jdbcTemplate).queryForList(captor.capture(), anyInt());
assertThat(captor.getValue())
.isEqualTo("SELECT patient.patienten_id, dk_molekluargenmsi.*, prozedur.patient_id, prozedur.hauptprozedur_id FROM dk_molekluargenmsi JOIN prozedur ON (prozedur.id = dk_molekluargenmsi.id) JOIN patient ON (patient.id = prozedur.patient_id) WHERE geloescht = 0 AND hauptprozedur_id = ?");
}
@Test
void shouldUseCorrectMerkmalQuery(@Mock Map<String, Object> resultSet) {
when(resultSet.get(anyString()))
.thenReturn(Map.of("feldname", "name", "feldwert", "wert"));
doAnswer(invocationOnMock -> List.of(resultSet))
.when(jdbcTemplate)
.queryForList(anyString(), anyInt());
this.catalogue.getMerkmaleById(1);
var captor = ArgumentCaptor.forClass(String.class);
verify(this.jdbcTemplate).queryForList(captor.capture(), anyInt());
assertThat(captor.getValue())
.isEqualTo("SELECT feldname, feldwert FROM dk_molekluargenmsi_merkmale WHERE eintrag_id = ?");
}
@Test
void shouldUseMerkmalList() {
doAnswer(invocationOnMock -> {
var sql = invocationOnMock.getArgument(0, String.class);
ArrayList<Map<String, Object>> result = new ArrayList<>();
if (sql.startsWith("SELECT feldname")) {
result.add(Map.of("feldname", "name", "feldwert", "wert1"));
result.add(Map.of("feldname", "name", "feldwert", "wert2"));
} else {
var map = new HashMap<String, Object>();
map.put("id", 1);
map.put("name", "x");
result.add(map);
}
return result;
})
.when(jdbcTemplate)
.queryForList(anyString(), anyInt());
var result = this.catalogue.getById(1);
assertThat(result.getInteger("id")).isEqualTo(1);
assertThat(result.getMerkmalList("name")).isEqualTo(List.of("wert1", "wert2"));
}
}