Skip to content
This repository has been archived by the owner on Apr 8, 2024. It is now read-only.

Commit

Permalink
Merge branch 'release/v1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
subigre committed Apr 27, 2021
2 parents ec389ee + ec38c9b commit b96f5df
Show file tree
Hide file tree
Showing 107 changed files with 73,801 additions and 1,569 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ The implementation is based on [Apache Camel](https://camel.apache.org/) and [Op

## Releases

##### 2021, April 27
v1.1.0 - Final release 1.1.0 including the fix for SMART on FHIR.

##### 2021, April 22
v1.1.0-RC3 - Add support for Basic and OAuth 2.0 Authentication.

Expand Down
5 changes: 3 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>org.ehrbase.fhirbridge</groupId>
<artifactId>fhir-bridge</artifactId>
<version>1.1.0-RC3</version>
<version>1.1.0</version>

<name>FHIR Bridge</name>

Expand Down Expand Up @@ -42,7 +42,8 @@
<profile>
<id>circleci</id>
<properties>
<commandLineArguments>--logging.level.root=error --logging.level.org.apache.camel=off</commandLineArguments>
<commandLineArguments>--logging.level.root=error --logging.level.org.apache.camel=off
</commandLineArguments>
</properties>
</profile>
</profiles>
Expand Down
51 changes: 33 additions & 18 deletions src/main/java/org/ehrbase/fhirbridge/camel/route/BundleRoutes.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.camel.builder.RouteBuilder;
import org.ehrbase.fhirbridge.camel.FhirBridgeConstants;
import org.ehrbase.fhirbridge.fhir.bundle.converter.AntiBodyPanelConverter;
import org.ehrbase.fhirbridge.fhir.bundle.converter.BloodGasPanelConverter;
import org.ehrbase.fhirbridge.fhir.bundle.converter.DiagnosticReportLabConverter;
import org.ehrbase.fhirbridge.fhir.bundle.validator.AntiBodyPanelBundleValidator;
import org.ehrbase.fhirbridge.fhir.bundle.validator.BloodGasPanelBundleValidator;
import org.ehrbase.fhirbridge.fhir.bundle.validator.DiagnosticReportLabBundleValidator;
import org.ehrbase.fhirbridge.fhir.common.Profile;
Expand All @@ -36,36 +38,49 @@
@Component
public class BundleRoutes extends AbstractRouteBuilder {

private final String CONVERT = "convert";
private final String BUNDLE_RESPONSE_PROCESSOR = "bundleResponseProcessor";

@Override
public void configure() throws Exception {
// @formatter:off
super.configure();

// 'Provide Bundle' route definition
from("bundle-provide:consumer?fhirContext=#fhirContext")
.setHeader(FhirBridgeConstants.PROFILE, method(Bundles.class, "getTransactionProfile"))
.choice()
.when(header(FhirBridgeConstants.PROFILE).isEqualTo(Profile.BLOOD_GAS_PANEL))
.to("direct:process-blood-gas-panel-bundle")
.when(header(FhirBridgeConstants.PROFILE).isEqualTo(Profile.DIAGNOSTIC_REPORT_LAB))
.to("direct:process-diagnostic-report-lab-bundle")
.otherwise()
.throwException(new UnprocessableEntityException("Unsupported transaction: provided Bundle should have a resource that " +
"uses on of the following profiles: " + Profile.BLOOD_GAS_PANEL.getUri() + ", " + Profile.DIAGNOSTIC_REPORT_LAB.getUri()));
.setHeader(FhirBridgeConstants.PROFILE, method(Bundles.class, "getTransactionProfile"))
.choice()
.when(header(FhirBridgeConstants.PROFILE).isEqualTo(Profile.BLOOD_GAS_PANEL))
.to("direct:process-blood-gas-panel-bundle")
.when(header(FhirBridgeConstants.PROFILE).isEqualTo(Profile.ANTI_BODY_PANEL))
.to("direct:process-anti-body-panel-bundle")
.when(header(FhirBridgeConstants.PROFILE).isEqualTo(Profile.DIAGNOSTIC_REPORT_LAB))
.to("direct:process-diagnostic-report-lab-bundle")
.otherwise()
.throwException(new UnprocessableEntityException("Unsupported transaction: provided Bundle should have a resource that " +
"uses on of the following profiles: " + Profile.BLOOD_GAS_PANEL.getUri() + ", " + Profile.DIAGNOSTIC_REPORT_LAB.getUri()));


from("direct:process-anti-body-panel-bundle")
.bean(AntiBodyPanelBundleValidator.class)
.bean(AntiBodyPanelConverter.class, CONVERT)
.to("direct:process-observation")
.process(BUNDLE_RESPONSE_PROCESSOR);

// Internal routes definition
from("direct:process-blood-gas-panel-bundle")
.bean(BloodGasPanelBundleValidator.class)
.bean(BloodGasPanelConverter.class, "convert")
.to("direct:process-observation")
.process("bundleResponseProcessor");
.bean(BloodGasPanelBundleValidator.class)
.bean(BloodGasPanelConverter.class, CONVERT)
.to("direct:process-observation")
.process(BUNDLE_RESPONSE_PROCESSOR);

from("direct:process-diagnostic-report-lab-bundle")
.bean(DiagnosticReportLabBundleValidator.class)
.bean(DiagnosticReportLabConverter.class, "convert")
.to("direct:process-diagnostic-report")
.process("bundleResponseProcessor");
.bean(DiagnosticReportLabBundleValidator.class)
.bean(DiagnosticReportLabConverter.class, CONVERT)
.to("direct:process-diagnostic-report")
.process(BUNDLE_RESPONSE_PROCESSOR);

// @formatter:on
}
}


Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ehrbase.fhirbridge.config;

import org.ehrbase.fhirbridge.ehr.converter.ConversionService;
import org.ehrbase.fhirbridge.ehr.converter.specific.antibodypanel.GECCOSerologischerBefundCompositionConverter;
import org.ehrbase.fhirbridge.ehr.converter.specific.bloodgas.BloodGasPanelCompositionConverter;
import org.ehrbase.fhirbridge.ehr.converter.specific.bloodpressure.BloodPressureCompositionConverter;
import org.ehrbase.fhirbridge.ehr.converter.specific.bodyheight.BodyHeightCompositionConverter;
Expand Down Expand Up @@ -84,6 +85,7 @@ private void registerDiagnosticReportConverters(ConversionService conversionServ
private void registerObservationConverters(ConversionService conversionService) {
conversionService.registerConverter(Profile.BODY_HEIGHT, new BodyHeightCompositionConverter());
conversionService.registerConverter(Profile.BLOOD_GAS_PANEL, new BloodGasPanelCompositionConverter());
conversionService.registerConverter(Profile.ANTI_BODY_PANEL, new GECCOSerologischerBefundCompositionConverter());
conversionService.registerConverter(Profile.BLOOD_PRESSURE, new BloodPressureCompositionConverter());
conversionService.registerConverter(Profile.BODY_TEMP, new BodyTemperatureCompositionConverter());
conversionService.registerConverter(Profile.BODY_WEIGHT, new BodyWeightCompositionConverter());
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public C convert(@NonNull Procedure resource) {
C composition = super.convert(resource);

// Mandatory
TimeConverter.convertProcedureTime(resource).ifPresent(composition::setStartTimeValue); // EndTimeValue
composition.setStartTimeValue(TimeConverter.convertProcedureTime(resource));
composition.setComposer(getComposerOrDefault(resource)); // Composer

// Optional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ public void invokeTimeValues(E entryEntity, Procedure resource) {
public void invokeSetTimeValue(E entryEntity, Procedure resource) {
try {
Method setOriginValue = entryEntity.getClass().getMethod("setOriginValue", TemporalAccessor.class);
if (TimeConverter.convertProcedureTime(resource).isPresent()) {
setOriginValue.invoke(entryEntity, TimeConverter.convertProcedureTime(resource).get());
}
setOriginValue.invoke(entryEntity, TimeConverter.convertProcedureTime(resource));
} catch (IllegalAccessException | InvocationTargetException exception) {
exception.printStackTrace();
} catch (NoSuchMethodException ignored) {
Expand All @@ -38,9 +36,7 @@ public void invokeSetTimeValue(E entryEntity, Procedure resource) {
public void invokeOriginValue(E entryEntity, Procedure resource) {
try {
Method setTimeValue = entryEntity.getClass().getMethod("setTimeValue", TemporalAccessor.class);
if (TimeConverter.convertProcedureTime(resource).isPresent()) {
setTimeValue.invoke(entryEntity, TimeConverter.convertProcedureTime(resource).get());
}
setTimeValue.invoke(entryEntity, TimeConverter.convertProcedureTime(resource));
} catch (IllegalAccessException | InvocationTargetException exception) {
exception.printStackTrace();
} catch (NoSuchMethodException ignored) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static TemporalAccessor convertObservationTime(Observation observation) {
.findFirst()
.orElse(ZonedDateTime.now());
} else if (observation.hasEffectiveInstantType()) { // EffectiveInstant
return observation.getEffectiveDateTimeType().getValueAsCalendar().toZonedDateTime();
return observation.getEffectiveInstantType().getValueAsCalendar().toZonedDateTime();
} else {
return ZonedDateTime.now();
}
Expand Down Expand Up @@ -85,16 +85,13 @@ public static Optional<TemporalAccessor> convertDiagnosticReportEndTime(Diagnost
}
}

public static Optional<TemporalAccessor> convertProcedureTime(Procedure resource) {
public static TemporalAccessor convertProcedureTime(Procedure resource) {
if (resource.hasPerformedDateTimeType() && resource.getPerformedDateTimeType().getExtension().isEmpty()) { // EffectiveDateTime
return Optional.ofNullable(resource.getPerformedDateTimeType().getValueAsCalendar().toZonedDateTime());
} else if (resource.hasPerformedDateTimeType() && resource.getPerformedDateTimeType().hasExtension() && resource.getPerformedDateTimeType().getExtension().get(0).getValue().toString().equals("not-performed")) {
//TODO wait until Template is fixed return Optional.empty();
return Optional.of(OffsetDateTime.now());
return resource.getPerformedDateTimeType().getValueAsCalendar().toZonedDateTime();
} else if (resource.hasPerformedPeriod() && resource.getPerformedPeriod().hasStart()) { // EffectivePeriod
return Optional.ofNullable(resource.getPerformedPeriod().getStartElement().getValueAsCalendar().toZonedDateTime());
return resource.getPerformedPeriod().getStartElement().getValueAsCalendar().toZonedDateTime();
} else {
return Optional.empty();
return ZonedDateTime.now();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.ehrbase.fhirbridge.ehr.converter.specific.antibodypanel;

import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.ehrbase.fhirbridge.ehr.opt.geccoserologischerbefundcomposition.definition.VirusnachweistestDefiningCode;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Resource;

import java.util.List;
import java.util.Optional;

public class AntiBodyPanel {

private Optional<Observation> antiBodyPanel = Optional.empty();
private Optional<Immunoassay> aBPresence = Optional.empty();
private Optional<Immunoassay> aBUnitsVolume = Optional.empty();
private Optional<Immunoassay> igAAbPresence = Optional.empty();
private Optional<Immunoassay> igAAbUnitVolume = Optional.empty();
private Optional<Immunoassay> igMAbPresence = Optional.empty();
private Optional<Immunoassay> igMAbUnitsVolume = Optional.empty();
private Optional<Immunoassay> igGAbPresence = Optional.empty();
private Optional<Immunoassay> igGAbUnitsVolume = Optional.empty();

public AntiBodyPanel(Observation observation) {
setObservations(observation);
}

private void setObservations(Observation observation) {
antiBodyPanel = Optional.of(observation);
for (Resource resource : observation.getContained()) {
setProfiles(resource);
}
}

private void setProfiles(Resource resource) {
try {
String profileUrl = resource.getMeta().getProfile().get(0).getValue();
resoulveProfile(resource, profileUrl);
} catch (IndexOutOfBoundsException e) {
throw new UnprocessableEntityException("Make sure only the for Anti body panel supported Profiles are contained in the Bundle");
}
}

private void resoulveProfile(Resource resource, String profileUrl) {
if (AntiBodyProfileUrl.AB_PRESENCE.getUrl().equals(profileUrl)) {
this.aBPresence = Optional.of(new Immunoassay((Observation) resource, VirusnachweistestDefiningCode.SARS_COV2_COVID19_AB_PRESENCE_IN_SERUM_OR_PLASMA_BY_IMMUNOASSAY));

} else if (AntiBodyProfileUrl.AB_UNITS_VOLUME.getUrl().equals(profileUrl)) {
this.aBUnitsVolume = Optional.of(new Immunoassay((Observation) resource, VirusnachweistestDefiningCode.SARS_COV2_COVID19_AB_UNITS_VOLUME_IN_SERUM_OR_PLASMA_BY_IMMUNOASSAY));

} else if (AntiBodyProfileUrl.IGA_AB_PRESENCE.getUrl().equals(profileUrl)) {
this.igAAbPresence = Optional.of(new Immunoassay((Observation) resource, VirusnachweistestDefiningCode.SARS_COV2_COVID19_IGA_AB_PRESENCE_IN_SERUM_OR_PLASMA_BY_IMMUNOASSAY));

} else if (AntiBodyProfileUrl.IGA_AB_UNITS_VOLUME.getUrl().equals(profileUrl)) {
this.igAAbUnitVolume = Optional.of(new Immunoassay((Observation) resource, VirusnachweistestDefiningCode.SARS_COV2_COVID19_IGA_AB_UNITS_VOLUME_IN_SERUM_OR_PLASMA_BY_IMMUNOASSAY));

} else if (AntiBodyProfileUrl.IGM_AB_PRESENCE.getUrl().equals(profileUrl)) {
this.igMAbPresence = Optional.of(new Immunoassay((Observation) resource, VirusnachweistestDefiningCode.SARS_COV2_COVID19_IGM_AB_PRESENCE_IN_SERUM_OR_PLASMA_BY_IMMUNOASSAY));

} else if (AntiBodyProfileUrl.IGM_AB_UNITS_VOLUME.getUrl().equals(profileUrl)) {
this.igMAbUnitsVolume = Optional.of(new Immunoassay((Observation) resource, VirusnachweistestDefiningCode.SARS_COV2_COVID19_IGM_AB_UNITS_VOLUME_IN_SERUM_OR_PLASMA_BY_IMMUNOASSAY));

} else if (AntiBodyProfileUrl.IGG_AB_PRESENCE.getUrl().equals(profileUrl)) {
this.igGAbPresence = Optional.of(new Immunoassay((Observation) resource, VirusnachweistestDefiningCode.SARS_COV2_COVID19_IGG_AB_PRESENCE_IN_SERUM_OR_PLASMA_BY_IMMUNOASSAY));

} else if (AntiBodyProfileUrl.IGG_AB_UNITS_VOLUME.getUrl().equals(profileUrl)) {
this.igGAbUnitsVolume = Optional.of(new Immunoassay((Observation) resource, VirusnachweistestDefiningCode.SARS_COV2_COVID19_IGG_AB_UNITS_VOLUME_IN_SERUM_OR_PLASMA_BY_IMMUNOASSAY));

} else {
throw new UnprocessableEntityException("Anti body panel bundle needs to contain only the profiles for the Anti body panel. Please delete profile " + profileUrl + " from the Bundle.");
}
}

public List<Optional<Immunoassay>> getAllNonPanel() {
return List.of(aBPresence, aBUnitsVolume, igAAbPresence, igAAbUnitVolume, igMAbPresence, igMAbUnitsVolume, igGAbPresence, igGAbUnitsVolume);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.ehrbase.fhirbridge.ehr.converter.specific.antibodypanel;


import org.ehrbase.fhirbridge.ehr.converter.specific.CodeSystem;

public enum AntiBodyPanelCode {
LABORATORY_STUDIES(CodeSystem.LOINC.getUrl(), "26436-6");

private final String system;
private final String code;


AntiBodyPanelCode( String system, String code) {
this.code = code;
this.system = system;
}

public String getSystem() {
return system;
}

public String getCode() {
return code;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.ehrbase.fhirbridge.ehr.converter.specific.antibodypanel;


public enum AntiBodyProfileUrl {
AB_PRESENCE("https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/sars-cov-2-ab-ser-pl-ql-ia"),
AB_UNITS_VOLUME("https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/sars-cov-2-ab-ser-pl-ia-acnc"),
IGA_AB_PRESENCE("https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/sars-cov-2-iga-ser-pl-ql-ia"),
IGA_AB_UNITS_VOLUME("https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/sars-cov-2-iga-ser-pl-ia-acnc"),
IGM_AB_PRESENCE("https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/sars-cov-2-igm-ser-pl-ql-ia"),
IGM_AB_UNITS_VOLUME("https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/sars-cov-2-igm-ser-pl-ia-acnc"),
IGG_AB_PRESENCE("https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/sars-cov-2-igg-ser-pl-ql-ia"),
IGG_AB_UNITS_VOLUME("https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/sars-cov-2-igg-ser-pl-ia-acnc");

private final String url;

AntiBodyProfileUrl(String url) {
this.url = url;
}

public String getUrl() {
return url;
}

}
Loading

0 comments on commit b96f5df

Please sign in to comment.