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:
@@ -54,7 +54,7 @@ Um Mithilfe wird gebeten.
|
||||
| vorherige Molekular-Diagnostik | ✅ | |
|
||||
| Histologie-Berichte | ✅ | |
|
||||
| IHC-Berichte | - | Aktuell nicht vorgesehen |
|
||||
| MSI-Befunde | ⌛ | Aktuell in Arbeit, https://github.com/dnpm-dip/mtb-model/issues/10 ist behoben |
|
||||
| MSI-Befunde | ⛅ | Best effort: Formular OS.Molekulargenetik erfüllt nicht alle Anforderungen (2) |
|
||||
| NGS-Berichte | ⛅ | Best effort: Formular OS.Molekulargenetik erfüllt nicht alle Anforderungen (2) |
|
||||
| MTB-Beschlüsse | ✅ | Stützende molekulare Alteration(en) für einfache Variante und CNV (3) |
|
||||
| Follow-Up Verlauf | - | Späterer Zeitpunkt |
|
||||
@@ -67,7 +67,10 @@ Um Mithilfe wird gebeten.
|
||||
|
||||
1. Nicht alle möglichen Ausprägungen in `OS.Molekulargenetik` vorhanden.
|
||||
2. Aktuell nicht alle Angaben effektiv im Formular `OS.Molekulargenetik` wie gefordert angebbar.
|
||||
Hinweis: Tumorzellgehalt-Methode problematisch, wenn auch im NGS-Bericht histologisch festgestellt.
|
||||
Hinweise:
|
||||
* Tumorzellgehalt-Methode problematisch, wenn auch im NGS-Bericht histologisch festgestellt.
|
||||
* Angabe zu MSI-Interpretation fehlt in Formular, ist aber Pflichtangabe - Wird gefiltert.
|
||||
* Datenbanktabelle für MSI lautet tatsächlich `dk_molekluargenmsi` [sic!]
|
||||
3. Implementierung des Mappings von HGNC-Symbol (Gen-Name) zu HGNC-ID über enthaltene Gen-Liste.
|
||||
|
||||
## Enthaltene Liste mit Genen
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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())
|
||||
)
|
||||
;
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user