use of org.hl7.fhir.r4.model.Composition.DocumentRelationshipType.REPLACES in project hl7v2-fhir-converter by LinuxForHealth.
the class HL7MergeFHIRConversionTest method validateHappyPathADT_A34WithMRG.
// Test ADT_A34 with one MRG segment (the most it supports).
@Test
void validateHappyPathADT_A34WithMRG() {
String hl7message = "MSH|^~\\&|SENDING_APPLICATION|SENDING_FACILITY|RECEIVING_APPLICATION|RECEIVING_FACILITY|||ADT^A34||P|2.3||||\r" + "EVN|A40|20110613122406637||01\r" + // Identifier value 234 with no system and no identifier type
"PID|1||123^^^^MR||||||||||||||||||||||||||||||||||||\r" + // Identifier value 456 with no system and no identifier type
"MRG|456||||||\r";
// Convert hl7 message
List<BundleEntryComponent> e = ResourceUtils.createFHIRBundleFromHL7MessageReturnEntryList(ftv, hl7message);
// Find the patient resources in the FHIR bundle.
List<Resource> patientResources = ResourceUtils.getResourceList(e, ResourceType.Patient);
// There should be 2 - One for the PID segment and one for the MRG segment
assertThat(patientResources).hasSize(2);
// Get first patient and associated identifiers and id.
Patient patientOne = PatientUtils.getPatientFromResource(patientResources.get(0));
String patientOneId = patientOne.getId();
List<Identifier> patientOneIdentifierList = patientOne.getIdentifier();
// Get second patient and associated identifiers and id.
Patient patientTwo = PatientUtils.getPatientFromResource(patientResources.get(1));
String patientTwoId = patientTwo.getId();
List<Identifier> patientTwoIdentifierList = patientTwo.getIdentifier();
/*-----------Verify Patient One-----------*/
// Verify patient one's identifier is set correctly (this is the patient build
// from the PID segment)
//
// "identifier":[
// {
// "type":{
// "coding":[
// {
// "system":"http://terminology.hl7.org/CodeSystem/v2-0203",
// "code":"MR",
// "display":"Medical record number"
// }
// ]
// },
// "value":"123"
// },
// {
// "use":"old",
// "value":"456"
// }
// ]
// Verify the patient has two identifiers
assertThat(patientOneIdentifierList).hasSize(2);
// Verify the identifier values are correct.
assertThat(patientOneIdentifierList.get(0).getValue()).isEqualTo("123");
assertThat(patientOneIdentifierList.get(1).getValue()).isEqualTo("456");
// Verify the system values are not present
assertThat(patientOneIdentifierList.get(0).getSystem()).isNull();
assertThat(patientOneIdentifierList.get(1).getSystem()).isNull();
// Verify the first identifier has no use field.
assertThat(patientOneIdentifierList.get(0).getUse()).isNull();
// Verify the second identifier is marked as old
assertThat(patientOneIdentifierList.get(1).getUse()).isEqualTo(Identifier.IdentifierUse.OLD);
// Verify identifier type is set correctly for the first identifier
CodeableConcept patientOneIdentifierType = patientOneIdentifierList.get(0).getType();
assertThat(patientOneIdentifierType.getCoding().get(0).getSystem()).isEqualTo("http://terminology.hl7.org/CodeSystem/v2-0203");
assertThat(patientOneIdentifierType.getCoding().get(0).getDisplay()).isEqualTo("Medical record number");
assertThat(patientOneIdentifierType.getCoding().get(0).getCode()).isEqualTo("MR");
// Verify the identifier type for the second identifier is not present.
assertThat(patientOneIdentifierList.get(1).getType().getCoding()).isEmpty();
// Verify first patient has these fields
//
// "active":true,
// "link":[
// {
// "other":{
// "reference":"Patient/expiringID-MRG"
// },
// "type":"replaces"
// }
// ]
// The first patient should be active.
assertThat(patientOne.getActive()).isTrue();
// Verify link.other.reference references the MRG (2nd) patient with of a type of 'replaces'
PatientLinkComponent linkOne = patientOne.getLink().get(0);
assertThat(linkOne.getType()).isEqualTo(Patient.LinkType.REPLACES);
assertThat(linkOne.getOther().getReference()).isEqualTo(patientTwoId);
/*-----------Verify Patient Two-----------*/
// Verify patient two's identifier is set correctly (this is the patient build
// from the MRG segment)
//
// "identifier":[
// {
// "use":"old",
// "value":"456"
// }
// ]
// Verify has only 1 identifier
assertThat(patientTwoIdentifierList).hasSize(1);
// Verify the identifier value is correct.
assertThat(patientTwoIdentifierList.get(0).getValue()).isEqualTo("456");
// Verify the system is not present
assertThat(patientTwoIdentifierList.get(0).getSystem()).isNull();
// Verify the identifier is marked as old
assertThat(patientTwoIdentifierList.get(0).getUse()).isEqualTo(Identifier.IdentifierUse.OLD);
// Verify identifier type is not present
assertThat(patientTwoIdentifierList.get(0).getType().getCoding()).isEmpty();
// Verify second patient has these fields.
// "active":false,
// "link":[
// {
// "other":{
// "reference":"Patient/survivingID"
// },
// "type":"replaced-by"
// }
// ]
// The second patient should NOT be active.
assertThat(patientTwo.getActive()).isFalse();
// We should have link.other.reference to the PID (1st) patient with a type of 'replaced-by'
PatientLinkComponent linkTwo = patientTwo.getLink().get(0);
assertThat(linkTwo.getType()).isEqualTo(Patient.LinkType.REPLACEDBY);
assertThat(linkTwo.getOther().getReference()).isEqualTo(patientOneId);
;
}
use of org.hl7.fhir.r4.model.Composition.DocumentRelationshipType.REPLACES in project synthea by synthetichealth.
the class FhirR4 method mapCodeToCodeableConcept.
/**
* Helper function to convert a Code into a CodeableConcept. Takes an optional system, which
* replaces the Code.system in the resulting CodeableConcept if not null.
*
* @param from The Code to create a CodeableConcept from.
* @param system The system identifier, such as a URI. Optional; may be null.
* @return The converted CodeableConcept
*/
private static CodeableConcept mapCodeToCodeableConcept(Code from, String system) {
CodeableConcept to = new CodeableConcept();
system = system == null ? null : ExportHelper.getSystemURI(system);
from.system = ExportHelper.getSystemURI(from.system);
if (from.display != null) {
to.setText(from.display);
}
Coding coding = new Coding();
coding.setCode(from.code);
coding.setDisplay(from.display);
if (from.system == null) {
coding.setSystem(system);
} else {
coding.setSystem(from.system);
}
to.addCoding(coding);
return to;
}
use of org.hl7.fhir.r4.model.Composition.DocumentRelationshipType.REPLACES in project synthea by synthetichealth.
the class FHIRSTU3ExporterTest method testFHIRSTU3Export.
@Test
public void testFHIRSTU3Export() throws Exception {
TestHelper.loadTestProperties();
Generator.DEFAULT_STATE = Config.get("test_state.default", "Massachusetts");
Config.set("exporter.baseDirectory", tempFolder.newFolder().toString());
FhirContext ctx = FhirStu3.getContext();
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
FhirValidator validator = ctx.newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResources validationResources = new ValidationResources();
List<String> errors = ParallelTestingService.runInParallel((person) -> {
List<String> validationErrors = new ArrayList<String>();
TestHelper.exportOff();
Config.set("exporter.fhir_stu3.export", "true");
Config.set("exporter.fhir.use_shr_extensions", "true");
FhirStu3.TRANSACTION_BUNDLE = person.randBoolean();
String fhirJson = FhirStu3.convertToFHIRJson(person, System.currentTimeMillis());
// (these should have been converted into URIs)
if (fhirJson.contains("SNOMED-CT")) {
validationErrors.add("JSON contains unconverted references to 'SNOMED-CT' (should be URIs)");
}
// let's crack open the Bundle and validate
// each individual entry.resource to get context-sensitive error
// messages...
Bundle bundle = parser.parseResource(Bundle.class, fhirJson);
for (BundleEntryComponent entry : bundle.getEntry()) {
ValidationResult eresult = validator.validateWithResult(entry.getResource());
if (!eresult.isSuccessful()) {
for (SingleValidationMessage emessage : eresult.getMessages()) {
boolean valid = false;
if (emessage.getMessage().contains("@ Observation obs-7")) {
/*
* The obs-7 invariant basically says that Observations should have values, unless
* they are made of components. This test replaces an invalid XPath expression
* that was causing correct instances to fail validation.
*/
valid = validateObs7((Observation) entry.getResource());
} else if (emessage.getMessage().contains("@ Condition con-4")) {
/*
* The con-4 invariant says "If condition is abated, then clinicalStatus must be
* either inactive, resolved, or remission" which is very clear and sensical.
* However, the XPath expression does not evaluate correctly for valid instances,
* so we must manually validate.
*/
valid = validateCon4((Condition) entry.getResource());
} else if (emessage.getMessage().contains("@ MedicationRequest mps-1")) {
/*
* The mps-1 invariant says MedicationRequest.requester.onBehalfOf can only be
* specified if MedicationRequest.requester.agent is practitioner or device.
* But the invariant is poorly written and does not correctly handle references
* starting with "urn:uuid"
*/
// ignore this error
valid = true;
} else if (emessage.getMessage().contains("per-1: If present, start SHALL have a lower value than end")) {
/*
* The per-1 invariant does not account for daylight savings time... so, if the
* daylight savings switch happens between the start and end, the validation
* fails, even if it is valid.
*/
// ignore this error
valid = true;
}
if (!valid) {
System.out.println(parser.encodeResourceToString(entry.getResource()));
System.out.println("ERROR: " + emessage.getMessage());
validationErrors.add(emessage.getMessage());
}
}
}
// Check ExplanationOfBenefit Resources against BlueButton
if (entry.getResource().fhirType().equals("ExplanationOfBenefit")) {
ValidationResult bbResult = validationResources.validateSTU3(entry.getResource());
for (SingleValidationMessage message : bbResult.getMessages()) {
if (message.getMessage().contains("extension https://bluebutton.cms.gov/assets")) {
/*
* The instance validator complains about the BlueButton extensions, ignore
*/
continue;
} else if (message.getSeverity() == ResultSeverityEnum.ERROR) {
if (!(message.getMessage().contains("Element 'ExplanationOfBenefit.id': minimum required = 1, but only found 0") || message.getMessage().contains("Could not verify slice for profile"))) {
// For some reason the validator is not detecting the IDs on the resources,
// even though they appear to be present while debugging and during normal
// operations.
System.out.println(message.getSeverity() + ": " + message.getMessage());
Assert.fail(message.getSeverity() + ": " + message.getMessage());
}
}
}
}
}
if (!validationErrors.isEmpty()) {
Exporter.export(person, System.currentTimeMillis());
}
return validationErrors;
});
assertTrue("Validation of exported FHIR bundle failed: " + String.join("|", errors), errors.size() == 0);
}
use of org.hl7.fhir.r4.model.Composition.DocumentRelationshipType.REPLACES in project synthea by synthetichealth.
the class FhirStu3 method mapCodeToCodeableConcept.
/**
* Helper function to convert a Code into a CodeableConcept. Takes an optional system, which
* replaces the Code.system in the resulting CodeableConcept if not null.
*
* @param from The Code to create a CodeableConcept from.
* @param system The system identifier, such as a URI. Optional; may be null.
* @return The converted CodeableConcept
*/
private static CodeableConcept mapCodeToCodeableConcept(Code from, String system) {
CodeableConcept to = new CodeableConcept();
system = system == null ? null : ExportHelper.getSystemURI(system);
from.system = ExportHelper.getSystemURI(from.system);
if (from.display != null) {
to.setText(from.display);
}
Coding coding = new Coding();
coding.setCode(from.code);
coding.setDisplay(from.display);
if (from.system == null) {
coding.setSystem(system);
} else {
coding.setSystem(from.system);
}
to.addCoding(coding);
return to;
}
use of org.hl7.fhir.r4.model.Composition.DocumentRelationshipType.REPLACES in project summary-care-record-api by NHSDigital.
the class GpSummaryMapper method map.
@SneakyThrows
public List<Resource> map(Node document) {
var gpSummaryId = xmlUtils.getValueByXPath(document, GP_SUMMARY_ID_XPATH);
var gpSummaryCodeCode = xmlUtils.getValueByXPath(document, GP_SUMMARY_CODE_CODE_XPATH);
var gpSummaryCodeDisplayName = xmlUtils.getValueByXPath(document, GP_SUMMARY_CODE_DISPLAY_NAME_XPATH);
var gpSummaryStatusCode = xmlUtils.getValueByXPath(document, GP_SUMMARY_STATUS_CODE_XPATH);
var gpSummaryEffectiveTime = parseDate(xmlUtils.getValueByXPath(document, GP_SUMMARY_EFFECTIVE_TIME_XPATH), InstantType.class);
var authorTime = parseDate(xmlUtils.getValueByXPath(document, GP_SUMMARY_AUTHOR_TIME_XPATH), DateTimeType.class);
var replacementOfPriorMessageRefIdRoot = xmlUtils.getOptionalValueByXPath(document, REPLACEMENT_OF_PRIOR_MESSAGE_REF_ID_ROOT_XPATH);
var pertinentRootCreTypeCodeCode = xmlUtils.getValueByXPath(document, PERTINENT_ROOT_CRE_TYPE_CODE_CODE_XPATH);
var pertinentRootCreTypeCodeDisplayName = xmlUtils.getValueByXPath(document, PERTINENT_ROOT_CRE_TYPE_CODE_DISPLAY_NAME_XPATH);
var presentationTextValue = xmlUtils.detachOptionalNodeByXPath(document, PRESENTATION_TEXT_VALUE);
var eventId = xmlUtils.getValueByXPath(document, EVENT_ID_XPATH);
List<Resource> resources = new ArrayList<>();
var composition = new Composition();
composition.setId(eventId);
composition.setIdentifier(new Identifier().setValue(gpSummaryId).setSystem("https://tools.ietf.org/html/rfc4122"));
composition.setType(new CodeableConcept().addCoding(new Coding().setCode(gpSummaryCodeCode).setSystem(SNOMED_SYSTEM).setDisplay(gpSummaryCodeDisplayName)));
composition.setMeta(new Meta().setLastUpdatedElement(gpSummaryEffectiveTime));
composition.setStatus(mapCompositionStatus(gpSummaryStatusCode));
composition.setDateElement(authorTime);
replacementOfPriorMessageRefIdRoot.ifPresent(val -> composition.addRelatesTo(new Composition.CompositionRelatesToComponent().setTarget(new Identifier().setValue(val)).setCode(REPLACES)));
composition.addCategory(new CodeableConcept().addCoding(new Coding().setCode(pertinentRootCreTypeCodeCode).setSystem(SNOMED_SYSTEM).setDisplay(pertinentRootCreTypeCodeDisplayName)));
Map<String, List<String>> references = sectionReferences(document);
presentationTextValue.map(htmlParser::parse).map(Collection::stream).ifPresent(it -> it.forEach(section -> {
if (section.getTitle() != null && CODED_ENTRY_RESOURCE_MAP.keySet().contains(section.getTitle()) && references.containsKey(section.getTitle())) {
for (String codedEntryId : references.get(section.getTitle())) {
section.addEntry(new Reference(CODED_ENTRY_RESOURCE_MAP.get(section.getTitle()) + "/" + codedEntryId));
}
}
composition.addSection(section);
}));
resources.add(composition);
addAuthor(document, resources, composition);
return resources;
}
Aggregations