Skip to content

Commit

Permalink
adds support for references in blood-gas-panel resource
Browse files Browse the repository at this point in the history
This is not a fix for the more general problem described in #37, but
blood-gas-panel is now fully supported.
  • Loading branch information
hhund committed Jun 25, 2022
1 parent 77dc628 commit 44293c2
Show file tree
Hide file tree
Showing 9 changed files with 913 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,18 @@ protected Optional<Patient> findPatientInLocalFhirStore(String pseudonym)
{
logger.warn("Error while searching for Patient with pseudonym {}|{}, message: {}, status: {}",
NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM, pseudonym, e.getMessage(), e.getStatusCode());

IBaseOperationOutcome outcome = e.getOperationOutcome();

if (outcome != null && outcome instanceof OperationOutcome)
outcomeLogger.logOutcome((OperationOutcome) outcome);

throw e;
}
catch (Exception e)
{
logger.warn("Error while searching for Patient with pseudonym " + NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM + "|"
+ pseudonym, e);
logger.warn("Error while searching for Patient with pseudonym " + NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM
+ "|" + pseudonym, e);
throw e;
}
}
Expand Down Expand Up @@ -227,10 +227,10 @@ public Stream<DomainResource> getNewData(String pseudonym, DateWithPrecision exp
logger.debug("Search-Bundle result: {}",
geccoClient.getFhirContext().newJsonParser().encodeResourceToString(resultBundle));

return Stream.concat(Stream.of(localPatient.get()),
return distinctById(Stream.concat(Stream.of(localPatient.get()),
resultBundle.getEntry().stream().filter(BundleEntryComponent::hasResource)
.map(BundleEntryComponent::getResource).filter(r -> r instanceof Bundle).map(r -> (Bundle) r)
.flatMap(this::getDomainResources));
.flatMap(this::getDomainResources)));
}

private Optional<Patient> findPatientInLocalFhirStore(String system, String pseudonym)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,54 @@ public abstract class AbstractFhirClient implements GeccoFhirClient
private static final Logger logger = LoggerFactory.getLogger(AbstractFhirClient.class);
private static final OutcomeLogger outcomeLogger = new OutcomeLogger(logger);

private static final class DomainResourceUniqueByUnqualifiedVersionlessId
{
private final DomainResource resource;
private final String unqualifiedVersionlessIdValue;

public DomainResourceUniqueByUnqualifiedVersionlessId(DomainResource resource)
{
this.resource = resource;

unqualifiedVersionlessIdValue = resource.getIdElement().toUnqualifiedVersionless().getValue();
}

public DomainResource getResource()
{
return resource;
}

@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result
+ ((unqualifiedVersionlessIdValue == null) ? 0 : unqualifiedVersionlessIdValue.hashCode());
return result;
}

@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DomainResourceUniqueByUnqualifiedVersionlessId other = (DomainResourceUniqueByUnqualifiedVersionlessId) obj;
if (unqualifiedVersionlessIdValue == null)
{
if (other.unqualifiedVersionlessIdValue != null)
return false;
}
else if (!unqualifiedVersionlessIdValue.equals(other.unqualifiedVersionlessIdValue))
return false;
return true;
}
}

private static final List<String> RESOURCES_WITH_PATIENT_REF = Arrays.asList("AllergyIntolerance", "CarePlan",
"CareTeam", "ClinicalImpression", "Composition", "Condition", "Consent", "DetectedIssue", "DeviceRequest",
"DeviceUseStatement", "DiagnosticReport", "DocumentManifest", "DocumentReference", "Encounter",
Expand Down Expand Up @@ -624,4 +672,10 @@ public void updatePatient(Patient patient)
throw e;
}
}

protected Stream<DomainResource> distinctById(Stream<DomainResource> resources)
{
return resources.map(DomainResourceUniqueByUnqualifiedVersionlessId::new).distinct()
.map(DomainResourceUniqueByUnqualifiedVersionlessId::getResource);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@

import static de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.ConstantsDataTransfer.NAMING_SYSTEM_NUM_CODEX_CRR_PSEUDONYM;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;

import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -28,6 +35,8 @@ public class FhirBridgeClient extends AbstractComplexFhirClient
private static final Logger logger = LoggerFactory.getLogger(FhirBridgeClient.class);
private static final OutcomeLogger outcomeLogger = new OutcomeLogger(logger);

private static final String NUM_CODEX_BLOOD_GAS_PANEL = "https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/blood-gas-panel";

/**
* @param geccoClient
* not <code>null</code>
Expand All @@ -43,14 +52,16 @@ public void storeBundle(Bundle bundle)
// either bundle has a patient, or patient should already exists
Patient patient = createOrUpdatePatient(bundle).orElseGet(() -> getExistingPatientOrThrow(bundle));

Map<String, IdType> resourceIdsByUuid = new HashMap<>();
for (int i = 0; i < bundle.getEntry().size(); i++)
{
BundleEntryComponent entry = bundle.getEntry().get(i);

if (isEntrySupported(entry, e -> !(e.getResource() instanceof Patient)))
createOrUpdateEntry(i, entry, patient);
createOrUpdateEntry(i, entry, patient, resourceIdsByUuid);

// only log for non Patients
else if (!entry.hasResource() || !(entry.getResource() instanceof Patient))
// only log for non Patients
logger.warn("Bundle entry at index {} not supported, ignoring entry", i);
}
}
Expand Down Expand Up @@ -220,14 +231,18 @@ private Optional<Patient> create(Patient newPatient, String pseudonym, String bu
}
}

private void createOrUpdateEntry(int index, BundleEntryComponent entry, Patient patient)
private void createOrUpdateEntry(int index, BundleEntryComponent entry, Patient patient,
Map<String, IdType> resourceIdsByUuid)
{
Resource resource = entry.getResource();
String url = entry.getRequest().getUrl();

Optional<Resource> existingResource = findResourceInLocalFhirStore(url, resource.getClass());
existingResource.ifPresentOrElse(existing -> update(existing, resource, entry.getFullUrl()),
() -> create(resource, entry.getFullUrl()));
IdType resourceId = existingResource.map(
existing -> update(existing, fixTemporaryReferences(resource, resourceIdsByUuid), entry.getFullUrl()))
.orElseGet(() -> create(fixTemporaryReferences(resource, resourceIdsByUuid), entry.getFullUrl()));

resourceIdsByUuid.put(entry.getFullUrl(), resourceId);
}

private Optional<Resource> findResourceInLocalFhirStore(String url, Class<? extends Resource> resourceType)
Expand Down Expand Up @@ -302,7 +317,43 @@ private Optional<Resource> findResourceInLocalFhirStore(String url, Class<? exte
}
}

private void update(Resource existingResource, Resource newResource, String bundleFullUrl)
private Resource fixTemporaryReferences(Resource resource, Map<String, IdType> resourceIdsByUuid)
{
if (resource == null)
return null;

else if (resource instanceof Observation)
{
if (resource.getMeta().getProfile().stream().map(CanonicalType::getValue)
.anyMatch(url -> NUM_CODEX_BLOOD_GAS_PANEL.equals(url)
|| (url != null && url.startsWith(NUM_CODEX_BLOOD_GAS_PANEL + "|"))))
{
Observation observation = (Observation) resource;
List<Reference> members = observation.getHasMember();
for (int i = 0; i < members.size(); i++)
{
Reference member = members.get(i);
if (member.hasReference())
{
String uuid = member.getReference();
IdType resourceId = resourceIdsByUuid.get(uuid);

if (resourceId != null)
{
logger.debug(
"Replacing reference at Observation.hasMember[{}] from bundle resource {} with existing resource id",
i, resource.getIdElement().getValue());
member.setReferenceElement(resourceId);
}
}
}
}
}

return resource;
}

private IdType update(Resource existingResource, Resource newResource, String bundleFullUrl)
{
logger.debug("Updating {}", newResource.getResourceType().name());

Expand All @@ -315,7 +366,7 @@ private void update(Resource existingResource, Resource newResource, String bund

if (outcome.getId() == null)
{
logger.warn("Could not update {} {}", newResource.getResourceType().name(),
logger.warn("Could not update {} {}: unknown reason", newResource.getResourceType().name(),
newResource.getIdElement().toString());
if (outcome.getOperationOutcome() != null && outcome.getOperationOutcome() instanceof OperationOutcome)
outcomeLogger.logOutcome((OperationOutcome) outcome.getOperationOutcome());
Expand All @@ -324,7 +375,15 @@ private void update(Resource existingResource, Resource newResource, String bund
+ newResource.getIdElement().toString());
}
else if (outcome.getOperationOutcome() != null && outcome.getOperationOutcome() instanceof OperationOutcome)
{
outcomeLogger.logOutcome((OperationOutcome) outcome.getOperationOutcome());
logger.warn("Could not update {} {}: unknown reason", newResource.getResourceType().name(),
newResource.getIdElement().toString());
throw new RuntimeException("Could not create " + newResource.getResourceType().name() + " "
+ newResource.getIdElement().toString() + ": unknown reason");
}
else
return (IdType) outcome.getId();
}
catch (UnprocessableEntityException e)
{
Expand Down Expand Up @@ -367,7 +426,7 @@ else if (outcome.getOperationOutcome() != null && outcome.getOperationOutcome()
}
}

private void create(Resource newResource, String bundleFullUrl)
private IdType create(Resource newResource, String bundleFullUrl)
{
logger.debug("Creating {}", newResource.getResourceType().name());

Expand All @@ -387,7 +446,15 @@ private void create(Resource newResource, String bundleFullUrl)
+ newResource.getIdElement().toString());
}
else if (outcome.getOperationOutcome() != null && outcome.getOperationOutcome() instanceof OperationOutcome)
{
outcomeLogger.logOutcome((OperationOutcome) outcome.getOperationOutcome());
logger.warn("Could not create {} {}: unknown reason", newResource.getResourceType().name(),
newResource.getIdElement().toString());
throw new RuntimeException("Could not create " + newResource.getResourceType().name() + " "
+ newResource.getIdElement().toString() + ": unknown reason");
}
else
return (IdType) outcome.getId();
}
catch (UnprocessableEntityException e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public Stream<DomainResource> getNewData(String pseudonym, DateWithPrecision exp
logger.debug("Search-Bundle result: {}",
geccoClient.getFhirContext().newJsonParser().encodeResourceToString(resultBundle));

return resultBundle.getEntry().stream().filter(BundleEntryComponent::hasResource)
return distinctById(resultBundle.getEntry().stream().filter(BundleEntryComponent::hasResource)
.map(BundleEntryComponent::getResource).filter(r -> r instanceof Bundle).map(r -> (Bundle) r)
.flatMap(this::getDomainResources);
.flatMap(this::getDomainResources));
}
}
Loading

0 comments on commit 44293c2

Please sign in to comment.