1
0
mirror of https://github.com/pcvolkmer/mv64e-onkostar-data.git synced 2025-07-02 02:22:54 +00:00

feat: add initial support for recommendations

This commit is contained in:
2025-06-22 19:11:24 +02:00
parent a24855ec7a
commit 1ab33de3f8
15 changed files with 306 additions and 30 deletions

View File

@ -30,11 +30,11 @@ public class ResultSet {
}
/**
* Get the procedure id
* Get the id
*
* @return The procedure id if any
* @return The procedures id
*/
public Integer getProcedureId() {
public Integer getId() {
var procedureId = this.getInteger("id");
if (procedureId == null) {
throw new DataAccessException("No procedure id found");
@ -42,19 +42,6 @@ public class ResultSet {
return procedureId;
}
/**
* Get the disease id
*
* @return The procedure id if any
*/
public Integer getDiseaseId() {
var diseaseId = this.getInteger("id");
if (diseaseId == null) {
throw new DataAccessException("No disease id found");
}
return diseaseId;
}
/**
* Get column value as String and cast value if possible
*

View File

@ -69,6 +69,8 @@ public class DataCatalogueFactory {
return VorbefundeCatalogue.create(jdbcTemplate);
} else if (c == TherapieplanCatalogue.class) {
return TherapieplanCatalogue.create(jdbcTemplate);
} else if (c == EinzelempfehlungCatalogue.class) {
return EinzelempfehlungCatalogue.create(jdbcTemplate);
}
throw new DataCatalogueCreationException(clazz);
});

View File

@ -0,0 +1,26 @@
package dev.pcvolkmer.onco.datamapper.datacatalogues;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Load raw result sets from database table 'dk_dnpm_uf_einzelempfehlung'
*
* @author Paul-Christian Volkmer
* @since 0.1
*/
public class EinzelempfehlungCatalogue extends AbstractSubformDataCatalogue {
private EinzelempfehlungCatalogue(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
protected String getTableName() {
return "dk_dnpm_uf_einzelempfehlung";
}
public static EinzelempfehlungCatalogue create(JdbcTemplate jdbcTemplate) {
return new EinzelempfehlungCatalogue(jdbcTemplate);
}
}

View File

@ -0,0 +1,101 @@
package dev.pcvolkmer.onco.datamapper.mapper;
import dev.pcvolkmer.mv64e.mtb.*;
import dev.pcvolkmer.onco.datamapper.ResultSet;
import dev.pcvolkmer.onco.datamapper.datacatalogues.EinzelempfehlungCatalogue;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static dev.pcvolkmer.onco.datamapper.mapper.MapperUtils.getPatientReference;
/**
* Mapper class to load and map diagnosis data from database table 'dk_dnpm_einzelempfehlung'
*
* @author Paul-Christian Volkmer
* @since 0.1
*/
public class EinzelempfehlungProzedurDataMapper extends AbstractSubformDataMapper<ProcedureRecommendation> {
public EinzelempfehlungProzedurDataMapper(EinzelempfehlungCatalogue einzelempfehlungCatalogue) {
super(einzelempfehlungCatalogue);
}
@Override
protected ProcedureRecommendation map(ResultSet resultSet) {
return ProcedureRecommendation.builder()
.id(resultSet.getString("id"))
.patient(getPatientReference(resultSet.getString("patient_id")))
// TODO Fix id?
.reason(Reference.builder().id(resultSet.getString("id")).build())
.issuedOn(resultSet.getDate("datum"))
.priority(
getRecommendationPriorityCoding(
resultSet.getString("evidenzlevel"),
resultSet.getInteger("evidenzlevel_propcat_version")
)
)
.code(
getMtbProcedureRecommendationCategoryCoding(
resultSet.getString("art_der_therapie"),
resultSet.getInteger("art_der_therapie_propcat_version")
)
)
.build();
}
@Override
public ProcedureRecommendation getById(int id) {
return this.map(this.catalogue.getById(id));
}
@Override
public List<ProcedureRecommendation> getByParentId(final int parentId) {
return catalogue.getAllByParentId(parentId)
.stream()
// Filter Prozedurempfehlung (Weitere Empfehlungen)
.filter(it -> it.getString("art_der_therapie") != null && !it.getString("art_der_therapie").isBlank() )
.map(this::map)
.collect(Collectors.toList());
}
private RecommendationPriorityCoding getRecommendationPriorityCoding(String code, int version) {
if (code == null || !Arrays.stream(RecommendationPriorityCodingCode.values()).map(RecommendationPriorityCodingCode::toValue).collect(Collectors.toSet()).contains(code)) {
return null;
}
var resultBuilder = RecommendationPriorityCoding.builder()
.system("dnpm-dip/recommendation/priority");
try {
resultBuilder
.code(RecommendationPriorityCodingCode.forValue(code))
.display(code);
} catch (IOException e) {
return null;
}
return resultBuilder.build();
}
private MtbProcedureRecommendationCategoryCoding getMtbProcedureRecommendationCategoryCoding(String code, int version) {
if (code == null || !Arrays.stream(MtbProcedureRecommendationCategoryCodingCode.values()).map(MtbProcedureRecommendationCategoryCodingCode::toValue).collect(Collectors.toSet()).contains(code)) {
return null;
}
var resultBuilder = MtbProcedureRecommendationCategoryCoding.builder()
.system("dnpm-dip/mtb/recommendation/procedure/category");
try {
resultBuilder
.code(MtbProcedureRecommendationCategoryCodingCode.forValue(code))
.display(code);
} catch (IOException e) {
return null;
}
return resultBuilder.build();
}
}

View File

@ -0,0 +1,80 @@
package dev.pcvolkmer.onco.datamapper.mapper;
import dev.pcvolkmer.mv64e.mtb.MtbMedicationRecommendation;
import dev.pcvolkmer.mv64e.mtb.RecommendationPriorityCoding;
import dev.pcvolkmer.mv64e.mtb.RecommendationPriorityCodingCode;
import dev.pcvolkmer.mv64e.mtb.Reference;
import dev.pcvolkmer.onco.datamapper.ResultSet;
import dev.pcvolkmer.onco.datamapper.datacatalogues.EinzelempfehlungCatalogue;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static dev.pcvolkmer.onco.datamapper.mapper.MapperUtils.getPatientReference;
/**
* Mapper class to load and map diagnosis data from database table 'dk_dnpm_einzelempfehlung'
*
* @author Paul-Christian Volkmer
* @since 0.1
*/
public class EinzelempfehlungWirkstoffDataMapper extends AbstractSubformDataMapper<MtbMedicationRecommendation> {
public EinzelempfehlungWirkstoffDataMapper(EinzelempfehlungCatalogue einzelempfehlungCatalogue) {
super(einzelempfehlungCatalogue);
}
@Override
protected MtbMedicationRecommendation map(ResultSet resultSet) {
return MtbMedicationRecommendation.builder()
.id(resultSet.getString("id"))
.patient(getPatientReference(resultSet.getString("patient_id")))
// TODO Fix id?
.reason(Reference.builder().id(resultSet.getString("id")).build())
.issuedOn(resultSet.getDate("datum"))
.priority(
getRecommendationPriorityCoding(
resultSet.getString("evidenzlevel"),
resultSet.getInteger("evidenzlevel_propcat_version")
)
)
.build();
}
@Override
public MtbMedicationRecommendation getById(int id) {
return this.map(this.catalogue.getById(id));
}
@Override
public List<MtbMedicationRecommendation> getByParentId(final int parentId) {
return catalogue.getAllByParentId(parentId)
.stream()
// Filter Wirkstoffempfehlung (Systemische Therapie)
.filter(it -> it.getString("art_der_therapie") == null || it.getString("art_der_therapie").isBlank() )
.map(this::map)
.collect(Collectors.toList());
}
private RecommendationPriorityCoding getRecommendationPriorityCoding(String code, int version) {
if (code == null || !Arrays.stream(RecommendationPriorityCodingCode.values()).map(RecommendationPriorityCodingCode::toValue).collect(Collectors.toSet()).contains(code)) {
return null;
}
var resultBuilder = RecommendationPriorityCoding.builder()
.system("dnpm-dip/recommendation/priority");
try {
resultBuilder
.code(RecommendationPriorityCodingCode.forValue(code))
.display(code);
} catch (IOException e) {
return null;
}
return resultBuilder.build();
}
}

View File

@ -51,7 +51,7 @@ public class KpaEcogDataMapper extends AbstractSubformDataMapper<PerformanceStat
protected PerformanceStatus map(final ResultSet resultSet) {
var builder = PerformanceStatus.builder();
builder
.id(resultSet.getProcedureId().toString())
.id(resultSet.getId().toString())
.patient(getPatientReference(resultSet.getString("patient_id")))
.effectiveDate(resultSet.getDate("datum"))
.value(getEcogCoding(resultSet.getString("ecog")))

View File

@ -35,17 +35,17 @@ public class KpaProzedurDataMapper extends AbstractKpaTherapieverlaufDataMapper<
@Override
protected OncoProcedure map(final ResultSet resultSet) {
var diseases = catalogue.getDiseases(resultSet.getProcedureId());
var diseases = catalogue.getDiseases(resultSet.getId());
if (diseases.size() != 1) {
throw new IllegalStateException(String.format("No unique disease for procedure %s", resultSet.getProcedureId()));
throw new IllegalStateException(String.format("No unique disease for procedure %s", resultSet.getId()));
}
var builder = OncoProcedure.builder();
builder
.id(resultSet.getString("id"))
.patient(getPatientReference(resultSet.getString("patient_id")))
.basedOn(Reference.builder().id(diseases.get(0).getDiseaseId().toString()).build())
.basedOn(Reference.builder().id(diseases.get(0).getString("id")).build())
.recordedOn(resultSet.getDate("erfassungsdatum"))
.therapyLine(resultSet.getLong("therapielinie"))
.intent(

View File

@ -35,17 +35,17 @@ public class KpaTherapielinieDataMapper extends AbstractKpaTherapieverlaufDataMa
@Override
protected MtbSystemicTherapy map(final ResultSet resultSet) {
var diseases = catalogue.getDiseases(resultSet.getProcedureId());
var diseases = catalogue.getDiseases(resultSet.getId());
if (diseases.size() != 1) {
throw new IllegalStateException(String.format("No unique disease for procedure %s", resultSet.getProcedureId()));
throw new IllegalStateException(String.format("No unique disease for procedure %s", resultSet.getId()));
}
var builder = MtbSystemicTherapy.builder();
builder
.id(resultSet.getString("id"))
.patient(getPatientReference(resultSet.getString("patient_id")))
.basedOn(Reference.builder().id(diseases.get(0).getDiseaseId().toString()).build())
.basedOn(Reference.builder().id(diseases.get(0).getString("id")).build())
.recordedOn(resultSet.getDate("erfassungsdatum"))
.therapyLine(resultSet.getLong("therapielinie"))
.intent(

View File

@ -80,7 +80,11 @@ public class MtbDataMapper implements DataMapper<Mtb> {
);
var therapieplanCatalogue = catalogueFactory.catalogue(TherapieplanCatalogue.class);
var therapieplanDataMapper = new TherapieplanDataMapper(therapieplanCatalogue, propertyCatalogue);
var therapieplanDataMapper = new TherapieplanDataMapper(
therapieplanCatalogue,
catalogueFactory.catalogue(EinzelempfehlungCatalogue.class),
propertyCatalogue
);
var resultBuilder = Mtb.builder();

View File

@ -2,6 +2,7 @@ package dev.pcvolkmer.onco.datamapper.mapper;
import dev.pcvolkmer.mv64e.mtb.MtbCarePlan;
import dev.pcvolkmer.onco.datamapper.PropertyCatalogue;
import dev.pcvolkmer.onco.datamapper.datacatalogues.EinzelempfehlungCatalogue;
import dev.pcvolkmer.onco.datamapper.datacatalogues.TherapieplanCatalogue;
import static dev.pcvolkmer.onco.datamapper.mapper.MapperUtils.getPatientReference;
@ -17,12 +18,19 @@ public class TherapieplanDataMapper implements DataMapper<MtbCarePlan> {
private final TherapieplanCatalogue therapieplanCatalogue;
private final PropertyCatalogue propertyCatalogue;
private final EinzelempfehlungProzedurDataMapper einzelempfehlungProzedurDataMapper;
private final EinzelempfehlungWirkstoffDataMapper einzelempfehlungWirkstoffDataMapper;
public TherapieplanDataMapper(
final TherapieplanCatalogue therapieplanCatalogue,
final EinzelempfehlungCatalogue einzelempfehlungCatalogue,
final PropertyCatalogue propertyCatalogue
) {
this.therapieplanCatalogue = therapieplanCatalogue;
this.propertyCatalogue = propertyCatalogue;
this.einzelempfehlungProzedurDataMapper = new EinzelempfehlungProzedurDataMapper(einzelempfehlungCatalogue);
this.einzelempfehlungWirkstoffDataMapper = new EinzelempfehlungWirkstoffDataMapper(einzelempfehlungCatalogue);
}
/**
@ -40,6 +48,8 @@ public class TherapieplanDataMapper implements DataMapper<MtbCarePlan> {
.id(therapieplanData.getString("id"))
.patient(getPatientReference(therapieplanData.getString("patient_id")))
.issuedOn(therapieplanData.getDate("datum"))
.medicationRecommendations(einzelempfehlungWirkstoffDataMapper.getByParentId(id))
.procedureRecommendations(einzelempfehlungProzedurDataMapper.getByParentId(id))
;
return builder.build();
}

View File

@ -0,0 +1,62 @@
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.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.verify;
@ExtendWith(MockitoExtension.class)
class EinzelempfehlungCatalogueTest {
JdbcTemplate jdbcTemplate;
EinzelempfehlungCatalogue catalogue;
@BeforeEach
void setUp(@Mock JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.catalogue = EinzelempfehlungCatalogue.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 * FROM dk_dnpm_uf_einzelempfehlung JOIN prozedur ON (prozedur.id = dk_dnpm_uf_einzelempfehlung.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 * FROM dk_dnpm_uf_einzelempfehlung JOIN prozedur ON (prozedur.id = dk_dnpm_uf_einzelempfehlung.id) WHERE geloescht = 0 AND hauptprozedur_id = ?");
}
}

View File

@ -55,7 +55,7 @@ class KpaEcogDataMapperTest {
return testData.get(columnName);
}).when(resultSet).getDate(anyString());
when(resultSet.getProcedureId()).thenReturn(1);
when(resultSet.getId()).thenReturn(1);
doAnswer(invocationOnMock -> List.of(resultSet))
.when(catalogue)

View File

@ -69,8 +69,8 @@ class KpaProzedurDataMapperTest {
return testData.get(columnName);
}).when(resultSet).getDate(anyString());
when(resultSet.getDiseaseId()).thenReturn(1);
when(resultSet.getProcedureId()).thenReturn(1);
when(resultSet.getInteger(anyString())).thenReturn(1);
when(resultSet.getId()).thenReturn(1);
doAnswer(invocationOnMock -> List.of(resultSet))
.when(catalogue)

View File

@ -68,8 +68,8 @@ class KpaTherapielinieDataMapperTest {
return testData.get(columnName);
}).when(resultSet).getDate(anyString());
when(resultSet.getDiseaseId()).thenReturn(1);
when(resultSet.getProcedureId()).thenReturn(1);
when(resultSet.getInteger(anyString())).thenReturn(1);
when(resultSet.getId()).thenReturn(1);
doAnswer(invocationOnMock -> List.of(resultSet))
.when(catalogue)

View File

@ -4,6 +4,7 @@ import dev.pcvolkmer.mv64e.mtb.MtbCarePlan;
import dev.pcvolkmer.mv64e.mtb.Reference;
import dev.pcvolkmer.onco.datamapper.PropertyCatalogue;
import dev.pcvolkmer.onco.datamapper.ResultSet;
import dev.pcvolkmer.onco.datamapper.datacatalogues.EinzelempfehlungCatalogue;
import dev.pcvolkmer.onco.datamapper.datacatalogues.TherapieplanCatalogue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -22,6 +23,7 @@ import static org.mockito.Mockito.doAnswer;
class TherapieplanDataMapperTest {
TherapieplanCatalogue therapieplanCatalogue;
EinzelempfehlungCatalogue einzelempfehlungCatalogue;
PropertyCatalogue propertyCatalogue;
TherapieplanDataMapper dataMapper;
@ -29,11 +31,13 @@ class TherapieplanDataMapperTest {
@BeforeEach
void setUp(
@Mock TherapieplanCatalogue therapieplanCatalogue,
@Mock EinzelempfehlungCatalogue einzelempfehlungCatalogue,
@Mock PropertyCatalogue propertyCatalogue
) {
this.therapieplanCatalogue = therapieplanCatalogue;
this.einzelempfehlungCatalogue = einzelempfehlungCatalogue;
this.propertyCatalogue = propertyCatalogue;
this.dataMapper = new TherapieplanDataMapper(therapieplanCatalogue, propertyCatalogue);
this.dataMapper = new TherapieplanDataMapper(therapieplanCatalogue, einzelempfehlungCatalogue, propertyCatalogue);
}
@Test