From 9df9f912b04f60b1e59a6a313fc31bc0b3517de1 Mon Sep 17 00:00:00 2001 From: Volker Schmidt Date: Wed, 14 Nov 2018 23:07:44 +0100 Subject: [PATCH] Added WHO tracked entity type and its attributes. --- README.md | 13 +- app/pom.xml | 2 +- .../org/dhis2/fhir/adapter/DemoClient.java | 71 ++- .../dhis2/fhir/adapter/WebSecurityConfig.java | 1 + .../fhir/adapter/setup/ReferenceSetup.java | 118 ++++ .../setup/RemoteSubscriptionFhirSetup.java | 12 + .../org/dhis2/fhir/adapter/setup/Setup.java | 17 + .../fhir/adapter/setup/SetupController.java | 2 +- .../fhir/adapter/setup/SetupService.java | 87 ++- .../adapter/setup/TrackedEntitySetup.java | 199 ++++++ .../main/resources/default-application.yml | 3 + app/src/main/resources/templates/setup.html | 155 ++++- .../org/dhis2/fhir/adapter/model/Gender.java | 39 ++ .../fhir/adapter/dhis/model/Reference.java | 2 + .../ImmutableTrackedEntityAttribute.java | 15 + .../trackedentity/TrackedEntityAttribute.java | 5 + .../trackedentity/TrackedEntityInstance.java | 19 + .../WritableTrackedEntityAttribute.java | 30 +- .../TrackedEntityMetadataServiceImpl.java | 5 +- ...stu3AddressFhirToDhisTransformerUtils.java | 45 ++ .../Dstu3CodeFhirToDhisTransformerUtils.java | 22 +- ...ontactPointFhirToDhisTransformerUtils.java | 107 ++++ ...tu3DateTimeFhirToDhisTransformerUtils.java | 20 + ...FhirClientFhirToDhisTransformerUtils.java} | 6 +- .../Dstu3GeoFhirToDhisTransformerUtils.java | 2 +- ...3IdentifierFhirToDhisTransformerUtils.java | 6 +- ...ObservationFhirToDhisTransformerUtils.java | 6 +- ...ganizationFhirToDhisTransformerUtils.java} | 6 +- ...stu3PatientFhirToDhisTransformerUtils.java | 97 +++ fhir/pom.xml | 1 + fhir/src/main/asciidoc/api-guide.adoc | 25 + .../IgnoredSubscriptionResourceException.java | 45 ++ ...ueuedRemoteFhirResourceRepositoryImpl.java | 30 +- ...moteSubscriptionRequestRepositoryImpl.java | 30 +- .../fhir/metadata/model/AbstractRule.java | 15 +- .../fhir/metadata/model/ConstantCategory.java | 2 +- .../adapter/fhir/metadata/model/DataType.java | 4 + .../fhir/metadata/model/FhirResourceType.java | 3 +- .../metadata/model/MappedTrackedEntity.java | 135 +++++ .../metadata/model/RemoteSubscription.java | 2 +- .../model/RemoteSubscriptionResource.java | 14 + .../metadata/model/TrackedEntityRule.java | 49 +- .../metadata/model/TransformDataType.java | 3 +- .../repository/CodeCategoryRepository.java | 2 +- .../metadata/repository/CodeRepository.java | 7 +- .../MappedTrackedEntityRepository.java | 94 +++ .../CodeFindAllBySystemCodesKeyGenerator.java | 62 ++ ...reateSaveMappedTrackedEntityValidator.java | 83 +++ ...eCreateSaveTrackedEntityRuleValidator.java | 16 +- .../fhir/queue/QueueListenerErrorHandler.java | 22 +- .../impl/RemoteRestHookProcessorImpl.java | 48 +- ...emoteHierarchicallyFhirRepositoryImpl.java | 27 +- .../repository/impl/FhirRepositoryImpl.java | 77 ++- .../FhirToDhisTransformerContext.java | 5 + .../FhirToDhisTransformerService.java | 2 +- .../impl/AbstractFhirToDhisTransformer.java | 15 +- .../FhirToDhisTransformerContextImpl.java | 10 + .../FhirToDhisTransformerServiceImpl.java | 7 +- .../FhirToProgramStageTransformer.java | 7 +- .../FhirToTrackedEntityTransformer.java | 113 +++- ...WritableScriptedTrackedEntityInstance.java | 51 +- ...ractAddressFhirToDhisTransformerUtils.java | 14 + ...bstractCodeFhirToDhisTransformerUtils.java | 63 +- ...ontactPointFhirToDhisTransformerUtils.java | 64 ++ ...actDateTimeFhirToDhisTransformerUtils.java | 3 + ...FhirClientFhirToDhisTransformerUtils.java} | 4 +- ...AbstractGeoFhirToDhisTransformerUtils.java | 2 +- ...tIdentifierFhirToDhisTransformerUtils.java | 10 +- ...bservationFhirToDhisTransformerUtils.java} | 8 +- ...ganizationFhirToDhisTransformerUtils.java} | 40 +- ...ractPatientFhirToDhisTransformerUtils.java | 60 ++ ... ReferenceFhirToDhisTransformerUtils.java} | 69 ++- .../production/V1.0.0.0_0_0__Initial.sql | 540 +++++++++++++++-- .../static/dhis/metadata/person-metadata.json | 568 ++++++++++++++++++ ...riptionResourceRepositoryRestDocsTest.java | 2 + .../RuleRepositoryRestDocsTest.java | 58 +- .../TrackedEntityRepositoryRestDocsTest.java | 138 +++++ fhir/src/test/resources/data.sql | 18 +- .../repository/createTrackedEntity.json | 13 + .../repository/createTrackedEntityRule.json | 9 +- 80 files changed, 3491 insertions(+), 310 deletions(-) create mode 100644 app/src/main/java/org/dhis2/fhir/adapter/setup/ReferenceSetup.java create mode 100644 app/src/main/java/org/dhis2/fhir/adapter/setup/TrackedEntitySetup.java create mode 100644 common/src/main/java/org/dhis2/fhir/adapter/model/Gender.java create mode 100644 fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3ContactPointFhirToDhisTransformerUtils.java rename fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/{Dstu3FhirClientTransformUtils.java => Dstu3FhirClientFhirToDhisTransformerUtils.java} (92%) rename fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/{Dstu3OrganizationTransformerUtils.java => Dstu3OrganizationFhirToDhisTransformerUtils.java} (93%) create mode 100644 fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3PatientFhirToDhisTransformerUtils.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/IgnoredSubscriptionResourceException.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackedEntity.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/MappedTrackedEntityRepository.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/cache/CodeFindAllBySystemCodesKeyGenerator.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveMappedTrackedEntityValidator.java create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractContactPointFhirToDhisTransformerUtils.java rename fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/{AbstractFhirClientTransformUtils.java => AbstractFhirClientFhirToDhisTransformerUtils.java} (97%) rename fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/{AbstractObservationTransformUtils.java => AbstractObservationFhirToDhisTransformerUtils.java} (88%) rename fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/{AbstractOrganizationTransformerUtils.java => AbstractOrganizationFhirToDhisTransformerUtils.java} (85%) create mode 100644 fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractPatientFhirToDhisTransformerUtils.java rename fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/{ReferenceTransformUtils.java => ReferenceFhirToDhisTransformerUtils.java} (71%) create mode 100644 fhir/src/main/resources/static/dhis/metadata/person-metadata.json create mode 100644 fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRepositoryRestDocsTest.java create mode 100644 fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntity.json diff --git a/README.md b/README.md index 029a13d0..a862822d 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,13 @@ Depending on the country specific use of FHIR organization and location resource ## Running the Adapter ### Dependent Software Components #### DHIS2 -DHIS 2.29 or newer must be installed. +DHIS 2.30 or newer must be installed. -The standard tracked entity type Person with first and last name tracked entity attribute must be available. For example, this tracked entity type can be found at [DHIS 2 Play](https://play.dhis2.org/). +The application includes a suggested tracked entity type person. The corresponding metadata for DHIS 2.30 is located at [Person Metadata](fhir/src/main/resources/static/dhis/metadata/person-metadata.json). It can also be accessed from the setup application + that is described below. This allows also to customize the mapping to the tracked entity type for the FHIR resource patient. #### FHIR Service -A FHIR Service that provides the FHIR Endpoints and also supports FHIR Subscriptions is required. HAPI FHIR JPA Server Example 3.5.0 or later can be used. Instructions on how to setup the FHIR Service can be found at http://hapifhir.io/doc_jpa.html. +A FHIR Service that provides the FHIR Endpoints and also supports FHIR Subscriptions is required. HAPI FHIR JPA Server Example 3.6.0 or later can be used. Instructions on how to setup the FHIR Service can be found at http://hapifhir.io/doc_jpa.html. The initial subscription for FHIR Resource Patient is created by the initial setup user interface that is described later in this document. @@ -57,6 +58,10 @@ Exit the console and return to your previous user with \q followed by exit. The database tables will be created on first startup of the Adapter automatically. +### Downloading +You can download the latest build of the Adapter at [Download WAR](https://s3-eu-west-1.amazonaws.com/releases.dhis2.org/fhir/dhis2-fhir-adapter.war). Alternatively you can also build the application from its source code as described below. Otherwise you +can continue with the configuration section of this document. + ### Building In order to build the adapter Java Development Kit 8 and Maven 3.2 or later is required. No additional repositories need to be configured in Maven configuration. The following command builds the artifact dhis2-fhir-adapter.war in sub-directory app/target. @@ -88,7 +93,7 @@ The following example contains the content of configuration file application.yml # The base URL of the DHIS2 installation. url: http://localhost:8080 # The API version that should be accessed on the DHIS2 installation. - api-version: 29 + api-version: 30 # Authentication data to access metadata on DHIS2 installation. # The complete metadata (organization units, tracked entity types, # tracked entity attributes, tracker programs, tracker program stages) diff --git a/app/pom.xml b/app/pom.xml index 499395ea..db040232 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -47,7 +47,7 @@ admin district http://localhost:8080 - 29 + 30 diff --git a/app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java b/app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java index f7b5ed05..14671a8b 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java @@ -35,6 +35,7 @@ import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.ContactPoint; import org.hl7.fhir.dstu3.model.DateTimeType; import org.hl7.fhir.dstu3.model.DecimalType; import org.hl7.fhir.dstu3.model.Enumerations; @@ -47,12 +48,14 @@ import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Quantity; import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.RelatedPerson; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.codesystems.ObservationCategory; import java.math.BigDecimal; import java.time.LocalDate; import java.time.ZoneId; +import java.util.Collections; import java.util.Date; /** @@ -116,6 +119,10 @@ public static void main( String[] args ) .setValue( "XZY123456" ); org.setPartOf( new Reference( hospitalOrg.getIdElement() ) ); + Organization birthOrg = new Organization(); + birthOrg.setName( "Birth Unit" ); + birthOrg.setPartOf( new Reference( hospitalOrg.getIdElement() ) ); + ////////////////////////////////// // Create Patient (new born child) ////////////////////////////////// @@ -135,6 +142,8 @@ public static void main( String[] args ) TemporalPrecisionEnum.DAY ); child.setGender( Enumerations.AdministrativeGender.MALE ); child.addAddress() + .addLine( "Water Road 675" ) + .addLine( "Apartment 62" ) .setCity( "Freetown" ) .setCountry( "Sierra Leone" ); child.getAddress().get( 0 ) @@ -176,7 +185,7 @@ public static void main( String[] args ) .addExtension( new Extension() .setUrl( "longitude" ) .setValue( new DecimalType( -13.262743 ) ) ); - mother.setManagingOrganization( new Reference( org.getId() ) ); + mother.setManagingOrganization( new Reference( birthOrg ) ); ////////////////////// // Create Vaccinations @@ -463,7 +472,67 @@ public static void main( String[] args ) .getRequest() .setMethod( Bundle.HTTPVerb.POST ) .setUrl( "Observation" ); + client.transaction().withBundle( bundle ).execute(); + Bundle searchResult = client.search().forResource( Patient.class ).returnBundle( Bundle.class ) + .whereMap( Collections.singletonMap( "identifier", Collections.singletonList( "http://example.sl/patients|" + childNationalId ) ) ).execute(); + child = (Patient) searchResult.getEntry().get( 0 ).getResource(); + searchResult = client.search().forResource( Patient.class ).returnBundle( Bundle.class ) + .whereMap( Collections.singletonMap( "identifier", Collections.singletonList( "http://example.sl/patients|" + motherNationalId ) ) ).execute(); + mother = (Patient) searchResult.getEntry().get( 0 ).getResource(); + + RelatedPerson childRelatedPerson = new RelatedPerson(); + childRelatedPerson.setActive( true ); + childRelatedPerson.getRelationship().addCoding( + new Coding().setSystem( "http://hl7.org/fhir/v3/RoleCode" ).setCode( "MTH" ) ); + childRelatedPerson.setGender( Enumerations.AdministrativeGender.FEMALE ); + childRelatedPerson.addName().setFamily( "West" ).addGiven( "Elizabeth" ); + childRelatedPerson.addTelecom().setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "(123) 456-7890.10" ).setRank( 1 ).setUse( ContactPoint.ContactPointUse.OLD ); + childRelatedPerson.addTelecom().setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "(723) 456-7890.10" ).setRank( 2 ).setUse( ContactPoint.ContactPointUse.HOME ); + childRelatedPerson.getPatient().setReferenceElement( child.getIdElement().toUnqualifiedVersionless() ); + child.addLink().setType( Patient.LinkType.SEEALSO ).getOther().setResource( childRelatedPerson ); + + RelatedPerson motherRelatedPerson = new RelatedPerson(); + motherRelatedPerson.setId( IdType.newRandomUuid() ); + motherRelatedPerson.addIdentifier().setSystem( "http://example.sl/relatedPersons" ).setValue( "mth" + motherNationalId ); + motherRelatedPerson.setActive( true ); + motherRelatedPerson.getRelationship().addCoding( + new Coding().setSystem( "http://hl7.org/fhir/v3/RoleCode" ).setCode( "MTH" ) ); + motherRelatedPerson.setGender( Enumerations.AdministrativeGender.FEMALE ); + motherRelatedPerson.addName().setFamily( "West" ).addGiven( "Maria" ); + motherRelatedPerson.addTelecom().setSystem( ContactPoint.ContactPointSystem.PHONE ).setValue( "(723) 456-7890.20" ).setRank( 2 ).setUse( ContactPoint.ContactPointUse.HOME ); + motherRelatedPerson.getPatient().setReferenceElement( mother.getIdElement().toUnqualifiedVersionless() ); + + bundle = new Bundle(); + bundle.setType( Bundle.BundleType.TRANSACTION ); + bundle.addEntry() + .setResource( motherRelatedPerson ) + .setFullUrl( motherRelatedPerson.getId() ) + .getRequest() + .setMethod( Bundle.HTTPVerb.PUT ) + .setUrl( "Patient?identifier=http://example.sl/relatedPersons|mth" + motherNationalId ); + bundle.addEntry() + .setResource( child ) + .setFullUrl( child.getId() ) + .getRequest() + .setMethod( Bundle.HTTPVerb.PUT ) + .setUrl( "Patient?identifier=http://example.sl/patients|" + childNationalId ); + client.transaction().withBundle( bundle ).execute(); + + searchResult = client.search().forResource( RelatedPerson.class ).returnBundle( Bundle.class ) + .whereMap( Collections.singletonMap( "identifier", Collections.singletonList( "http://example.sl/relatedPersons|mth" + motherNationalId ) ) ).execute(); + motherRelatedPerson = (RelatedPerson) searchResult.getEntry().get( 0 ).getResource(); + + mother.addLink().setType( Patient.LinkType.SEEALSO ).getOther().setReferenceElement( motherRelatedPerson.getIdElement().toUnqualifiedVersionless() ); + + bundle = new Bundle(); + bundle.setType( Bundle.BundleType.TRANSACTION ); + bundle.addEntry() + .setResource( mother ) + .setFullUrl( mother.getId() ) + .getRequest() + .setMethod( Bundle.HTTPVerb.PUT ) + .setUrl( "Patient?identifier=http://example.sl/patients|" + motherNationalId ); client.transaction().withBundle( bundle ).execute(); } } diff --git a/app/src/main/java/org/dhis2/fhir/adapter/WebSecurityConfig.java b/app/src/main/java/org/dhis2/fhir/adapter/WebSecurityConfig.java index fd7e9595..c291547e 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/WebSecurityConfig.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/WebSecurityConfig.java @@ -82,6 +82,7 @@ protected void configure( @Nonnull HttpSecurity http ) throws Exception .antMatchers( HttpMethod.GET, "/actuator/health" ).permitAll() .antMatchers( HttpMethod.GET, "/actuator/info" ).permitAll() .antMatchers( HttpMethod.GET, "/docs/**" ).permitAll() + .antMatchers( HttpMethod.GET, "/dhis/metadata/**" ).permitAll() .antMatchers( HttpMethod.OPTIONS, "/api/**" ).permitAll() .antMatchers( "/actuator/**" ).hasRole( AdapterAuthorities.ADMINISTRATION_AUTHORITY ) .anyRequest().authenticated() diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/ReferenceSetup.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/ReferenceSetup.java new file mode 100644 index 00000000..b9381085 --- /dev/null +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/ReferenceSetup.java @@ -0,0 +1,118 @@ +package org.dhis2.fhir.adapter.setup; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; + +/** + * Contains the setup of a single reference. + * + * @author volsch + */ +public class ReferenceSetup implements Serializable +{ + private static final long serialVersionUID = 767218823240343127L; + + private boolean enabled; + + @NotNull + private ReferenceType referenceType; + + @NotBlank + @Size( max = Reference.MAX_VALUE_LENGTH ) + private String referenceValue; + + public ReferenceSetup() + { + super(); + } + + public ReferenceSetup( @NotNull ReferenceType referenceType, @NotBlank @Size( max = Reference.MAX_VALUE_LENGTH ) String referenceValue ) + { + this.referenceType = referenceType; + this.referenceValue = referenceValue; + this.enabled = true; + } + + public boolean isEnabled() + { + return enabled; + } + + public void setEnabled( boolean enabled ) + { + this.enabled = enabled; + } + + public ReferenceType getReferenceType() + { + return referenceType; + } + + public void setReferenceType( ReferenceType referenceType ) + { + this.referenceType = referenceType; + } + + public String getReferenceValue() + { + return referenceValue; + } + + public void setReferenceValue( String referenceValue ) + { + this.referenceValue = referenceValue; + } + + @Nonnull + public Reference getReference() + { + return new Reference( getReferenceValue(), getReferenceType() ); + } + + @Nonnull + public String getMandatoryRefVal() + { + return getReference().toString(); + } + + @Nullable + public String getOptionalRefVal() + { + return isEnabled() ? getMandatoryRefVal() : null; + } +} diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionFhirSetup.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionFhirSetup.java index a10dcc6a..92f8051a 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionFhirSetup.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/RemoteSubscriptionFhirSetup.java @@ -65,6 +65,8 @@ public class RemoteSubscriptionFhirSetup implements Serializable @Min( value = 0, message = "Must be a positive value." ) private int toleranceMillis = 5_000; + private boolean supportsRelatedPerson; + public String getBaseUrl() { return baseUrl; @@ -114,4 +116,14 @@ public void setToleranceMillis( int toleranceMillis ) { this.toleranceMillis = toleranceMillis; } + + public boolean isSupportsRelatedPerson() + { + return supportsRelatedPerson; + } + + public void setSupportsRelatedPerson( boolean supportsRelatedPerson ) + { + this.supportsRelatedPerson = supportsRelatedPerson; + } } diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/Setup.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/Setup.java index c57c19f8..c12a0d35 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/Setup.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/Setup.java @@ -29,6 +29,7 @@ */ import javax.validation.Valid; +import javax.validation.constraints.NotNull; import java.io.Serializable; /** @@ -41,11 +42,17 @@ public class Setup implements Serializable private static final long serialVersionUID = -5257148949174992807L; @Valid + @NotNull private RemoteSubscriptionSetup remoteSubscriptionSetup = new RemoteSubscriptionSetup(); @Valid + @NotNull private OrganizationCodeSetup organizationCodeSetup = new OrganizationCodeSetup(); + @Valid + @NotNull + private TrackedEntitySetup trackedEntitySetup = new TrackedEntitySetup(); + public RemoteSubscriptionSetup getRemoteSubscriptionSetup() { return remoteSubscriptionSetup; @@ -65,4 +72,14 @@ public void setOrganizationCodeSetup( OrganizationCodeSetup organizationCodeSetu { this.organizationCodeSetup = organizationCodeSetup; } + + public TrackedEntitySetup getTrackedEntitySetup() + { + return trackedEntitySetup; + } + + public void setTrackedEntitySetup( TrackedEntitySetup trackedEntitySetup ) + { + this.trackedEntitySetup = trackedEntitySetup; + } } diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupController.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupController.java index 6fe4a317..5e2a6744 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupController.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupController.java @@ -81,7 +81,7 @@ public String display( @Nonnull Model model, @Nonnull HttpServletRequest servlet } @PostMapping( "/setup" ) - public String submit( @Valid Setup setup, BindingResult bindingResult, Model model ) + public String submit( @Valid Setup setup, @Nonnull BindingResult bindingResult, @Nonnull Model model, @Nonnull HttpServletRequest servletRequest ) { if ( bindingResult.hasErrors() ) { diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupService.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupService.java index 96921779..ade6cf40 100644 --- a/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupService.java +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/SetupService.java @@ -34,9 +34,13 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.CodeCategory; import org.dhis2.fhir.adapter.fhir.metadata.model.ExecutableScriptArg; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; +import org.dhis2.fhir.adapter.fhir.metadata.model.MappedTrackedEntity; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; +import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionResource; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionSystem; import org.dhis2.fhir.adapter.fhir.metadata.model.RequestHeader; +import org.dhis2.fhir.adapter.fhir.metadata.model.Script; +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptArg; import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionAdapterEndpoint; import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionDhisEndpoint; import org.dhis2.fhir.adapter.fhir.metadata.model.SubscriptionFhirEndpoint; @@ -45,7 +49,9 @@ import org.dhis2.fhir.adapter.fhir.metadata.repository.CodeCategoryRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.CodeRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.ExecutableScriptArgRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.MappedTrackedEntityRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.RemoteSubscriptionRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.ScriptRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemCodeRepository; import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemRepository; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; @@ -60,8 +66,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -94,9 +102,14 @@ public class SetupService private final RemoteSubscriptionRepository remoteSubscriptionRepository; + private final MappedTrackedEntityRepository trackedEntityRepository; + + private final ScriptRepository scriptRepository; + public SetupService( @Nonnull CodeCategoryRepository codeCategoryRepository, @Nonnull CodeRepository codeRepository, @Nonnull SystemRepository systemRepository, @Nonnull SystemCodeRepository systemCodeRepository, - @Nonnull ExecutableScriptArgRepository executableScriptArgRepository, @Nonnull RemoteSubscriptionRepository remoteSubscriptionRepository ) + @Nonnull ExecutableScriptArgRepository executableScriptArgRepository, @Nonnull RemoteSubscriptionRepository remoteSubscriptionRepository, + @Nonnull ScriptRepository scriptRepository, @Nonnull MappedTrackedEntityRepository trackedEntityRepository ) { this.codeCategoryRepository = codeCategoryRepository; this.codeRepository = codeRepository; @@ -104,6 +117,8 @@ public SetupService( @Nonnull CodeCategoryRepository codeCategoryRepository, @No this.systemCodeRepository = systemCodeRepository; this.executableScriptArgRepository = executableScriptArgRepository; this.remoteSubscriptionRepository = remoteSubscriptionRepository; + this.scriptRepository = scriptRepository; + this.trackedEntityRepository = trackedEntityRepository; } public boolean hasCompletedSetup() @@ -132,13 +147,16 @@ public void apply( @Nonnull Setup setup ) createRemoteSubscription( setup.getRemoteSubscriptionSetup(), organizationSystem, patientSystem ); createSystemCodes( setup.getOrganizationCodeSetup(), organizationSystem ); + updateTrackedEntity( setup.getTrackedEntitySetup() ); } private void createRemoteSubscription( @Nonnull RemoteSubscriptionSetup setup, @Nonnull System organizationSystem, @Nonnull System patientSystem ) { + final Set autoCreatedSubscriptionResources = new HashSet<>(); + autoCreatedSubscriptionResources.add( FhirResourceType.PATIENT ); + final RemoteSubscription remoteSubscription = new RemoteSubscription(); remoteSubscription.setSystems( new ArrayList<>() ); - remoteSubscription.setAutoCreatedSubscriptionResources( Collections.singleton( FhirResourceType.PATIENT ) ); remoteSubscription.setName( "Default Remote Subscription" ); remoteSubscription.setCode( "DEFAULT" ); remoteSubscription.setDescription( "Default remote subscription." ); @@ -146,6 +164,26 @@ private void createRemoteSubscription( @Nonnull RemoteSubscriptionSetup setup, @ remoteSubscription.setEnabled( true ); remoteSubscription.setToleranceMillis( setup.getFhirSetup().getToleranceMillis() ); + if ( setup.getFhirSetup().isSupportsRelatedPerson() ) + { + autoCreatedSubscriptionResources.add( FhirResourceType.RELATED_PERSON ); + } + else + { + if ( remoteSubscription.getResources() == null ) + { + remoteSubscription.setResources( new ArrayList<>() ); + } + + final RemoteSubscriptionResource rsr = new RemoteSubscriptionResource(); + rsr.setRemoteSubscription( remoteSubscription ); + rsr.setFhirResourceType( FhirResourceType.RELATED_PERSON ); + rsr.setVirtual( true ); + rsr.setDescription( "Virtual remote subscription for FHIR Related Person." ); + remoteSubscription.getResources().add( rsr ); + } + remoteSubscription.setAutoCreatedSubscriptionResources( autoCreatedSubscriptionResources ); + final SubscriptionAdapterEndpoint adapterEndpoint = new SubscriptionAdapterEndpoint(); adapterEndpoint.setBaseUrl( StringUtils.trim( setup.getAdapterSetup().getBaseUrl() ) ); adapterEndpoint.setAuthorizationHeader( StringUtils.trim( setup.getAdapterSetup().getAuthorizationHeaderValue() ) ); @@ -217,6 +255,24 @@ private void createSystemCodes( @Nonnull OrganizationCodeSetup setup, @Nonnull S executableScriptArgRepository.save( scriptArg ); } + private void updateTrackedEntity( @Nonnull TrackedEntitySetup trackedEntitySetup ) + { + final MappedTrackedEntity trackedEntity = findTrackedEntity( "Person" ); + trackedEntity.setTrackedEntityReference( trackedEntitySetup.getType().getReference() ); + trackedEntity.setTrackedEntityIdentifierReference( trackedEntitySetup.getPatientId().getReference() ); + + findScriptArg( "TRANSFORM_FHIR_PATIENT_DHIS_PERSON", "uniqueIdAttribute" ).setDefaultValue( trackedEntitySetup.getUniqueId().getOptionalRefVal() ); + findScriptArg( "TRANSFORM_FHIR_PATIENT_DHIS_PERSON", "firstNameAttribute" ).setDefaultValue( trackedEntitySetup.getFirstName().getMandatoryRefVal() ); + findScriptArg( "TRANSFORM_FHIR_PATIENT_DHIS_PERSON", "lastNameAttribute" ).setDefaultValue( trackedEntitySetup.getLastName().getMandatoryRefVal() ); + findScriptArg( "TRANSFORM_FHIR_PATIENT_DHIS_PERSON", "birthDateAttribute" ).setDefaultValue( trackedEntitySetup.getBirthDate().getOptionalRefVal() ); + findScriptArg( "TRANSFORM_FHIR_PATIENT_DHIS_PERSON", "genderAttribute" ).setDefaultValue( trackedEntitySetup.getGender().getOptionalRefVal() ); + findScriptArg( "TRANSFORM_FHIR_PATIENT_DHIS_PERSON", "addressTextAttribute" ).setDefaultValue( trackedEntitySetup.getVillageName().getOptionalRefVal() ); + + findScriptArg( "TRANSFORM_FHIR_RELATED_PERSON_DHIS_PERSON", "personFirstNameAttribute" ).setDefaultValue( trackedEntitySetup.getCaregiverFirstName().getOptionalRefVal() ); + findScriptArg( "TRANSFORM_FHIR_RELATED_PERSON_DHIS_PERSON", "personLastNameAttribute" ).setDefaultValue( trackedEntitySetup.getCaregiverLastName().getOptionalRefVal() ); + findScriptArg( "TRANSFORM_FHIR_RELATED_PERSON_DHIS_PERSON", "personPhoneAttribute" ).setDefaultValue( trackedEntitySetup.getCaregiverPhone().getOptionalRefVal() ); + } + @Nonnull private Map createCodes( @Nonnull List codeMappings, @Nonnull CodeCategory organizationCodeCategory ) { @@ -258,11 +314,28 @@ protected CodeCategory findCodeCategory( @Nonnull String code ) { CodeCategory codeCategory = new CodeCategory(); codeCategory.setCode( code ); - codeCategory = codeCategoryRepository.findAll( Example.of( codeCategory ) ).stream().findFirst().orElse( null ); - if ( codeCategory == null ) - { - throw new SetupException( "Code category with code " + code + " does not exist." ); - } + codeCategory = codeCategoryRepository.findAll( Example.of( codeCategory ) ).stream().findFirst() + .orElseThrow( () -> new SetupException( "Code category with code " + code + " does not exist." ) ); return codeCategory; } + + @Nonnull + protected MappedTrackedEntity findTrackedEntity( @Nonnull String name ) + { + MappedTrackedEntity trackedEntity = new MappedTrackedEntity(); + trackedEntity.setName( name ); + trackedEntity = trackedEntityRepository.findAll( Example.of( trackedEntity ) ) + .stream().findFirst().orElseThrow( () -> new SetupException( "Tracked entity with name " + name + " does not exist." ) ); + return trackedEntity; + } + + @Nonnull + protected ScriptArg findScriptArg( @Nonnull String scriptCode, @Nonnull String attrName ) + { + Script script = new Script(); + script.setCode( scriptCode ); + script = scriptRepository.findAll( Example.of( script ) ).stream().findFirst().orElseThrow( () -> new SetupException( "Script with code " + scriptCode + " does not exist." ) ); + return script.getArguments().stream().filter( a -> attrName.equals( a.getName() ) ).findFirst() + .orElseThrow( () -> new SetupException( "Script " + scriptCode + " does not include script argument " + attrName + "." ) ); + } } diff --git a/app/src/main/java/org/dhis2/fhir/adapter/setup/TrackedEntitySetup.java b/app/src/main/java/org/dhis2/fhir/adapter/setup/TrackedEntitySetup.java new file mode 100644 index 00000000..73ef70e5 --- /dev/null +++ b/app/src/main/java/org/dhis2/fhir/adapter/setup/TrackedEntitySetup.java @@ -0,0 +1,199 @@ +package org.dhis2.fhir.adapter.setup; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * Contains the setup for the tracked entity. + * + * @author volsch + */ +public class TrackedEntitySetup implements Serializable +{ + private static final long serialVersionUID = 6119463810974449663L; + + @Valid + @NotNull + private ReferenceSetup type = new ReferenceSetup( ReferenceType.NAME, "Person" ); + + @Valid + @NotNull + private ReferenceSetup uniqueId = new ReferenceSetup( ReferenceType.ID, "KSr2yTdu1AI" ); + + @Valid + @NotNull + private ReferenceSetup patientId = new ReferenceSetup( ReferenceType.ID, "Ewi7FUfcHAD" ); + + @Valid + @NotNull + private ReferenceSetup firstName = new ReferenceSetup( ReferenceType.ID, "TfdH5KvFmMy" ); + + @Valid + @NotNull + private ReferenceSetup lastName = new ReferenceSetup( ReferenceType.ID, "aW66s2QSosT" ); + + @Valid + @NotNull + private ReferenceSetup birthDate = new ReferenceSetup( ReferenceType.ID, "BiTsLcJQ95V" ); + + @Valid + @NotNull + private ReferenceSetup gender = new ReferenceSetup( ReferenceType.ID, "CklPZdOd6H1" ); + + @Valid + @NotNull + private ReferenceSetup villageName = new ReferenceSetup( ReferenceType.ID, "Y0i71Y6CVdy" ); + + @Valid + @NotNull + private ReferenceSetup caregiverFirstName = new ReferenceSetup( ReferenceType.ID, "ftFBu8mHZ4H" ); + + @Valid + @NotNull + private ReferenceSetup caregiverLastName = new ReferenceSetup( ReferenceType.ID, "EpbquVl5OD6" ); + + @Valid + @NotNull + private ReferenceSetup caregiverPhone = new ReferenceSetup( ReferenceType.ID, "pjexi5YaAPa" ); + + public ReferenceSetup getType() + { + return type; + } + + public void setType( ReferenceSetup type ) + { + this.type = type; + } + + public ReferenceSetup getUniqueId() + { + return uniqueId; + } + + public void setUniqueId( ReferenceSetup uniqueId ) + { + this.uniqueId = uniqueId; + } + + public ReferenceSetup getPatientId() + { + return patientId; + } + + public void setPatientId( ReferenceSetup patientId ) + { + this.patientId = patientId; + } + + public ReferenceSetup getFirstName() + { + return firstName; + } + + public void setFirstName( ReferenceSetup firstName ) + { + this.firstName = firstName; + } + + public ReferenceSetup getLastName() + { + return lastName; + } + + public void setLastName( ReferenceSetup lastName ) + { + this.lastName = lastName; + } + + public ReferenceSetup getBirthDate() + { + return birthDate; + } + + public void setBirthDate( ReferenceSetup birthDate ) + { + this.birthDate = birthDate; + } + + public ReferenceSetup getGender() + { + return gender; + } + + public void setGender( ReferenceSetup gender ) + { + this.gender = gender; + } + + public ReferenceSetup getVillageName() + { + return villageName; + } + + public void setVillageName( ReferenceSetup villageName ) + { + this.villageName = villageName; + } + + public ReferenceSetup getCaregiverFirstName() + { + return caregiverFirstName; + } + + public void setCaregiverFirstName( ReferenceSetup caregiverFirstName ) + { + this.caregiverFirstName = caregiverFirstName; + } + + public ReferenceSetup getCaregiverLastName() + { + return caregiverLastName; + } + + public void setCaregiverLastName( ReferenceSetup caregiverLastName ) + { + this.caregiverLastName = caregiverLastName; + } + + public ReferenceSetup getCaregiverPhone() + { + return caregiverPhone; + } + + public void setCaregiverPhone( ReferenceSetup caregiverPhone ) + { + this.caregiverPhone = caregiverPhone; + } +} diff --git a/app/src/main/resources/default-application.yml b/app/src/main/resources/default-application.yml index 9d771c4c..e45c3990 100644 --- a/app/src/main/resources/default-application.yml +++ b/app/src/main/resources/default-application.yml @@ -94,10 +94,13 @@ dhis2.fhir-adapter: security: authorities: administration: + - ALL - F_SYSTEM_SETTING code-mapping: + - ALL - F_SYSTEM_SETTING data-mapping: + - ALL - F_SYSTEM_SETTING # Defines the settings for the different cached that are used by the application. cache: diff --git a/app/src/main/resources/templates/setup.html b/app/src/main/resources/templates/setup.html index ee6ee763..d12456f8 100644 --- a/app/src/main/resources/templates/setup.html +++ b/app/src/main/resources/templates/setup.html @@ -71,7 +71,8 @@

DHIS2 FHIR Adapter - Initial Setup

FHIR Subscriptions

This section contains the data that is used to setup the FHIR subscriptions.

DHIS2 Setup

-

This sub-section contains the data of DHIS2 that is required when storing mapped and transformed FHIR data on DHIS2.

+

This sub-section contains the data of DHIS2 that is required when storing mapped and transformed FHIR data on DHIS2. Even if the entered user has ALL authority on DHIS2, the user must have least one "Data capture and maintenance organisation units" + selected.

The username and password of the user that is used to connect to DHIS2 and that creates and updates data on DHIS2. This user must have privileges to create and update tracked entity instances, create and update Tracker program instances and create and update tracker program stage instances.

@@ -133,6 +134,10 @@

FHIR Setup

ms

Tolerance Millis Errors

+
+

If your FHIR service has support for related persons of patients and also supports subscriptions for related persons (not only support for contained related persons), then you should mark the following checkbox.

+

+

System URI Setup

To identify FHIR organization and FHIR patient resources they must have a unique business identifier (FHIR identifier property). These unique business identifiers are defined for a specific system URI. If several FHIR servers are connected to the @@ -175,6 +180,154 @@

FHIR Organization to DHIS2 Organization Unit Mapping

Org Code Default

+

Tracked Entity

+

This section contains the setup for the transformation from FHIR Patient to a tracked entity type that has been configured on your DHIS2 installation. At least the patient ID (contains national ID that has been extracted from FHIR identifier), first + name and last name attributes are required. The checkbox in each line can be used to disable the transformation of that value.The reference to DHIS2 can be made by ID, unique name or unique code (if any). The default configuration below uses the tracked + entity attributes that have been configured in the metadata package (does not contain share information) that can be obtained from this link.

+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
+

+ + + +

+

Error

+

Error

+
+
diff --git a/common/src/main/java/org/dhis2/fhir/adapter/model/Gender.java b/common/src/main/java/org/dhis2/fhir/adapter/model/Gender.java new file mode 100644 index 00000000..2ebfe96f --- /dev/null +++ b/common/src/main/java/org/dhis2/fhir/adapter/model/Gender.java @@ -0,0 +1,39 @@ +package org.dhis2.fhir.adapter.model; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * The gender of a person. + * + * @author volsch + */ +public enum Gender +{ + MALE, FEMALE +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/Reference.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/Reference.java index 09ebd0cb..b614516e 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/Reference.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/model/Reference.java @@ -52,6 +52,8 @@ public class Reference implements Serializable { private static final long serialVersionUID = 6049184293580457755L; + public static final int MAX_LENGTH = 230; + public static final int MAX_VALUE_LENGTH = 200; @NotBlank diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityAttribute.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityAttribute.java index 8aee86d0..6ccfe712 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityAttribute.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/ImmutableTrackedEntityAttribute.java @@ -31,6 +31,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.dhis2.fhir.adapter.dhis.model.ImmutableOptionSet; +import org.dhis2.fhir.adapter.dhis.model.OptionSet; import org.dhis2.fhir.adapter.model.ValueType; import javax.annotation.Nonnull; @@ -83,4 +85,17 @@ public boolean isGenerated() { return delegate.isGenerated(); } + + @Override + public boolean isOptionSetValue() + { + return delegate.isOptionSetValue(); + } + + @JsonIgnore + @Override + public OptionSet getOptionSet() + { + return (delegate.getOptionSet() == null) ? null : new ImmutableOptionSet( delegate.getOptionSet() ); + } } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityAttribute.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityAttribute.java index 5a9fa415..a7642232 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityAttribute.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityAttribute.java @@ -29,6 +29,7 @@ */ import org.dhis2.fhir.adapter.Scriptable; +import org.dhis2.fhir.adapter.dhis.model.OptionSet; import org.dhis2.fhir.adapter.model.ValueType; @Scriptable @@ -43,4 +44,8 @@ public interface TrackedEntityAttribute ValueType getValueType(); boolean isGenerated(); + + boolean isOptionSetValue(); + + OptionSet getOptionSet(); } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java index 8e5b6a83..1d4cdb45 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/TrackedEntityInstance.java @@ -52,6 +52,9 @@ public class TrackedEntityInstance implements DhisResource, Serializable @JsonInclude( JsonInclude.Include.NON_NULL ) private String id; + @JsonIgnore + private String identifier; + @JsonProperty( "trackedEntityType" ) private String typeId; @@ -118,6 +121,16 @@ public void setId( String id ) this.id = id; } + public String getIdentifier() + { + return identifier; + } + + public void setIdentifier( String identifier ) + { + this.identifier = identifier; + } + public String getTypeId() { return typeId; @@ -163,6 +176,12 @@ public boolean containsAttribute( @Nonnull String attributeId ) return getAttributes().stream().anyMatch( a -> Objects.equals( attributeId, a.getAttributeId() ) ); } + public boolean containsAttributeWithValue( @Nonnull String attributeId ) + { + return getAttributes().stream().filter( a -> (a.getValue() != null) ) + .anyMatch( a -> Objects.equals( attributeId, a.getAttributeId() ) ); + } + public boolean isModified() { return modified; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/WritableTrackedEntityAttribute.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/WritableTrackedEntityAttribute.java index edeaefa4..dd7b9ecc 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/WritableTrackedEntityAttribute.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/WritableTrackedEntityAttribute.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.dhis2.fhir.adapter.dhis.model.WritableOptionSet; import org.dhis2.fhir.adapter.model.ValueType; import java.io.Serializable; @@ -46,18 +47,23 @@ public class WritableTrackedEntityAttribute implements TrackedEntityAttribute, S private boolean generated; + private boolean optionSetValue; + + private WritableOptionSet optionSet; + public WritableTrackedEntityAttribute() { super(); } - public WritableTrackedEntityAttribute( String id, String name, String code, ValueType valueType, boolean generated ) + public WritableTrackedEntityAttribute( String id, String name, String code, ValueType valueType, boolean generated, WritableOptionSet optionSet ) { this.id = id; this.name = name; this.code = code; this.valueType = valueType; this.generated = generated; + this.optionSet = optionSet; } @Override @@ -114,4 +120,26 @@ public void setGenerated( boolean generated ) { this.generated = generated; } + + @Override + public boolean isOptionSetValue() + { + return optionSetValue; + } + + public void setOptionSetValue( boolean optionSetValue ) + { + this.optionSetValue = optionSetValue; + } + + @Override + public WritableOptionSet getOptionSet() + { + return optionSet; + } + + public void setOptionSet( WritableOptionSet optionSet ) + { + this.optionSet = optionSet; + } } diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityMetadataServiceImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityMetadataServiceImpl.java index 0f2da9fe..3bbd3f1f 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityMetadataServiceImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/tracker/trackedentity/impl/TrackedEntityMetadataServiceImpl.java @@ -51,9 +51,10 @@ @Service public class TrackedEntityMetadataServiceImpl implements TrackedEntityMetadataService { - protected static final String TRACKED_ENTITY_TYPE_FIELDS = "id,name,trackedEntityTypeAttributes[id,name,valueType,mandatory,trackedEntityAttribute[id,name,code,valueType,generated]]"; + protected static final String TRACKED_ENTITY_ATTRIBUTE_FIELDS = "id,name,code,valueType,optionSetValue,optionSet[id,name,options[code,name]]"; - protected static final String TRACKED_ENTITY_ATTRIBUTE_FIELDS = "id,name,code,valueType,generated"; + protected static final String TRACKED_ENTITY_TYPE_FIELDS = "id,name,trackedEntityTypeAttributes[id,name,valueType,mandatory,trackedEntityAttribute[" + + TRACKED_ENTITY_ATTRIBUTE_FIELDS + "]]"; protected static final String TRACKED_ENTITY_TYPE_BY_ID_URI = "/trackedEntityTypes/{id}.json?fields=" + TRACKED_ENTITY_TYPE_FIELDS; diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3AddressFhirToDhisTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3AddressFhirToDhisTransformerUtils.java index c8cdb696..3fa99103 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3AddressFhirToDhisTransformerUtils.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3AddressFhirToDhisTransformerUtils.java @@ -28,6 +28,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import org.apache.commons.lang3.StringUtils; import org.dhis2.fhir.adapter.Scriptable; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; @@ -85,6 +86,50 @@ public String getSingleLine( @Nullable ICompositeType address, @Nonnull String d return String.join( delimiter, convertedAddress.getLine().stream().map( PrimitiveType::getValue ).collect( Collectors.toList() ) ); } + @Nullable + @Override + public String getConstructedText( @Nullable ICompositeType address, @Nonnull String delimiter ) + { + final Address convertedAddress = (Address) address; + if ( address == null ) + { + return null; + } + + final StringBuilder sb = new StringBuilder(); + convertedAddress.getLine().stream().filter( l -> (l != null) && StringUtils.isNotBlank( l.getValue() ) ) + .forEach( l -> sb.append( delimiter ).append( l.getValue() ) ); + if ( StringUtils.isNotBlank( convertedAddress.getPostalCode() ) && StringUtils.isNotBlank( convertedAddress.getCity() ) ) + { + sb.append( delimiter ).append( convertedAddress.getPostalCode() ).append( ' ' ).append( convertedAddress.getCity() ); + } + else if ( StringUtils.isNotBlank( convertedAddress.getPostalCode() ) ) + { + sb.append( delimiter ).append( convertedAddress.getPostalCode() ); + } + else if ( StringUtils.isNotBlank( convertedAddress.getCity() ) ) + { + sb.append( delimiter ).append( convertedAddress.getCity() ); + } + if ( StringUtils.isNotBlank( convertedAddress.getState() ) ) + { + sb.append( delimiter ).append( convertedAddress.getState() ); + } + if ( sb.length() == 0 ) + { + return convertedAddress.getText(); + } + return sb.substring( delimiter.length() ); + } + + @Nullable + @Override + public String getText( @Nullable ICompositeType address ) + { + final Address convertedAddress = (Address) address; + return (convertedAddress == null) ? null : convertedAddress.getText(); + } + @Nonnull protected Optional
getOptionalPrimaryAddress( @Nonnull List addresses ) { diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3CodeFhirToDhisTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3CodeFhirToDhisTransformerUtils.java index f0b6c838..5840ece9 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3CodeFhirToDhisTransformerUtils.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3CodeFhirToDhisTransformerUtils.java @@ -64,14 +64,12 @@ public class Dstu3CodeFhirToDhisTransformerUtils extends AbstractCodeFhirToDhisT { private final Logger logger = LoggerFactory.getLogger( getClass() ); - private final SystemCodeRepository systemCodeRepository; public Dstu3CodeFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull CodeRepository codeRepository, @Nonnull SystemCodeRepository systemCodeRepository ) { - super( scriptExecutionContext, codeRepository ); - this.systemCodeRepository = systemCodeRepository; + super( scriptExecutionContext, codeRepository, systemCodeRepository ); } @Nonnull @@ -121,25 +119,25 @@ public String getCode( @Nullable ICompositeType codeableConcept, @Nullable Strin } @Override - public boolean containsMappedCode( @Nullable ICompositeType codeableConcept, @Nullable Object mappedCodes ) + public boolean containsMappingCode( @Nullable ICompositeType codeableConcept, @Nullable Object mappingCodes ) { - if ( mappedCodes == null ) + if ( mappingCodes == null ) { - throw new IllegalArgumentException( "Mapped codes must be specified." ); + throw new IllegalArgumentException( "Codes must be specified." ); } - if ( codeableConcept == null ) + if ( (codeableConcept == null) || codeableConcept.isEmpty() ) { return false; } - final List convertedMappedCodes = ScriptArgUtils.extractStringArray( mappedCodes ); - if ( CollectionUtils.isEmpty( convertedMappedCodes ) ) + final List convertedCodes = ScriptArgUtils.extractStringArray( mappingCodes ); + if ( CollectionUtils.isEmpty( convertedCodes ) ) { return false; } final Set checkedCodes = new HashSet<>(); - final Collection systemCodes = systemCodeRepository.findAllByCodes( convertedMappedCodes ); + final Collection systemCodes = getSystemCodeRepository().findAllByCodes( convertedCodes ); for ( final SystemCode systemCode : systemCodes ) { if ( containsCode( codeableConcept, systemCode.getSystem().getSystemUri(), systemCode.getSystemCode() ) ) @@ -148,9 +146,9 @@ public boolean containsMappedCode( @Nullable ICompositeType codeableConcept, @Nu } checkedCodes.add( systemCode.getCode().getCode() ); } - if ( logger.isDebugEnabled() && checkedCodes.size() < convertedMappedCodes.size() ) + if ( logger.isDebugEnabled() && checkedCodes.size() < convertedCodes.size() ) { - logger.info( "Mapped codes have not been defined: " + Sets.difference( new HashSet<>( convertedMappedCodes ), checkedCodes ) ); + logger.info( "Codes have not been defined: " + Sets.difference( new HashSet<>( convertedCodes ), checkedCodes ) ); } return false; } diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3ContactPointFhirToDhisTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3ContactPointFhirToDhisTransformerUtils.java new file mode 100644 index 00000000..03df9cec --- /dev/null +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3ContactPointFhirToDhisTransformerUtils.java @@ -0,0 +1,107 @@ +package org.dhis2.fhir.adapter.fhir.transform.impl.util.dstu3; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.Scriptable; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractContactPointFhirToDhisTransformerUtils; +import org.hl7.fhir.dstu3.model.ContactPoint; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +@Component +@Scriptable +public class Dstu3ContactPointFhirToDhisTransformerUtils extends AbstractContactPointFhirToDhisTransformerUtils +{ + private final Dstu3DateTimeFhirToDhisTransformerUtils dateTimeTransformerUtils; + + public Dstu3ContactPointFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull Dstu3DateTimeFhirToDhisTransformerUtils dateTimeTransformerUtils ) + { + super( scriptExecutionContext ); + this.dateTimeTransformerUtils = dateTimeTransformerUtils; + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.DSTU3_ONLY; + } + + @Nullable + @Override + protected String getContactPointValue( @Nullable List contactPoints, @Nullable String code ) + { + if ( (contactPoints == null) || (code == null) ) + { + return null; + } + + final ContactPoint found = contactPoints.stream().map( cp -> (ContactPoint) cp ) + .filter( cp -> cp.hasSystem() && code.equalsIgnoreCase( cp.getSystem().toCode() ) + && dateTimeTransformerUtils.isValidNow( cp.getPeriod() ) && cp.hasValue() ).min( new ContactPointComparator() ).orElse( null ); + return (found == null) ? null : found.getValue(); + } + + protected static class ContactPointComparator implements Comparator + { + @Override + public int compare( ContactPoint o1, ContactPoint o2 ) + { + return getOrderString( o1 ).compareTo( getOrderString( o2 ) ); + } + + @Nonnull + private String getOrderString( @Nonnull ContactPoint cp ) + { + final String use; + if ( cp.getUse() == ContactPoint.ContactPointUse.OLD ) + { + use = "8"; + } + else if ( cp.getUse() == ContactPoint.ContactPointUse.TEMP ) + { + use = "5"; + } + else + { + use = "0"; + } + final int rank = cp.hasRank() ? cp.getRank() : Integer.MAX_VALUE; + return use + String.format( "0x%08x", rank ); + } + } +} diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3DateTimeFhirToDhisTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3DateTimeFhirToDhisTransformerUtils.java index c2a3a7b8..2b157b63 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3DateTimeFhirToDhisTransformerUtils.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3DateTimeFhirToDhisTransformerUtils.java @@ -35,6 +35,8 @@ import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractDateTimeFhirToDhisTransformerUtils; import org.dhis2.fhir.adapter.util.CastUtils; import org.hl7.fhir.dstu3.model.BaseDateTimeType; +import org.hl7.fhir.dstu3.model.Period; +import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.stereotype.Component; @@ -75,6 +77,24 @@ public boolean hasDayPrecision( @Nullable IPrimitiveType dateTime ) return (((BaseDateTimeType) dateTime).getPrecision().ordinal() >= TemporalPrecisionEnum.DAY.ordinal()); } + @Override + public boolean isValidNow( @Nullable ICompositeType period ) + { + if ( period == null ) + { + return true; + } + + final Period p = (Period) period; + final Date now = new Date(); + // start will be ignored since there may be no further notification about that event + if ( (p.getEnd() != null) && now.after( p.getEnd() ) ) + { + return false; + } + return true; + } + @Override @Nullable protected LocalDate castDate( @Nonnull Object date ) diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3FhirClientTransformUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3FhirClientFhirToDhisTransformerUtils.java similarity index 92% rename from fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3FhirClientTransformUtils.java rename to fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3FhirClientFhirToDhisTransformerUtils.java index 3a27a82e..7db43f82 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3FhirClientTransformUtils.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3FhirClientFhirToDhisTransformerUtils.java @@ -34,7 +34,7 @@ import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemCodeRepository; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; -import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractFhirClientTransformUtils; +import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractFhirClientFhirToDhisTransformerUtils; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent; @@ -52,9 +52,9 @@ @Component @Scriptable -public class Dstu3FhirClientTransformUtils extends AbstractFhirClientTransformUtils +public class Dstu3FhirClientFhirToDhisTransformerUtils extends AbstractFhirClientFhirToDhisTransformerUtils { - public Dstu3FhirClientTransformUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull @Qualifier( "fhirContextDstu3" ) FhirContext fhirContext, + public Dstu3FhirClientFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull @Qualifier( "fhirContextDstu3" ) FhirContext fhirContext, @Nonnull RemoteSubscriptionResourceRepository subscriptionResourceRepository, @Nonnull SystemCodeRepository systemCodeRepository ) { super( scriptExecutionContext, fhirContext, subscriptionResourceRepository, systemCodeRepository ); diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3GeoFhirToDhisTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3GeoFhirToDhisTransformerUtils.java index 978c1662..e55bb377 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3GeoFhirToDhisTransformerUtils.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3GeoFhirToDhisTransformerUtils.java @@ -67,7 +67,7 @@ public Set getFhirVersions() public Location getLocation( @Nonnull IElement element ) throws TransformerException { final Element e = (Element) element; - final List locationExtensions = e.getExtensionsByUrl( GEO_LOCATION_URL ); + final List locationExtensions = e.getExtensionsByUrl( GEO_LOCATION_URI ); if ( locationExtensions.isEmpty() ) { return null; diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3IdentifierFhirToDhisTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3IdentifierFhirToDhisTransformerUtils.java index ab85e067..1eb067de 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3IdentifierFhirToDhisTransformerUtils.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3IdentifierFhirToDhisTransformerUtils.java @@ -32,7 +32,7 @@ import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractIdentifierFhirToDhisTransformerUtils; -import org.dhis2.fhir.adapter.fhir.transform.impl.util.ReferenceTransformUtils; +import org.dhis2.fhir.adapter.fhir.transform.impl.util.ReferenceFhirToDhisTransformerUtils; import org.hl7.fhir.dstu3.model.Identifier; import org.hl7.fhir.instance.model.api.IDomainResource; import org.springframework.stereotype.Component; @@ -49,9 +49,9 @@ @Scriptable public class Dstu3IdentifierFhirToDhisTransformerUtils extends AbstractIdentifierFhirToDhisTransformerUtils { - public Dstu3IdentifierFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ReferenceTransformUtils referenceTransformUtils ) + public Dstu3IdentifierFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ReferenceFhirToDhisTransformerUtils referenceFhirToDhisTransformerUtils ) { - super( scriptExecutionContext, referenceTransformUtils ); + super( scriptExecutionContext, referenceFhirToDhisTransformerUtils ); } @Nonnull diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3ObservationFhirToDhisTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3ObservationFhirToDhisTransformerUtils.java index d8b4d23b..6e2e7f1b 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3ObservationFhirToDhisTransformerUtils.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3ObservationFhirToDhisTransformerUtils.java @@ -31,7 +31,7 @@ import org.dhis2.fhir.adapter.Scriptable; import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; -import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractObservationTransformUtils; +import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractObservationFhirToDhisTransformerUtils; import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation.ObservationComponentComponent; import org.hl7.fhir.dstu3.model.ResourceType; @@ -47,9 +47,9 @@ @Component @Scriptable -public class Dstu3ObservationFhirToDhisTransformerUtils extends AbstractObservationTransformUtils +public class Dstu3ObservationFhirToDhisTransformerUtils extends AbstractObservationFhirToDhisTransformerUtils { - public Dstu3ObservationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull Dstu3FhirClientTransformUtils clientTransformUtils, + public Dstu3ObservationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull Dstu3FhirClientFhirToDhisTransformerUtils clientTransformUtils, @Nonnull Dstu3CodeFhirToDhisTransformerUtils codeTransformerUtils ) { super( scriptExecutionContext, clientTransformUtils, codeTransformerUtils ); diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationFhirToDhisTransformerUtils.java similarity index 93% rename from fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationTransformerUtils.java rename to fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationFhirToDhisTransformerUtils.java index 54278e85..40ccec49 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationTransformerUtils.java +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3OrganizationFhirToDhisTransformerUtils.java @@ -35,7 +35,7 @@ import org.dhis2.fhir.adapter.fhir.repository.RemoteFhirRepository; import org.dhis2.fhir.adapter.fhir.repository.RemoteHierarchicallyFhirRepository; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; -import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractOrganizationTransformerUtils; +import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractOrganizationFhirToDhisTransformerUtils; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Organization; import org.hl7.fhir.instance.model.api.IBaseBundle; @@ -52,9 +52,9 @@ @Component @Scriptable -public class Dstu3OrganizationTransformerUtils extends AbstractOrganizationTransformerUtils +public class Dstu3OrganizationFhirToDhisTransformerUtils extends AbstractOrganizationFhirToDhisTransformerUtils { - public Dstu3OrganizationTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, + public Dstu3OrganizationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull OrganisationUnitService organisationUnitService, @Nonnull RemoteSubscriptionResourceRepository subscriptionResourceRepository, @Nonnull RemoteFhirRepository remoteFhirRepository, diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3PatientFhirToDhisTransformerUtils.java b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3PatientFhirToDhisTransformerUtils.java new file mode 100644 index 00000000..814ebcc6 --- /dev/null +++ b/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/dstu3/Dstu3PatientFhirToDhisTransformerUtils.java @@ -0,0 +1,97 @@ +package org.dhis2.fhir.adapter.fhir.transform.impl.util.dstu3; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.Scriptable; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; +import org.dhis2.fhir.adapter.fhir.transform.impl.util.AbstractPatientFhirToDhisTransformerUtils; +import org.hl7.fhir.dstu3.model.Address; +import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.dstu3.model.Type; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.Set; + +@Component +@Scriptable +public class Dstu3PatientFhirToDhisTransformerUtils extends AbstractPatientFhirToDhisTransformerUtils +{ + public Dstu3PatientFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( scriptExecutionContext ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.DSTU3_ONLY; + } + + @Nullable + @Override + public ICompositeType getBirthPlaceAddress( @Nullable IDomainResource patient ) + { + if ( patient == null ) + { + return null; + } + + final Patient p = (Patient) patient; + final List birthPlaceExtensions = p.getExtensionsByUrl( BIRTH_PLACE_URI ); + if ( birthPlaceExtensions.isEmpty() ) + { + return null; + } + if ( birthPlaceExtensions.size() > 1 ) + { + throw new TransformerMappingException( "Patient contains " + birthPlaceExtensions.size() + " birth places." ); + } + + final Type value = birthPlaceExtensions.get( 0 ).getValue(); + if ( (value == null) || value.isEmpty() ) + { + return null; + } + + if ( !(value instanceof Address) ) + { + throw new TransformerMappingException( "Patient contains birth place that is no address: " + value.fhirType() ); + } + return (Address) value; + } +} diff --git a/fhir/pom.xml b/fhir/pom.xml index 1b565c4e..192157ff 100644 --- a/fhir/pom.xml +++ b/fhir/pom.xml @@ -140,6 +140,7 @@ org.asciidoctor asciidoctor-maven-plugin + 1.5.7.1 generate-docs diff --git a/fhir/src/main/asciidoc/api-guide.adoc b/fhir/src/main/asciidoc/api-guide.adoc index fee8eaa5..33050193 100644 --- a/fhir/src/main/asciidoc/api-guide.adoc +++ b/fhir/src/main/asciidoc/api-guide.adoc @@ -100,6 +100,9 @@ data type. | `DATE_UNIT` | A date unit. Possible values are YEARS, MONTHS, DAYS. +| `GENDER` +| Gender of a person. Possible values are MALE, FEMALE. + | `WEIGHT_UNIT` | A weight unit. Possible values are GRAM, KILO_GRAM, OUNCE, POUND. @@ -136,6 +139,9 @@ data type. | `FHIR_RESOURCE` | A FHIR resource as defined by HAPI FHIR API. There is no string representation for this data type. +| `FHIR_RESOURCE_LIST` +| A list of FHIR resources as defined by HAPI FHIR API. There is no string representation for this data type. + | `EVENT_DECISION_TYPE` | A decision that is made before processing an event (tracker program stage instance). Possible values are CONTINUE (use the existing event), NEW_EVENT (create a new event, just for repeatable program stages), BREAK (the complete rule is not applicable). |=== @@ -381,6 +387,25 @@ include::{snippets}/read-executable-script/response-body.adoc[] include::{snippets}/read-executable-script/response-fields.adoc[] +[[api-resources-data-tracked-entity]] +==== Tracked Entity +Configures the reference to a DHIS 2 tracked entity type. A tracked entity +type must define a unique identifier in order to be able to map FHIR data +to existing tracked entity instances. + +.Create resource curl snippet +include::{snippets}/create-tracked-entity/curl-request.adoc[] + +include::{snippets}/create-tracked-entity/request-fields.adoc[] + +.Read resource curl snippet +include::{snippets}/read-tracked-entity/curl-request.adoc[] + +.Read resource response body +include::{snippets}/read-tracked-entity/response-body.adoc[] + +include::{snippets}/read-tracked-entity/response-fields.adoc[] + [[api-resources-data-tracked-entity-rule]] ==== Tracked Entity Rule The tracked entity rule resource defines a rule that is able to transform diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/IgnoredSubscriptionResourceException.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/IgnoredSubscriptionResourceException.java new file mode 100644 index 00000000..0e58c08d --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/IgnoredSubscriptionResourceException.java @@ -0,0 +1,45 @@ +package org.dhis2.fhir.adapter.fhir.data.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * Thrown if the processed subscription resource can be ignored since it does + * no longer exist. + * + * @author volsch + */ +public class IgnoredSubscriptionResourceException extends RuntimeException +{ + private static final long serialVersionUID = 4787054440737823557L; + + public IgnoredSubscriptionResourceException( String message, Throwable cause ) + { + super( message, cause ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomQueuedRemoteFhirResourceRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomQueuedRemoteFhirResourceRepositoryImpl.java index 51ba8167..2f95535c 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomQueuedRemoteFhirResourceRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomQueuedRemoteFhirResourceRepositoryImpl.java @@ -30,12 +30,17 @@ import org.dhis2.fhir.adapter.fhir.data.model.QueuedRemoteSubscriptionRequest; import org.dhis2.fhir.adapter.fhir.data.repository.CustomQueuedRemoteFhirResourceRepository; +import org.dhis2.fhir.adapter.fhir.data.repository.IgnoredSubscriptionResourceException; +import org.hibernate.exception.ConstraintViolationException; import org.hibernate.query.NativeQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Nonnull; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; +import javax.persistence.PersistenceException; import javax.persistence.Query; import java.time.Instant; import java.util.UUID; @@ -47,8 +52,15 @@ */ public class CustomQueuedRemoteFhirResourceRepositoryImpl implements CustomQueuedRemoteFhirResourceRepository { + private final Logger logger = LoggerFactory.getLogger( getClass() ); + @PersistenceContext - EntityManager entityManager; + private EntityManager entityManager; + + public CustomQueuedRemoteFhirResourceRepositoryImpl( @Nonnull EntityManager entityManager ) + { + this.entityManager = entityManager; + } @Transactional @Override @@ -61,7 +73,21 @@ public boolean enqueue( @Nonnull UUID subscriptionResourceId, @Nonnull String fh .setParameter( "requestId", requestId ).setParameter( "queuedAt", Instant.now() ); // avoid invalidation of complete 2nd level cache query.unwrap( NativeQuery.class ).addSynchronizedEntityClass( QueuedRemoteSubscriptionRequest.class ); - return query.getResultList().stream().anyMatch( requestId::equals ); + + try + { + return query.getResultList().stream().anyMatch( requestId::equals ); + } + catch ( PersistenceException e ) + { + if ( e.getCause() instanceof ConstraintViolationException ) + { + logger.error( "Could not process enqueue request for subscription resource {} and FHIR resource {} due to constraint violation: {}", + subscriptionResourceId, fhirResourceId, e.getCause().getMessage() ); + throw new IgnoredSubscriptionResourceException( "Subscription resource " + subscriptionResourceId + " does no longer exist.", e ); + } + throw e; + } } @Transactional diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomQueuedRemoteSubscriptionRequestRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomQueuedRemoteSubscriptionRequestRepositoryImpl.java index 4395861c..afb33517 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomQueuedRemoteSubscriptionRequestRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/data/repository/impl/CustomQueuedRemoteSubscriptionRequestRepositoryImpl.java @@ -30,12 +30,17 @@ import org.dhis2.fhir.adapter.fhir.data.model.QueuedRemoteSubscriptionRequest; import org.dhis2.fhir.adapter.fhir.data.repository.CustomQueuedRemoteSubscriptionRequestRepository; +import org.dhis2.fhir.adapter.fhir.data.repository.IgnoredSubscriptionResourceException; +import org.hibernate.exception.ConstraintViolationException; import org.hibernate.query.NativeQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Nonnull; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; +import javax.persistence.PersistenceException; import javax.persistence.Query; import java.time.Instant; import java.util.UUID; @@ -47,8 +52,15 @@ */ public class CustomQueuedRemoteSubscriptionRequestRepositoryImpl implements CustomQueuedRemoteSubscriptionRequestRepository { + private final Logger logger = LoggerFactory.getLogger( getClass() ); + @PersistenceContext - EntityManager entityManager; + private EntityManager entityManager; + + public CustomQueuedRemoteSubscriptionRequestRepositoryImpl( @Nonnull EntityManager entityManager ) + { + this.entityManager = entityManager; + } @Transactional @Override @@ -60,7 +72,21 @@ public boolean enqueue( @Nonnull UUID subscriptionResourceId, @Nonnull String re .setParameter( "id", subscriptionResourceId ).setParameter( "requestId", requestId ).setParameter( "queuedAt", Instant.now() ); // avoid invalidation of complete 2nd level cache query.unwrap( NativeQuery.class ).addSynchronizedEntityClass( QueuedRemoteSubscriptionRequest.class ); - return query.getResultList().stream().anyMatch( requestId::equals ); + + try + { + return query.getResultList().stream().anyMatch( requestId::equals ); + } + catch ( PersistenceException e ) + { + if ( e.getCause() instanceof ConstraintViolationException ) + { + logger.error( "Could not process enqueue request for subscription resource {} due to constraint violation: {}", + subscriptionResourceId, e.getCause().getMessage() ); + throw new IgnoredSubscriptionResourceException( "Subscription resource " + subscriptionResourceId + " does no longer exist.", e ); + } + throw e; + } } @Transactional diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java index 86785e09..45be74e5 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/AbstractRule.java @@ -112,6 +112,8 @@ public abstract class AbstractRule extends VersionedBaseMetadata implements Seri @NotNull private ExecutableScript transformInScript; + private boolean containedAllowed; + protected AbstractRule() { super(); @@ -214,7 +216,7 @@ public void setApplicableCodeSet( CodeSet applicableCodeSet ) this.applicableCodeSet = applicableCodeSet; } - @ManyToOne + @ManyToOne( optional = false ) @JoinColumn( name = "transform_in_script_id", referencedColumnName = "id", nullable = false ) public ExecutableScript getTransformInScript() { @@ -226,6 +228,17 @@ public void setTransformInScript( ExecutableScript transformInScript ) this.transformInScript = transformInScript; } + @Column( name = "contained_allowed", nullable = false ) + public boolean isContainedAllowed() + { + return containedAllowed; + } + + public void setContainedAllowed( boolean containedAllowed ) + { + this.containedAllowed = containedAllowed; + } + @Override public int compareTo( @Nonnull AbstractRule o ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ConstantCategory.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ConstantCategory.java index 2ef96cab..949867f5 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ConstantCategory.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/ConstantCategory.java @@ -35,5 +35,5 @@ */ public enum ConstantCategory { - GENDER, OTHER + GENDER, TEXT_RECOGNITION, OTHER } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataType.java index 95944164..eff3a97b 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataType.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataType.java @@ -42,11 +42,13 @@ import org.dhis2.fhir.adapter.geo.Location; import org.dhis2.fhir.adapter.geo.StringToLocationConverter; import org.dhis2.fhir.adapter.model.DateUnit; +import org.dhis2.fhir.adapter.model.Gender; import org.dhis2.fhir.adapter.model.WeightUnit; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.core.convert.converter.Converter; import java.time.ZonedDateTime; +import java.util.List; import java.util.regex.Pattern; /** @@ -64,6 +66,7 @@ public enum DataType DATE_TIME( ZonedDateTime.class, new StringToZonedDateTimeConverter() ), DATE_UNIT( DateUnit.class, new StringToEnumConverter<>( DateUnit.class ) ), WEIGHT_UNIT( WeightUnit.class, new StringToEnumConverter<>( WeightUnit.class ) ), + GENDER( Gender.class, new StringToEnumConverter<>( Gender.class ) ), CONSTANT( String.class, new ObjectConverter<>( String.class ) ), CODE( String.class, new ObjectConverter<>( String.class ) ), LOCATION( Location.class, new StringToLocationConverter() ), @@ -75,6 +78,7 @@ public enum DataType PROGRAM_REF( Reference.class, new StringToReferenceConverter() ), PROGRAM_STAGE_REF( Reference.class, new StringToReferenceConverter() ), FHIR_RESOURCE( IBaseResource.class, new StringToExceptionConverter<>( IBaseResource.class ) ), + FHIR_RESOURCE_LIST( List.class, new StringToExceptionConverter<>( List.class ) ), EVENT_DECISION_TYPE( EventDecisionType.class, new StringToEnumConverter<>( EventDecisionType.class ) ); private final Class javaType; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java index 204c8255..b801638d 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/FhirResourceType.java @@ -57,7 +57,8 @@ public enum FhirResourceType MEDICATION_REQUEST( "MedicationRequest", "MedicationRequest" ), OBSERVATION( "Observation", "Observation" ), ORGANIZATION( "Organization", "Organization" ), - PATIENT( "Patient", "Patient" ); + PATIENT( "Patient", "Patient" ), + RELATED_PERSON( "RelatedPerson", "RelatedPerson" ); private static final Map resourcesBySimpleClassName = Arrays.stream( values() ).flatMap( v -> v.getSimpleClassNames().stream().map( scn -> new SimpleEntry<>( scn, v ) ) ) .collect( Collectors.toMap( SimpleEntry::getKey, SimpleEntry::getValue ) ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackedEntity.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackedEntity.java new file mode 100644 index 00000000..05b14b56 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/MappedTrackedEntity.java @@ -0,0 +1,135 @@ +package org.dhis2.fhir.adapter.fhir.metadata.model; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceAttributeConverter; + +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; + +/** + * The reference to the DHIS2 tracked entity (type). + * + * @author volsch + */ +@Entity +@Table( name = "fhir_tracked_entity" ) +public class MappedTrackedEntity extends VersionedBaseMetadata implements Serializable +{ + private static final long serialVersionUID = -2784006479143123933L; + + public static final int MAX_NAME_LENGTH = 230; + + @NotBlank + @Size( max = MAX_NAME_LENGTH ) + private String name; + + private String description; + + private boolean enabled = true; + + @NotNull + @Valid + private Reference trackedEntityReference; + + @NotNull + @Valid + private Reference trackedEntityIdentifierReference; + + @Basic + @Column( name = "name", nullable = false, length = 230 ) + public String getName() + { + return name; + } + + public void setName( String name ) + { + this.name = name; + } + + @Basic + @Column( name = "description", columnDefinition = "TEXT" ) + public String getDescription() + { + return description; + } + + public void setDescription( String description ) + { + this.description = description; + } + + @Basic + @Column( name = "enabled" ) + public boolean isEnabled() + { + return enabled; + } + + public void setEnabled( boolean enabled ) + { + this.enabled = enabled; + } + + @Basic + @Column( name = "tracked_entity_ref", nullable = false, length = 230 ) + @Convert( converter = ReferenceAttributeConverter.class ) + public Reference getTrackedEntityReference() + { + return trackedEntityReference; + } + + public void setTrackedEntityReference( Reference trackedEntityRef ) + { + this.trackedEntityReference = trackedEntityRef; + } + + @Basic + @Column( name = "tracked_entity_identifier_ref", nullable = false, length = 230 ) + @Convert( converter = ReferenceAttributeConverter.class ) + public Reference getTrackedEntityIdentifierReference() + { + return trackedEntityIdentifierReference; + } + + public void setTrackedEntityIdentifierReference( Reference trackedEntityIdentifierReference ) + { + this.trackedEntityIdentifierReference = trackedEntityIdentifierReference; + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java index 3c91d11a..fa677a36 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscription.java @@ -107,7 +107,7 @@ public class RemoteSubscription extends VersionedBaseMetadata implements Seriali private List systems; - @EnumValue( value = FhirResourceType.class, supported = { "PATIENT" } ) + @EnumValue( value = FhirResourceType.class, supported = { "PATIENT", "RELATED_PERSON" } ) private Set autoCreatedSubscriptionResources; @Basic diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionResource.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionResource.java index 22b6696c..7604298f 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionResource.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/RemoteSubscriptionResource.java @@ -74,6 +74,8 @@ public class RemoteSubscriptionResource extends VersionedBaseMetadata implements private RemoteSubscriptionResourceUpdate resourceUpdate; + private boolean virtual; + private String fhirSubscriptionId; @Basic @@ -138,6 +140,18 @@ public void setResourceUpdate( RemoteSubscriptionResourceUpdate resourceUpdate ) this.resourceUpdate = resourceUpdate; } + @Basic + @Column( name = "virtual", nullable = false ) + public boolean isVirtual() + { + return virtual; + } + + public void setVirtual( boolean virtual ) + { + this.virtual = virtual; + } + @Basic @Column( name = "fhir_subscription_id", length = 100 ) @JsonInclude( JsonInclude.Include.NON_NULL ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TrackedEntityRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TrackedEntityRule.java index 2608aede..a149c1dd 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TrackedEntityRule.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TrackedEntityRule.java @@ -29,18 +29,12 @@ */ import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; -import org.dhis2.fhir.adapter.dhis.model.Reference; -import org.dhis2.fhir.adapter.dhis.model.ReferenceAttributeConverter; -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.Convert; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; -import javax.validation.Valid; import javax.validation.constraints.NotNull; /** @@ -56,39 +50,33 @@ public class TrackedEntityRule extends AbstractRule private static final long serialVersionUID = -3997570895838354307L; @NotNull - @Valid - private Reference trackedEntityReference; + private MappedTrackedEntity trackedEntity; - @NotNull private ExecutableScript orgUnitLookupScript; - @NotNull private ExecutableScript locationLookupScript; - @NotNull - @Valid - private Reference trackedEntityIdentifierReference; + private ExecutableScript teiLookupScript; public TrackedEntityRule() { super( DhisResourceType.TRACKED_ENTITY ); } - @Basic - @Column( name = "tracked_entity_ref", nullable = false, length = 230 ) - @Convert( converter = ReferenceAttributeConverter.class ) - public Reference getTrackedEntityReference() + @ManyToOne( optional = false ) + @JoinColumn( name = "tracked_entity_id", nullable = false ) + public MappedTrackedEntity getTrackedEntity() { - return trackedEntityReference; + return trackedEntity; } - public void setTrackedEntityReference( Reference trackedEntityRef ) + public void setTrackedEntity( MappedTrackedEntity trackedEntity ) { - this.trackedEntityReference = trackedEntityRef; + this.trackedEntity = trackedEntity; } - @ManyToOne( optional = false ) - @JoinColumn( name = "org_lookup_script_id", nullable = false ) + @ManyToOne + @JoinColumn( name = "org_lookup_script_id" ) public ExecutableScript getOrgUnitLookupScript() { return orgUnitLookupScript; @@ -99,8 +87,8 @@ public void setOrgUnitLookupScript( ExecutableScript orgUnitLookupScript ) this.orgUnitLookupScript = orgUnitLookupScript; } - @ManyToOne( optional = false ) - @JoinColumn( name = "loc_lookup_script_id", nullable = false ) + @ManyToOne + @JoinColumn( name = "loc_lookup_script_id" ) public ExecutableScript getLocationLookupScript() { return locationLookupScript; @@ -111,16 +99,15 @@ public void setLocationLookupScript( ExecutableScript locationLookupScript ) this.locationLookupScript = locationLookupScript; } - @Basic - @Column( name = "tracked_entity_identifier_ref", nullable = false, length = 230 ) - @Convert( converter = ReferenceAttributeConverter.class ) - public Reference getTrackedEntityIdentifierReference() + @ManyToOne + @JoinColumn( name = "tei_lookup_script_id" ) + public ExecutableScript getTeiLookupScript() { - return trackedEntityIdentifierReference; + return teiLookupScript; } - public void setTrackedEntityIdentifierReference( Reference trackedEntityIdentifierReference ) + public void setTeiLookupScript( ExecutableScript teiLookupScript ) { - this.trackedEntityIdentifierReference = trackedEntityIdentifierReference; + this.teiLookupScript = teiLookupScript; } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TransformDataType.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TransformDataType.java index ef128a18..f8fb9a12 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TransformDataType.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/TransformDataType.java @@ -43,7 +43,8 @@ public enum TransformDataType FHIR_PATIENT( FhirResourceType.PATIENT ), FHIR_IMMUNIZATION( FhirResourceType.PATIENT ), FHIR_OBSERVATION( FhirResourceType.OBSERVATION ), - FHIR_DIAGNOSTIC_REPORT( FhirResourceType.DIAGNOSTIC_REPORT ); + FHIR_DIAGNOSTIC_REPORT( FhirResourceType.DIAGNOSTIC_REPORT ), + FHIR_RELATED_PERSON( FhirResourceType.RELATED_PERSON ); private final FhirResourceType fhirResourceType; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeCategoryRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeCategoryRepository.java index aefaef3f..88f20a97 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeCategoryRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeCategoryRepository.java @@ -47,7 +47,7 @@ * @author volsch */ @CacheConfig( cacheManager = "metadataCacheManager", cacheNames = "codeCategory" ) -@RepositoryRestResource( path = "codeCategories" ) +@RepositoryRestResource( path = "codeCategories", collectionResourceRel = "codeCategories", itemResourceRel = "codeCategory" ) @PreAuthorize( "hasRole('CODE_MAPPING')" ) public interface CodeCategoryRepository extends JpaRepository, QuerydslPredicateExecutor { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeRepository.java index bb5ea21e..1fb4efcb 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeRepository.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/CodeRepository.java @@ -42,6 +42,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import javax.annotation.Nonnull; +import java.util.Collection; import java.util.List; import java.util.UUID; @@ -57,9 +58,9 @@ public interface CodeRepository extends JpaRepository, QuerydslPredi { @RestResource( exported = false ) @Nonnull - @Cacheable( key = "{#root.methodName, #a0, #a1}" ) - @Query( "SELECT c FROM #{#entityName} c JOIN c.systemCodes sc ON sc.systemCode=:systemCode JOIN sc.system s ON s.systemUri=:systemUri AND s.enabled=true" ) - List findBySystemCode( @Param( "systemUri" ) @Nonnull String systemUri, @Param( "systemCode" ) @Nonnull String systemCode ); + @Cacheable( keyGenerator = "codeFindAllBySystemCodesKeyGenerator" ) + @Query( "SELECT c FROM #{#entityName} c JOIN c.systemCodes sc ON sc.systemCodeValue IN (:systemCodeValues) JOIN sc.system s ON s.enabled=true" ) + List findAllBySystemCodes( @Param( "systemCodeValues" ) @Nonnull Collection systemCodeValues ); @Override @Nonnull diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/MappedTrackedEntityRepository.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/MappedTrackedEntityRepository.java new file mode 100644 index 00000000..f422baf9 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/MappedTrackedEntityRepository.java @@ -0,0 +1,94 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.fhir.metadata.model.MappedTrackedEntity; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.security.access.prepost.PreAuthorize; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.UUID; + +/** + * Repository for {@link MappedTrackedEntity} entities. + * + * @author volsch + */ +@CacheConfig( cacheManager = "metadataCacheManager", cacheNames = "mappedTrackedEntity" ) +@RepositoryRestResource( path = "trackedEntities", collectionResourceRel = "trackedEntities", itemResourceRel = "trackedEntity" ) +@PreAuthorize( "hasRole('DATA_MAPPING')" ) +public interface MappedTrackedEntityRepository extends JpaRepository, QuerydslPredicateExecutor +{ + @Override + @Nonnull + @CacheEvict( allEntries = true ) + List saveAll( @Nonnull Iterable entities ); + + @Override + @Nonnull + @CachePut( key = "#a0.id" ) + @CacheEvict( allEntries = true ) + S saveAndFlush( @Nonnull S entity ); + + @Override + @Nonnull + @CachePut( key = "#a0.id" ) + @CacheEvict( allEntries = true ) + S save( @Nonnull S entity ); + + @Override + @CacheEvict( allEntries = true ) + void deleteInBatch( @Nonnull Iterable entities ); + + @Override + @CacheEvict( allEntries = true ) + void deleteAllInBatch(); + + @Override + @CacheEvict( key = "#a0" ) + void deleteById( @Nonnull UUID id ); + + @Override + @CacheEvict( key = "#a0.id" ) + void delete( @Nonnull MappedTrackedEntity entity ); + + @Override + @CacheEvict( allEntries = true ) + void deleteAll( @Nonnull Iterable entities ); + + @Override + @CacheEvict( allEntries = true ) + void deleteAll(); +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/cache/CodeFindAllBySystemCodesKeyGenerator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/cache/CodeFindAllBySystemCodesKeyGenerator.java new file mode 100644 index 00000000..49d0cd92 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/cache/CodeFindAllBySystemCodesKeyGenerator.java @@ -0,0 +1,62 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository.cache; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.TreeSet; + +/** + * Key generator for {@link org.dhis2.fhir.adapter.fhir.metadata.repository.CodeRepository#findAllBySystemCodes(Collection)}. + * Since cache may be serialized to external storage, cache is automatically a string representation. + * + * @author volsch + */ +@Component +public class CodeFindAllBySystemCodesKeyGenerator implements KeyGenerator +{ + @Override + @Nonnull + public Object generate( @Nonnull Object target, @Nonnull Method method, @Nonnull Object... params ) + { + @SuppressWarnings( "unchecked" ) final Collection systemCodeValues = (Collection) params[0]; + final StringBuilder sb = new StringBuilder( "findAllBySystemCodes," ); + sb.append( systemCodeValues.size() ); + // codes must have same order every time + for ( final String code : new TreeSet<>( systemCodeValues ) ) + { + sb.append( ',' ).append( code ); + } + return sb.toString(); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveMappedTrackedEntityValidator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveMappedTrackedEntityValidator.java new file mode 100644 index 00000000..89b11e8e --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveMappedTrackedEntityValidator.java @@ -0,0 +1,83 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository.validator; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.lang3.StringUtils; +import org.dhis2.fhir.adapter.fhir.metadata.model.MappedTrackedEntity; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +import javax.annotation.Nonnull; + +/** + * Spring Data REST validator for {@link MappedTrackedEntity}. + * + * @author volsch + */ +@Component +public class BeforeCreateSaveMappedTrackedEntityValidator implements Validator +{ + @Override + public boolean supports( @Nonnull Class clazz ) + { + return MappedTrackedEntity.class.isAssignableFrom( clazz ); + } + + @Override + public void validate( Object target, @Nonnull Errors errors ) + { + final MappedTrackedEntity mapped = (MappedTrackedEntity) target; + + if ( StringUtils.isBlank( mapped.getName() ) ) + { + errors.rejectValue( "name", "MappedTrackedEntity.name.blank", "Name must not be blank." ); + } + if ( StringUtils.length( mapped.getName() ) > MappedTrackedEntity.MAX_NAME_LENGTH ) + { + errors.rejectValue( "name", "MappedTrackedEntity.name.length", new Object[]{ MappedTrackedEntity.MAX_NAME_LENGTH }, "Name must not be longer than {0} characters." ); + } + if ( mapped.getTrackedEntityReference() == null ) + { + errors.rejectValue( "trackedEntityReference", "MappedTrackedEntity.trackedEntityReference.null", "Tracked entity reference is mandatory." ); + } + else if ( !mapped.getTrackedEntityReference().isValid() ) + { + errors.rejectValue( "trackedEntityReference", "MappedTrackedEntity.trackedEntityReference.invalid", "Tracked entity reference is not valid." ); + } + if ( mapped.getTrackedEntityIdentifierReference() == null ) + { + errors.rejectValue( "trackedEntityIdentifierReference", "MappedTrackedEntity.trackedEntityIdentifierReference.null", "Tracked entity identifier reference is mandatory." ); + } + else if ( !mapped.getTrackedEntityIdentifierReference().isValid() ) + { + errors.rejectValue( "trackedEntityIdentifierReference", "MappedTrackedEntity.trackedEntityIdentifierReference.invalid", "Tracked entity identifier reference is not valid." ); + } + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveTrackedEntityRuleValidator.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveTrackedEntityRuleValidator.java index 525eddcd..6d0c08da 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveTrackedEntityRuleValidator.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/repository/validator/BeforeCreateSaveTrackedEntityRuleValidator.java @@ -55,21 +55,9 @@ public void validate( Object target, @Nonnull Errors errors ) final TrackedEntityRule rule = (TrackedEntityRule) target; validate( rule, TransformDataType.DHIS_TRACKED_ENTITY_INSTANCE, errors ); - if ( rule.getTrackedEntityReference() == null ) + if ( rule.getTrackedEntity() == null ) { - errors.rejectValue( "trackedEntityReference", "TrackedEntityRule.trackedEntityReference.null", "Tracked entity reference is mandatory." ); - } - else if ( !rule.getTrackedEntityReference().isValid() ) - { - errors.rejectValue( "trackedEntityReference", "TrackedEntityRule.trackedEntityReferenceinvalid", "Tracked entity reference is not valid." ); - } - if ( rule.getTrackedEntityIdentifierReference() == null ) - { - errors.rejectValue( "trackedEntityIdentifierReference", "TrackedEntityRule.trackedEntityIdentifierReference.null", "Tracked entity identifier reference is mandatory." ); - } - else if ( !rule.getTrackedEntityIdentifierReference().isValid() ) - { - errors.rejectValue( "trackedEntityIdentifierReference", "TrackedEntityRule.trackedEntityIdentifierReference.invalid", "Tracked entity identifier reference is not valid." ); + errors.rejectValue( "trackedEntity", "TrackedEntityRule.trackedEntity.null", "Tracked entity is mandatory." ); } BeforeCreateSaveFhirResourceMappingValidator.checkValidOrgLookupScript( errors, "TrackedEntityRule.", "orgUnitLookupScript", rule.getFhirResourceType(), rule.getOrgUnitLookupScript() ); BeforeCreateSaveFhirResourceMappingValidator.checkValidLocationLookupScript( errors, "TrackedEntityRule.", "locationLookupScript", rule.getFhirResourceType(), rule.getLocationLookupScript() ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/queue/QueueListenerErrorHandler.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/queue/QueueListenerErrorHandler.java index efcc5542..f1da03d8 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/queue/QueueListenerErrorHandler.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/queue/QueueListenerErrorHandler.java @@ -31,6 +31,7 @@ import com.netflix.hystrix.exception.HystrixRuntimeException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.jms.listener.adapter.ListenerExecutionFailedException; import org.springframework.util.ErrorHandler; import javax.annotation.Nonnull; @@ -48,33 +49,38 @@ public class QueueListenerErrorHandler implements ErrorHandler @Override public void handleError( @Nonnull Throwable t ) { - if ( t instanceof HystrixRuntimeException ) + Throwable exception = t; + if ( (exception instanceof ListenerExecutionFailedException) && (exception.getCause() != null) ) { - final HystrixRuntimeException hre = (HystrixRuntimeException) t; + exception = exception.getCause(); + } + if ( exception instanceof HystrixRuntimeException ) + { + final HystrixRuntimeException hre = (HystrixRuntimeException) exception; switch ( hre.getFailureType() ) { case SHORTCIRCUIT: - logger.debug( "Could not process JMS message due to short circuit.", t ); + logger.debug( "Could not process JMS message due to short circuit.", exception ); logger.info( "Could not process JMS message due to short circuit." ); break; case TIMEOUT: case REJECTED_THREAD_EXECUTION: case REJECTED_SEMAPHORE_EXECUTION: case REJECTED_SEMAPHORE_FALLBACK: - logger.error( "Could not process JMS message due to a reported timeout (should not happen).", t ); + logger.error( "Could not process JMS message due to a reported timeout (should not happen).", exception ); break; default: - logger.error( "An error occurred when processing JMS message.", t ); + logger.error( "An error occurred when processing JMS message.", exception ); break; } } - else if ( t instanceof RetryQueueDeliveryException ) + else if ( exception instanceof RetryQueueDeliveryException ) { - logger.debug( "The delivery of the processed JMS message should be retried when possible.", t.getCause() ); + logger.debug( "The delivery of the processed JMS message should be retried when possible.", exception.getCause() ); } else { - logger.error( "An error occurred when processing JMS message.", t ); + logger.error( "An error occurred when processing JMS message.", exception ); } } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/RemoteRestHookProcessorImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/RemoteRestHookProcessorImpl.java index 9ce8325b..7135919b 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/RemoteRestHookProcessorImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/remote/impl/RemoteRestHookProcessorImpl.java @@ -30,6 +30,7 @@ import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.dhis2.fhir.adapter.fhir.data.model.ProcessedRemoteFhirResource; +import org.dhis2.fhir.adapter.fhir.data.repository.IgnoredSubscriptionResourceException; import org.dhis2.fhir.adapter.fhir.data.repository.ProcessedRemoteFhirResourceRepository; import org.dhis2.fhir.adapter.fhir.data.repository.QueuedRemoteFhirResourceRepository; import org.dhis2.fhir.adapter.fhir.data.repository.QueuedRemoteSubscriptionRequestRepository; @@ -130,9 +131,17 @@ public RemoteRestHookProcessorImpl( @Nonnull RemoteProcessorConfig processorConf public void received( @Nonnull UUID remoteSubscriptionResourceId, @Nonnull String requestId ) { logger.debug( "Checking for a queued entry of remote subscription resource {}.", remoteSubscriptionResourceId ); - if ( !queuedRemoteSubscriptionRequestRepository.enqueue( remoteSubscriptionResourceId, requestId ) ) + try + { + if ( !queuedRemoteSubscriptionRequestRepository.enqueue( remoteSubscriptionResourceId, requestId ) ) + { + logger.debug( "There is already a queued entry for remote subscription resource {}.", remoteSubscriptionResourceId ); + return; + } + } + catch ( IgnoredSubscriptionResourceException e ) { - logger.debug( "There is already a queued entry for remote subscription resource {}.", remoteSubscriptionResourceId ); + // has already been logger with sufficient details return; } @@ -165,7 +174,15 @@ public void receive( @Nonnull RemoteRestHookRequest remoteRestHookRequest ) protected void receiveAuthenticated( @Nonnull RemoteRestHookRequest remoteRestHookRequest ) { logger.info( "Processing queued web hook request {}.", remoteRestHookRequest.getRemoteSubscriptionResourceId() ); - queuedRemoteSubscriptionRequestRepository.dequeued( remoteRestHookRequest.getRemoteSubscriptionResourceId() ); + try + { + queuedRemoteSubscriptionRequestRepository.dequeued( remoteRestHookRequest.getRemoteSubscriptionResourceId() ); + } + catch ( IgnoredSubscriptionResourceException e ) + { + // has already been logger with sufficient details + return; + } final RemoteSubscriptionResource remoteSubscriptionResource = remoteSubscriptionResourceRepository.findByIdCached( remoteRestHookRequest.getRemoteSubscriptionResourceId() ).orElse( null ); @@ -198,18 +215,25 @@ protected void receiveAuthenticated( @Nonnull RemoteRestHookRequest remoteRestHo { // persist processed remote FHIR resource and processedRemoteFhirResourceRepository.process( new ProcessedRemoteFhirResource( remoteSubscriptionResource, versionedId, processedAt ), p -> { - if ( queuedRemoteFhirResourceRepository.enqueue( remoteSubscriptionResource.getId(), sr.getId(), requestId ) ) + try { - fhirResourceQueueJmsTemplate.convertAndSend( - new RemoteFhirResource( remoteSubscriptionResource.getId(), sr.getId(), sr.getVersion(), sr.getLastUpdated() ) ); - logger.debug( "FHIR Resource {} of remote subscription resource {} has been enqueued.", - sr.getId(), remoteSubscriptionResource.getId() ); - count.incrementAndGet(); + if ( queuedRemoteFhirResourceRepository.enqueue( remoteSubscriptionResource.getId(), sr.getId(), requestId ) ) + { + fhirResourceQueueJmsTemplate.convertAndSend( + new RemoteFhirResource( remoteSubscriptionResource.getId(), sr.getId(), sr.getVersion(), sr.getLastUpdated() ) ); + logger.debug( "FHIR Resource {} of remote subscription resource {} has been enqueued.", + sr.getId(), remoteSubscriptionResource.getId() ); + count.incrementAndGet(); + } + else + { + logger.debug( "FHIR Resource {} of remote subscription resource {} is still queued.", + sr.getId(), remoteSubscriptionResource.getId() ); + } } - else + catch ( IgnoredSubscriptionResourceException e ) { - logger.debug( "FHIR Resource {} of remote subscription resource {} is still queued.", - sr.getId(), remoteSubscriptionResource.getId() ); + // has already been logger with sufficient details } } ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteHierarchicallyFhirRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteHierarchicallyFhirRepositoryImpl.java index 6e27020a..ab69f9c1 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteHierarchicallyFhirRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/AbstractRemoteHierarchicallyFhirRepositoryImpl.java @@ -81,6 +81,7 @@ public IBaseBundle findWithParents( @Nonnull UUID remoteSubscriptionId, @Nonnull return createBundle( Collections.emptyList() ); } + final Set processedResources = new HashSet<>(); final Set processedResourceIds = new HashSet<>(); processedResourceIds.add( resourceType + "/" + resourceId ); final List result = new ArrayList<>(); @@ -89,17 +90,29 @@ public IBaseBundle findWithParents( @Nonnull UUID remoteSubscriptionId, @Nonnull IBaseReference parentReference; while ( (child != null) && ((parentReference = parentReferenceFunction.apply( child )) != null) && !parentReference.isEmpty() && parentReference.getReferenceElement().hasIdPart() ) { - final String childResourceType = parentReference.getReferenceElement().hasResourceType() ? - parentReference.getReferenceElement().getResourceType() : resourceType; - if ( !processedResourceIds.add( childResourceType + "/" + parentReference.getReferenceElement().getIdPart() ) ) + if ( parentReference.getResource() == null ) { - // there is a dependency loop and search must be interrupted - break; + final String childResourceType = parentReference.getReferenceElement().hasResourceType() ? + parentReference.getReferenceElement().getResourceType() : resourceType; + if ( !processedResourceIds.add( childResourceType + "/" + parentReference.getReferenceElement().getIdPart() ) ) + { + // there is a dependency loop and search must be interrupted + break; + } + child = remoteFhirRepository.find( remoteSubscriptionId, fhirVersion, fhirEndpoint, + childResourceType, parentReference.getReferenceElement().getIdPart() ).orElse( null ); + } + else + { + child = parentReference.getResource(); } - child = remoteFhirRepository.find( remoteSubscriptionId, fhirVersion, fhirEndpoint, - childResourceType, parentReference.getReferenceElement().getIdPart() ).orElse( null ); if ( child != null ) { + if ( !processedResources.add( child ) ) + { + // there is a dependency loop and search must be interrupted + break; + } result.add( child ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java index c48e5acd..23bb29ee 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/repository/impl/FhirRepositoryImpl.java @@ -40,6 +40,7 @@ import org.dhis2.fhir.adapter.dhis.tracker.program.EventService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityInstance; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; +import org.dhis2.fhir.adapter.fhir.data.repository.IgnoredSubscriptionResourceException; import org.dhis2.fhir.adapter.fhir.data.repository.QueuedRemoteFhirResourceRepository; import org.dhis2.fhir.adapter.fhir.metadata.model.AuthenticationMethod; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; @@ -64,7 +65,9 @@ import org.dhis2.fhir.adapter.fhir.transform.model.WritableFhirRequest; import org.dhis2.fhir.adapter.lock.LockContext; import org.dhis2.fhir.adapter.lock.LockManager; +import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IDomainResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -186,8 +189,16 @@ protected void receiveAuthenticated( @Nonnull RemoteFhirResource remoteFhirResou { logger.info( "Processing FHIR resource {} for remote subscription resource {}.", remoteFhirResource.getFhirResourceId(), remoteFhirResource.getRemoteSubscriptionResourceId() ); - queuedRemoteFhirResourceRepository.dequeued( - remoteFhirResource.getRemoteSubscriptionResourceId(), remoteFhirResource.getFhirResourceId() ); + try + { + queuedRemoteFhirResourceRepository.dequeued( + remoteFhirResource.getRemoteSubscriptionResourceId(), remoteFhirResource.getFhirResourceId() ); + } + catch ( IgnoredSubscriptionResourceException e ) + { + // has already been logger with sufficient details + return; + } final RemoteSubscriptionResource remoteSubscriptionResource = remoteSubscriptionResourceRepository.findByIdCached( remoteFhirResource.getRemoteSubscriptionResourceId() ).orElse( null ); @@ -236,7 +247,10 @@ protected boolean saveRetriedWithoutTrackedEntityInstance( @Nonnull RemoteSubscr { try { - return saveRetried( subscriptionResource, resource ); + if ( !saveRetried( subscriptionResource, resource, false ) ) + { + return false; + } } catch ( TrackedEntityInstanceNotFoundException e ) { @@ -251,7 +265,8 @@ protected boolean saveRetriedWithoutTrackedEntityInstance( @Nonnull RemoteSubscr try { - return saveRetried( subscriptionResource, resource ); + if ( !saveRetried( subscriptionResource, resource, false ) ) + return false; } catch ( TrackedEntityInstanceNotFoundException e2 ) { @@ -260,6 +275,44 @@ protected boolean saveRetriedWithoutTrackedEntityInstance( @Nonnull RemoteSubscr return false; } } + + for ( final IAnyResource containedResource : ((IDomainResource) resource).getContained() ) + { + logger.info( "Processing contained FHIR resource {} with ID {}.", + containedResource.getClass().getSimpleName(), containedResource.getIdElement().toUnqualifiedVersionless() ); + try + { + saveContainedResource( subscriptionResource, containedResource ); + } + catch ( TrackedEntityInstanceNotFoundException e ) + { + logger.error( "Processing of contained FHIR resource {} with ID {} returned that tracked entity could not be found: {}", + containedResource.getClass().getSimpleName(), containedResource.getIdElement().toUnqualifiedVersionless(), e.getMessage() ); + } + } + + return true; + } + + protected boolean saveContainedResource( @Nonnull RemoteSubscriptionResource subscriptionResource, @Nonnull IBaseResource resource ) + { + final FhirResourceType fhirResourceType = FhirResourceType.getByResource( resource ); + if ( fhirResourceType == null ) + { + logger.info( "FHIR Resource type for {} is not available. Contained FHIR Resource cannot be processed." ); + return false; + } + + final RemoteSubscriptionResource containedRemoteSubscriptionResource = + remoteSubscriptionResourceRepository.findFirstCached( subscriptionResource.getRemoteSubscription(), fhirResourceType ).orElse( null ); + if ( containedRemoteSubscriptionResource == null ) + { + logger.info( "Remote subscription {} does not define a resource {}. Contained FHIR Resource cannot be processed.", + subscriptionResource.getRemoteSubscription().getId(), fhirResourceType ); + return false; + } + + return saveRetried( containedRemoteSubscriptionResource, resource, true ); } protected boolean createTrackedEntityInstance( @Nonnull RemoteSubscriptionResource subscriptionResource, @Nonnull IBaseResource resource ) @@ -282,26 +335,28 @@ protected boolean createTrackedEntityInstance( @Nonnull RemoteSubscriptionResour } final RemoteSubscription remoteSubscription = trackedEntityRemoteSubscriptionResource.getRemoteSubscription(); - final Optional refreshedResource = remoteFhirRepository.findRefreshed( + final Optional optionalRefreshedResource = remoteFhirRepository.findRefreshed( remoteSubscription.getId(), remoteSubscription.getFhirVersion(), remoteSubscription.getFhirEndpoint(), fhirResourceType.getResourceTypeName(), resource.getIdElement().getIdPart() ); - if ( !refreshedResource.isPresent() ) + if ( !optionalRefreshedResource.isPresent() ) { logger.info( "Resource {} that should be used as tracked entity resource does no longer exist for remote subscription {}.", resource.getIdElement(), trackedEntityRemoteSubscriptionResource.getRemoteSubscription().getId() ); return false; } - return saveRetried( trackedEntityRemoteSubscriptionResource, refreshedResource.get() ); + + saveRetried( trackedEntityRemoteSubscriptionResource, optionalRefreshedResource.get(), false ); + return true; } - protected boolean saveRetried( @Nonnull RemoteSubscriptionResource subscriptionResource, @Nonnull IBaseResource resource ) + protected boolean saveRetried( @Nonnull RemoteSubscriptionResource subscriptionResource, @Nonnull IBaseResource resource, boolean contained ) { DhisConflictException lastException = null; for ( int i = 0; i < MAX_CONFLICT_RETRIES + 1; i++ ) { try { - return saveInternally( subscriptionResource, resource ); + return saveInternally( subscriptionResource, resource, contained ); } catch ( DhisConflictException e ) { @@ -313,7 +368,7 @@ protected boolean saveRetried( @Nonnull RemoteSubscriptionResource subscriptionR } @SuppressWarnings( "unchecked" ) - protected boolean saveInternally( @Nonnull RemoteSubscriptionResource subscriptionResource, @Nonnull IBaseResource resource ) + protected boolean saveInternally( @Nonnull RemoteSubscriptionResource subscriptionResource, @Nonnull IBaseResource resource, boolean contained ) { final Collection systems = remoteSubscriptionSystemRepository.findByRemoteSubscription( subscriptionResource.getRemoteSubscription() ); @@ -336,7 +391,7 @@ protected boolean saveInternally( @Nonnull RemoteSubscriptionResource subscripti final HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { - outcome = fhirToDhisTransformerService.transform( fhirToDhisTransformerService.createContext( fhirRequest ), resource ); + outcome = fhirToDhisTransformerService.transform( fhirToDhisTransformerService.createContext( fhirRequest ), resource, contained ); } finally { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/FhirToDhisTransformerContext.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/FhirToDhisTransformerContext.java index b18f68d6..6262e131 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/FhirToDhisTransformerContext.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/FhirToDhisTransformerContext.java @@ -33,10 +33,12 @@ import org.dhis2.fhir.adapter.dhis.model.ReferenceType; import org.dhis2.fhir.adapter.fhir.transform.impl.TransformerScriptException; import org.dhis2.fhir.adapter.fhir.transform.model.FhirRequest; +import org.hl7.fhir.instance.model.api.IBaseResource; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.time.ZonedDateTime; +import java.util.List; @Scriptable public interface FhirToDhisTransformerContext @@ -58,6 +60,9 @@ public interface FhirToDhisTransformerContext Reference createReference( @Nullable String value, @Nonnull Object referenceType ) throws TransformerScriptException; + @Nonnull + List createFhirResourceList(); + @Nonnull ZonedDateTime now(); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/FhirToDhisTransformerService.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/FhirToDhisTransformerService.java index d8700c6a..9abd98b9 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/FhirToDhisTransformerService.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/FhirToDhisTransformerService.java @@ -41,6 +41,6 @@ public interface FhirToDhisTransformerService FhirToDhisTransformerContext createContext( @Nonnull FhirRequest fhirRequest ); @Nullable - FhirToDhisTransformOutcome transform( @Nonnull FhirToDhisTransformerContext context, @Nonnull IBaseResource input ) + FhirToDhisTransformOutcome transform( @Nonnull FhirToDhisTransformerContext context, @Nonnull IBaseResource input, boolean contained ) throws TransformerException; } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/AbstractFhirToDhisTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/AbstractFhirToDhisTransformer.java index 1791acd9..002dd309 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/AbstractFhirToDhisTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/AbstractFhirToDhisTransformer.java @@ -190,11 +190,16 @@ protected Optional getOrgUnit( @Nonnull FhirToDhisTransformerC logger.info( "Could not extract organization unit reference." ); return Optional.empty(); } + return getOrgUnit( context, orgUnitReference, scriptVariables ); + } + @Nonnull + protected Optional getOrgUnit( @Nonnull FhirToDhisTransformerContext context, @Nonnull Reference orgUnitReference, @Nonnull Map scriptVariables ) + { final Optional organisationUnit = organisationUnitService.findOneByReference( orgUnitReference ); if ( !organisationUnit.isPresent() ) { - logger.info( "Organization unit of extracted reference does not exist: " + orgUnitReference ); + logger.info( "Organization unit of reference does not exist: " + orgUnitReference ); } return organisationUnit; } @@ -234,8 +239,8 @@ protected Optional getTrackedEntityInstanceByIdentifier( identifier = createFullQualifiedTrackedEntityInstanceIdentifier( context, identifier ); final TrackedEntityAttributes trackedEntityAttributes = getScriptVariable( scriptVariables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ); - final TrackedEntityAttribute identifierAttribute = trackedEntityAttributes.getOptional( rule.getTrackedEntityIdentifierReference() ) - .orElseThrow( () -> new TransformerMappingException( "Tracked entity identifier attribute does not exist: " + rule.getTrackedEntityIdentifierReference() ) ); + final TrackedEntityAttribute identifierAttribute = trackedEntityAttributes.getOptional( rule.getTrackedEntity().getTrackedEntityIdentifierReference() ) + .orElseThrow( () -> new TransformerMappingException( "Tracked entity identifier attribute does not exist: " + rule.getTrackedEntity().getTrackedEntityIdentifierReference() ) ); final TrackedEntityType trackedEntityType = getScriptVariable( scriptVariables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ); final Collection result; @@ -254,7 +259,9 @@ protected Optional getTrackedEntityInstanceByIdentifier( throw new TransformerMappingException( "Filtering with identifier of rule " + rule + " returned more than one tracked entity instance: " + identifier ); } - return result.stream().findFirst(); + + final String finalIdentifier = identifier; + return result.stream().peek( tei -> tei.setIdentifier( finalIdentifier ) ).findFirst(); } protected String createFullQualifiedTrackedEntityInstanceIdentifier( @Nonnull FhirToDhisTransformerContext context, String identifier ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/FhirToDhisTransformerContextImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/FhirToDhisTransformerContextImpl.java index 1aa51041..1ab45cb9 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/FhirToDhisTransformerContextImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/FhirToDhisTransformerContextImpl.java @@ -34,11 +34,14 @@ import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.model.FhirRequest; import org.dhis2.fhir.adapter.fhir.transform.model.ImmutableFhirRequest; +import org.hl7.fhir.instance.model.api.IBaseResource; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.Serializable; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; public class FhirToDhisTransformerContextImpl implements FhirToDhisTransformerContext, Serializable { @@ -82,6 +85,13 @@ public Reference createReference( @Nullable String value, @Nonnull Object refere return new Reference( value, rt ); } + @Nonnull + @Override + public List createFhirResourceList() + { + return new ArrayList<>(); + } + @Nonnull @Override public ZonedDateTime now() diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/FhirToDhisTransformerServiceImpl.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/FhirToDhisTransformerServiceImpl.java index a39aef62..83bf8758 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/FhirToDhisTransformerServiceImpl.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/FhirToDhisTransformerServiceImpl.java @@ -122,7 +122,7 @@ public FhirToDhisTransformerContext createContext( @Nonnull FhirRequest fhirRequ @Nullable @Override - public FhirToDhisTransformOutcome transform( @Nonnull FhirToDhisTransformerContext context, @Nonnull IBaseResource originalInput ) throws TransformerException + public FhirToDhisTransformOutcome transform( @Nonnull FhirToDhisTransformerContext context, @Nonnull IBaseResource originalInput, boolean contained ) throws TransformerException { final FhirContext fhirContext = remoteFhirRepository.findFhirContext( context.getFhirRequest().getVersion() ) .orElseThrow( () -> new FatalTransformerException( "FHIR context for FHIR version " + context.getFhirRequest().getVersion() + " is not available." ) ); @@ -144,6 +144,11 @@ public FhirToDhisTransformOutcome transform( @Nonnull Fh codeTransformerUtils.getResourceCodes( input ) ); for ( final AbstractRule rule : rules ) { + if ( contained && !rule.isContainedAllowed() ) + { + continue; + } + final FhirToDhisTransformer transformer = this.transformers.get( new FhirVersionedValue<>( context.getFhirRequest().getVersion(), rule.getDhisResourceType() ) ); if ( transformer == null ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/program/FhirToProgramStageTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/program/FhirToProgramStageTransformer.java index 49bf2ba6..78560ade 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/program/FhirToProgramStageTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/program/FhirToProgramStageTransformer.java @@ -244,8 +244,13 @@ protected void addBasicScriptVariables( @Nonnull Map variables, variables.put( ScriptVariable.TRACKED_ENTITY_TYPE.getVariableName(), trackedEntityType ); } - protected void addScriptVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull Map variables, @Nonnull ProgramStageRule rule, @Nonnull TrackedEntityInstance trackedEntityInstance ) throws TransformerException + protected void addScriptVariables( @Nonnull FhirToDhisTransformerContext context, @Nonnull Map variables, @Nonnull ProgramStageRule rule, + @Nonnull TrackedEntityInstance trackedEntityInstance ) throws TransformerException { + if ( trackedEntityInstance.getIdentifier() == null ) + { + throw new FatalTransformerException( "Identifier of tracked entity instance has not yet been set." ); + } variables.put( ScriptVariable.TRACKED_ENTITY_INSTANCE.getVariableName(), new WritableScriptedTrackedEntityInstance( context, getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ), getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ), diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/FhirToTrackedEntityTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/FhirToTrackedEntityTransformer.java index b903645d..36d43d32 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/FhirToTrackedEntityTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/FhirToTrackedEntityTransformer.java @@ -30,6 +30,8 @@ import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; import org.dhis2.fhir.adapter.dhis.model.DhisResourceType; +import org.dhis2.fhir.adapter.dhis.model.Reference; +import org.dhis2.fhir.adapter.dhis.model.ReferenceType; import org.dhis2.fhir.adapter.dhis.orgunit.OrganisationUnit; import org.dhis2.fhir.adapter.dhis.orgunit.OrganisationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.RequiredValueType; @@ -44,6 +46,7 @@ import org.dhis2.fhir.adapter.fhir.transform.FatalTransformerException; import org.dhis2.fhir.adapter.fhir.transform.FhirToDhisTransformOutcome; import org.dhis2.fhir.adapter.fhir.transform.FhirToDhisTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.TrackedEntityInstanceNotFoundException; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; import org.dhis2.fhir.adapter.fhir.transform.impl.AbstractFhirToDhisTransformer; @@ -104,6 +107,11 @@ public Class getRuleClass() public FhirToDhisTransformOutcome transform( @Nonnull FhirToDhisTransformerContext context, @Nonnull IBaseResource input, @Nonnull TrackedEntityRule rule, @Nonnull Map scriptVariables ) throws TransformerException { + if ( !rule.getTrackedEntity().isEnabled() ) + { + return null; + } + final Map variables = new HashMap<>( scriptVariables ); if ( !addScriptVariables( variables, rule ) ) { @@ -112,34 +120,41 @@ public FhirToDhisTransformOutcome transform( @Nonnull Fhi final TrackedEntityAttributes trackedEntityAttributes = getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ); final TrackedEntityType trackedEntityType = getScriptVariable( variables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ); - final TrackedEntityInstance trackedEntityInstance = getResource( context, rule, variables ) - .orElseThrow( () -> new FatalTransformerException( "Tracked entity instance could neither be retrieved nor created." ) ); + final TrackedEntityInstance trackedEntityInstance = getResource( context, rule, variables ).orElse( null ); + if ( trackedEntityInstance == null ) + { + return null; + } + final WritableScriptedTrackedEntityInstance scriptedTrackedEntityInstance = new WritableScriptedTrackedEntityInstance( context, trackedEntityAttributes, trackedEntityType, trackedEntityInstance, valueConverter ); variables.put( ScriptVariable.OUTPUT.getVariableName(), scriptedTrackedEntityInstance ); - final Optional organisationUnit = getOrgUnit( context, rule.getOrgUnitLookupScript(), variables ); + final Optional organisationUnit; + if ( rule.getOrgUnitLookupScript() == null ) + { + if ( scriptedTrackedEntityInstance.getOrganizationUnitId() == null ) + { + logger.info( "Rule does not define an organization unit lookup script and tracked entity instance does not yet include one." ); + return null; + } + organisationUnit = getOrgUnit( context, + new Reference( scriptedTrackedEntityInstance.getOrganizationUnitId(), ReferenceType.ID ), variables ); + } + else + { + organisationUnit = getOrgUnit( context, rule.getOrgUnitLookupScript(), variables ); + organisationUnit.ifPresent( ou -> scriptedTrackedEntityInstance.setOrganizationUnitId( ou.getId() ) ); + } if ( !organisationUnit.isPresent() ) { return null; } - scriptedTrackedEntityInstance.setOrganizationUnitId( organisationUnit.get().getId() ); - if ( trackedEntityInstance.isNewResource() ) + if ( rule.getLocationLookupScript() != null ) { - String identifier = getIdentifier( context, rule, scriptVariables ); - if ( identifier == null ) - { - return null; - } - identifier = createFullQualifiedTrackedEntityInstanceIdentifier( context, identifier ); - - final String attributeId = trackedEntityAttributes.getOptional( rule.getTrackedEntityIdentifierReference() ).orElseThrow( () -> - new TransformerMappingException( "Tracked entity identifier attribute does not exist: " + rule.getTrackedEntityIdentifierReference() ) ).getId(); - trackedEntityInstance.getAttribute( attributeId ).setValue( identifier ); + scriptedTrackedEntityInstance.setCoordinates( getCoordinate( context, rule.getLocationLookupScript(), variables ) ); } - - scriptedTrackedEntityInstance.setCoordinates( getCoordinate( context, rule.getLocationLookupScript(), variables ) ); if ( !transform( context, rule, variables ) ) { return null; @@ -155,8 +170,10 @@ protected boolean addScriptVariables( @Nonnull Map arguments, @N { final TrackedEntityAttributes trackedEntityAttributes = trackedEntityMetadataService.getAttributes(); arguments.put( ScriptVariable.TRACKED_ENTITY_ATTRIBUTES.getVariableName(), trackedEntityAttributes ); - final TrackedEntityType trackedEntityType = trackedEntityMetadataService.getType( rule.getTrackedEntityReference() ) - .orElseThrow( () -> new TransformerMappingException( "Tracked entity type in rule " + rule + " could not be found: " + rule.getTrackedEntityReference() ) ); + final TrackedEntityType trackedEntityType = trackedEntityMetadataService.getType( + rule.getTrackedEntity().getTrackedEntityReference() ) + .orElseThrow( () -> new TransformerMappingException( "Tracked entity type in rule " + rule + " could not be found: " + + rule.getTrackedEntity().getTrackedEntityReference() ) ); arguments.put( ScriptVariable.TRACKED_ENTITY_TYPE.getVariableName(), trackedEntityType ); // is applicable for further processing @@ -181,7 +198,19 @@ protected Optional getResourceById( @Nullable String id ) protected Optional getActiveResource( @Nonnull FhirToDhisTransformerContext context, @Nonnull TrackedEntityRule rule, @Nonnull Map scriptVariables, boolean sync ) throws TransformerException { - final IBaseResource baseResource = getScriptVariable( scriptVariables, ScriptVariable.INPUT, IBaseResource.class ); + final IBaseResource baseResource; + if ( rule.getTeiLookupScript() == null ) + { + baseResource = getScriptVariable( scriptVariables, ScriptVariable.INPUT, IBaseResource.class ); + } + else + { + baseResource = getScriptExecutor().execute( rule.getTeiLookupScript(), context.getFhirRequest().getVersion(), scriptVariables, IBaseResource.class ); + } + if ( baseResource == null ) + { + return Optional.empty(); + } if ( sync ) { lockManager.getCurrentLockContext().orElseThrow( () -> new FatalTransformerException( "No lock context available." ) ) @@ -199,14 +228,54 @@ protected TrackedEntityInstance createResource( @Nonnull FhirToDhisTransformerCo { return null; } - return new TrackedEntityInstance( + + if ( rule.getTeiLookupScript() != null ) + { + final IBaseResource resource = getScriptExecutor().execute( rule.getTeiLookupScript(), context.getFhirRequest().getVersion(), scriptVariables, IBaseResource.class ); + if ( resource == null ) + { + return null; + } + throw new TrackedEntityInstanceNotFoundException( "Tracked entity for resource " + resource.getIdElement().toUnqualifiedVersionless() + " could not be found.", resource ); + } + + String identifier = getIdentifier( context, rule, scriptVariables ); + if ( identifier == null ) + { + return null; + } + identifier = createFullQualifiedTrackedEntityInstanceIdentifier( context, identifier ); + + final TrackedEntityInstance trackedEntityInstance = new TrackedEntityInstance( getScriptVariable( scriptVariables, ScriptVariable.TRACKED_ENTITY_TYPE, TrackedEntityType.class ), id, true ); + trackedEntityInstance.setIdentifier( identifier ); + + final TrackedEntityAttributes trackedEntityAttributes = getScriptVariable( scriptVariables, ScriptVariable.TRACKED_ENTITY_ATTRIBUTES, TrackedEntityAttributes.class ); + final String attributeId = trackedEntityAttributes.getOptional( + rule.getTrackedEntity().getTrackedEntityIdentifierReference() ).orElseThrow( () -> + new TransformerMappingException( "Tracked entity identifier attribute does not exist: " + + rule.getTrackedEntity().getTrackedEntityIdentifierReference() ) ).getId(); + trackedEntityInstance.getAttribute( attributeId ).setValue( identifier ); + + return trackedEntityInstance; } @Nullable protected String getIdentifier( @Nonnull FhirToDhisTransformerContext context, @Nonnull TrackedEntityRule rule, @Nonnull Map scriptVariables ) { - final IBaseResource baseResource = getScriptVariable( scriptVariables, ScriptVariable.INPUT, IBaseResource.class ); + final IBaseResource baseResource; + if ( rule.getTeiLookupScript() == null ) + { + baseResource = getScriptVariable( scriptVariables, ScriptVariable.INPUT, IBaseResource.class ); + } + else + { + baseResource = getScriptExecutor().execute( rule.getTeiLookupScript(), context.getFhirRequest().getVersion(), scriptVariables, IBaseResource.class ); + } + if ( baseResource == null ) + { + return null; + } return getIdentifier( context, baseResource, scriptVariables ); } } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/WritableScriptedTrackedEntityInstance.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/WritableScriptedTrackedEntityInstance.java index 4fe802a9..3875edd0 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/WritableScriptedTrackedEntityInstance.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/trackedentity/WritableScriptedTrackedEntityInstance.java @@ -31,6 +31,7 @@ import org.dhis2.fhir.adapter.Scriptable; import org.dhis2.fhir.adapter.converter.ConversionException; import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; +import org.dhis2.fhir.adapter.dhis.model.Option; import org.dhis2.fhir.adapter.dhis.model.Reference; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttribute; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityAttributeValue; @@ -47,7 +48,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.time.ZonedDateTime; +import java.util.Locale; import java.util.Objects; +import java.util.Optional; @Scriptable public class WritableScriptedTrackedEntityInstance implements ScriptedTrackedEntityInstance @@ -90,6 +93,12 @@ public String getTypeId() return trackedEntityType.getId(); } + @Nonnull + public String getIdentifier() + { + return trackedEntityInstance.getIdentifier(); + } + @Override @Nullable public String getOrganizationUnitId() @@ -188,7 +197,7 @@ protected boolean setValue( @Nonnull TrackedEntityAttribute attribute, @Nullable throw new TransformerMappingException( "Value of tracked entity type attribute \"" + attribute.getName() + "\" is generated and cannot be set." ); } - final Object convertedValue; + Object convertedValue; try { convertedValue = valueConverter.convert( value, attribute.getValueType(), String.class ); @@ -198,6 +207,44 @@ protected boolean setValue( @Nonnull TrackedEntityAttribute attribute, @Nullable throw new TransformerMappingException( "Value of tracked entity type attribute \"" + attribute.getName() + "\" could not be converted: " + e.getMessage(), e ); } + if ( (convertedValue != null) && attribute.isOptionSetValue() ) + { + String checked1 = convertedValue.toString(); + boolean found = attribute.getOptionSet().getOptions().stream() + .anyMatch( o -> Objects.equals( checked1, o.getCode() ) ); + if ( found ) + { + convertedValue = checked1; + } + else + { + // try locale independent upper case match + final String checked2 = checked1.toUpperCase( Locale.ROOT ); + // try locale independent upper case match + found = attribute.getOptionSet().getOptions().stream() + .anyMatch( o -> checked2.equals( o.getCode() ) ); + if ( found ) + { + convertedValue = checked2; + } + else + { + final Optional option = attribute.getOptionSet().getOptions().stream().filter( o -> o.getCode() != null ) + .filter( o -> checked2.equals( o.getCode().toUpperCase( Locale.ROOT ) ) ).findFirst(); + if ( option.isPresent() ) + { + convertedValue = option.get().getCode(); + found = true; + } + } + } + if ( !found ) + { + throw new TransformerMappingException( "Code \"" + convertedValue + "\" is not a valid option of \"" + + attribute.getOptionSet().getName() + "\" for attribute \"" + attribute.getName() + "\"." ); + } + } + final TrackedEntityAttributeValue attributeValue = trackedEntityInstance.getAttribute( attribute.getId() ); if ( (lastUpdated != null) && (attributeValue.getLastUpdated() != null) && attributeValue.getLastUpdated().isAfter( lastUpdated ) ) { @@ -236,7 +283,7 @@ public void validate() throws TransformerException } trackedEntityType.getAttributes().stream().filter( TrackedEntityTypeAttribute::isMandatory ).forEach( ta -> { - if ( !trackedEntityInstance.containsAttribute( ta.getAttributeId() ) ) + if ( !trackedEntityInstance.containsAttributeWithValue( ta.getAttributeId() ) ) { throw new TransformerMappingException( "Value of tracked entity type attribute \"" + ta.getName() + "\" is mandatory and must be set." ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractAddressFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractAddressFhirToDhisTransformerUtils.java index 82d80c10..49bd3431 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractAddressFhirToDhisTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractAddressFhirToDhisTransformerUtils.java @@ -41,6 +41,8 @@ public abstract class AbstractAddressFhirToDhisTransformerUtils extends Abstract { protected static final String DEFAULT_LINE_DELIMITER = " "; + protected static final String DEFAULT_TEXT_DELIMITER = " / "; + private static final String SCRIPT_ATTR_NAME = "addressUtils"; protected AbstractAddressFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext ) @@ -61,6 +63,12 @@ public String getSingleLine( @Nullable ICompositeType address ) return getSingleLine( address, DEFAULT_LINE_DELIMITER ); } + @Nullable + public String getConstructedText( @Nullable ICompositeType address ) + { + return getConstructedText( address, DEFAULT_TEXT_DELIMITER ); + } + public abstract boolean hasPrimaryAddress( @Nonnull List addresses ); @Nullable @@ -68,4 +76,10 @@ public String getSingleLine( @Nullable ICompositeType address ) @Nullable public abstract String getSingleLine( @Nullable ICompositeType address, @Nonnull String delimiter ); + + @Nullable + public abstract String getConstructedText( @Nullable ICompositeType address, @Nonnull String delimiter ); + + @Nullable + public abstract String getText( @Nullable ICompositeType address ); } diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractCodeFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractCodeFhirToDhisTransformerUtils.java index 457e0d08..6050c372 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractCodeFhirToDhisTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractCodeFhirToDhisTransformerUtils.java @@ -32,8 +32,11 @@ import org.dhis2.fhir.adapter.fhir.metadata.model.Code; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; +import org.dhis2.fhir.adapter.fhir.metadata.model.SystemCode; import org.dhis2.fhir.adapter.fhir.metadata.repository.CodeRepository; +import org.dhis2.fhir.adapter.fhir.metadata.repository.SystemCodeRepository; import org.dhis2.fhir.adapter.fhir.model.SystemCodeValue; +import org.dhis2.fhir.adapter.fhir.script.ScriptArgUtils; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionRequired; import org.dhis2.fhir.adapter.fhir.transform.FhirToDhisTransformerContext; @@ -44,14 +47,18 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; +import org.springframework.util.CollectionUtils; import org.springframework.util.ReflectionUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -64,12 +71,21 @@ public abstract class AbstractCodeFhirToDhisTransformerUtils extends AbstractFhi private final CodeRepository codeRepository; + private final SystemCodeRepository systemCodeRepository; + private volatile Map, Method> codeMethods = new HashMap<>(); - protected AbstractCodeFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull CodeRepository codeRepository ) + protected AbstractCodeFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull CodeRepository codeRepository, @Nonnull SystemCodeRepository systemCodeRepository ) { super( scriptExecutionContext ); this.codeRepository = codeRepository; + this.systemCodeRepository = systemCodeRepository; + } + + @Nonnull + protected SystemCodeRepository getSystemCodeRepository() + { + return systemCodeRepository; } @Nonnull @@ -85,13 +101,51 @@ public final String getScriptAttrName() @Nullable public abstract String getCode( @Nullable ICompositeType codeableConcept, @Nullable String system ); - public abstract boolean containsMappedCode( @Nullable ICompositeType codeableConcept, @Nullable Object mappedCodes ); + public abstract boolean containsMappingCode( @Nullable ICompositeType codeableConcept, @Nullable Object mappingCodes ); public abstract boolean containsCode( @Nullable ICompositeType codeableConcept, @Nonnull String system, @Nonnull String code ); @Nullable protected abstract List getSystemCodeValues( @Nonnull IDomainResource domainResource, @Nonnull Method identifierMethod ); + public boolean containsAnyCode( @Nullable ICompositeType codeableConcept, @Nullable Collection systemCodeValues ) + { + if ( (codeableConcept == null) || (systemCodeValues == null) ) + { + return false; + } + return systemCodeValues.stream().anyMatch( scv -> containsCode( codeableConcept, scv.getSystem(), scv.getCode() ) ); + } + + @Nonnull + public Map> getSystemCodeValuesByCodes( @Nullable Object[] codes ) + { + if ( (codes == null) || (codes.length == 0) ) + { + return Collections.emptyMap(); + } + + final List convertedCodes = ScriptArgUtils.extractStringArray( codes ); + if ( CollectionUtils.isEmpty( convertedCodes ) ) + { + return Collections.emptyMap(); + } + + // map must be returned ordered + final Map> result = new LinkedHashMap<>(); + convertedCodes.forEach( c -> result.put( c, new ArrayList<>() ) ); + + final Collection systemCodes = getSystemCodeRepository().findAllByCodes( convertedCodes ); + for ( final SystemCode systemCode : systemCodes ) + { + result.computeIfPresent( systemCode.getCode().getCode(), ( k, v ) -> { + v.add( systemCode.getCalculatedSystemCodeValue() ); + return v; + } ); + } + return result; + } + @Nullable public List getResourceCodes( @Nullable IBaseResource baseResource ) throws TransformerException { @@ -114,7 +168,7 @@ public List getResourceCodes( @Nullable IBaseResource baseResou } @ScriptExecutionRequired - public String getMappedCode( @Nullable String code, @Nonnull String fhirResourceType ) + public String getMappedCode( @Nullable String code, @Nonnull Object fhirResourceType ) { if ( code == null ) { @@ -134,7 +188,8 @@ public String getMappedCode( @Nullable String code, @Nonnull String fhirResource final ResourceSystem resourceSystem = context.getFhirRequest().getOptionalResourceSystem( resourceType ) .orElseThrow( () -> new TransformerMappingException( "No system has been defined for resource type " + resourceType + "." ) ); - final Code c = codeRepository.findBySystemCode( resourceSystem.getSystem(), code ).stream().findFirst().orElse( null ); + final Code c = codeRepository.findAllBySystemCodes( Collections.singleton( new SystemCodeValue( resourceSystem.getSystem(), code ).toString() ) ) + .stream().findFirst().orElse( null ); if ( c == null ) { return null; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractContactPointFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractContactPointFhirToDhisTransformerUtils.java new file mode 100644 index 00000000..d7c6f433 --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractContactPointFhirToDhisTransformerUtils.java @@ -0,0 +1,64 @@ +package org.dhis2.fhir.adapter.fhir.transform.impl.util; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.Scriptable; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.hl7.fhir.instance.model.api.ICompositeType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +@Scriptable +public abstract class AbstractContactPointFhirToDhisTransformerUtils extends AbstractFhirToDhisTransformerUtils +{ + private static final String SCRIPT_ATTR_NAME = "contactPointUtils"; + + protected AbstractContactPointFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( scriptExecutionContext ); + } + + @Nonnull + @Override + public final String getScriptAttrName() + { + return SCRIPT_ATTR_NAME; + } + + @Nullable + public String getPhoneContactPointValue( @Nullable List contactPoints ) + { + return getContactPointValue( contactPoints, "phone" ); + } + + @Nullable + protected abstract String getContactPointValue( @Nullable List contactPoints, @Nullable String code ); +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractDateTimeFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractDateTimeFhirToDhisTransformerUtils.java index 9503ff0d..f8eafd98 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractDateTimeFhirToDhisTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractDateTimeFhirToDhisTransformerUtils.java @@ -32,6 +32,7 @@ import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.transform.impl.TransformerScriptException; import org.dhis2.fhir.adapter.model.DateUnit; +import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import javax.annotation.Nonnull; @@ -140,6 +141,8 @@ public boolean isOlderThan( @Nullable Object dateTime, int amount, @Nonnull Obje return isOlderThan( ZonedDateTime.now(), dateTime, amount, dateUnit ); } + public abstract boolean isValidNow( @Nullable ICompositeType period ); + @Nullable protected final Integer getAge( @Nonnull Date relativeDate, @Nullable Date date, @Nonnull TemporalUnit temporalUnit ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractFhirClientTransformUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractFhirClientFhirToDhisTransformerUtils.java similarity index 97% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractFhirClientTransformUtils.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractFhirClientFhirToDhisTransformerUtils.java index 0546ffa3..589385fc 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractFhirClientTransformUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractFhirClientFhirToDhisTransformerUtils.java @@ -61,7 +61,7 @@ import java.util.stream.Collectors; @Scriptable -public abstract class AbstractFhirClientTransformUtils extends AbstractFhirToDhisTransformerUtils +public abstract class AbstractFhirClientFhirToDhisTransformerUtils extends AbstractFhirToDhisTransformerUtils { private static final String SCRIPT_ATTR_NAME = "fhirClientUtils"; @@ -71,7 +71,7 @@ public abstract class AbstractFhirClientTransformUtils extends AbstractFhirToDhi private final SystemCodeRepository systemCodeRepository; - protected AbstractFhirClientTransformUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull FhirContext fhirContext, + protected AbstractFhirClientFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull FhirContext fhirContext, @Nonnull RemoteSubscriptionResourceRepository subscriptionResourceRepository, @Nonnull SystemCodeRepository systemCodeRepository ) { super( scriptExecutionContext ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractGeoFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractGeoFhirToDhisTransformerUtils.java index 36e4e876..3edd2468 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractGeoFhirToDhisTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractGeoFhirToDhisTransformerUtils.java @@ -41,7 +41,7 @@ @Scriptable public abstract class AbstractGeoFhirToDhisTransformerUtils extends AbstractFhirToDhisTransformerUtils { - protected static final String GEO_LOCATION_URL = "http://hl7.org/fhir/StructureDefinition/geolocation"; + protected static final String GEO_LOCATION_URI = "http://hl7.org/fhir/StructureDefinition/geolocation"; protected static final String LATITUDE_URL = "latitude"; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractIdentifierFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractIdentifierFhirToDhisTransformerUtils.java index 349dea11..5db76f67 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractIdentifierFhirToDhisTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractIdentifierFhirToDhisTransformerUtils.java @@ -56,12 +56,12 @@ public abstract class AbstractIdentifierFhirToDhisTransformerUtils extends Abstr private volatile Map, Method> identifierMethods = new HashMap<>(); - private final ReferenceTransformUtils referenceTransformUtils; + private final ReferenceFhirToDhisTransformerUtils referenceFhirToDhisTransformerUtils; - protected AbstractIdentifierFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ReferenceTransformUtils referenceTransformUtils ) + protected AbstractIdentifierFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ReferenceFhirToDhisTransformerUtils referenceFhirToDhisTransformerUtils ) { super( scriptExecutionContext ); - this.referenceTransformUtils = referenceTransformUtils; + this.referenceFhirToDhisTransformerUtils = referenceFhirToDhisTransformerUtils; } @Nonnull @@ -82,7 +82,7 @@ public String getReferenceIdentifier( @Nullable IBaseReference reference, @Nonnu { return null; } - referenceTransformUtils.initReference( reference ); + referenceFhirToDhisTransformerUtils.initReference( reference, fhirResourceType ); if ( reference.getResource() instanceof IDomainResource ) { return getResourceIdentifier( (IDomainResource) reference.getResource(), fhirResourceType ); @@ -98,7 +98,7 @@ public String getReferenceIdentifier( @Nullable IBaseReference reference, @Nonnu { return null; } - referenceTransformUtils.initReference( reference ); + referenceFhirToDhisTransformerUtils.initReference( reference, fhirResourceType ); if ( reference.getResource() instanceof IDomainResource ) { return getResourceIdentifier( (IDomainResource) reference.getResource(), fhirResourceType, system ); diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractObservationTransformUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractObservationFhirToDhisTransformerUtils.java similarity index 88% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractObservationTransformUtils.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractObservationFhirToDhisTransformerUtils.java index f5324624..685511c5 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractObservationTransformUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractObservationFhirToDhisTransformerUtils.java @@ -40,18 +40,18 @@ import java.util.List; @Scriptable -public abstract class AbstractObservationTransformUtils extends AbstractFhirToDhisTransformerUtils +public abstract class AbstractObservationFhirToDhisTransformerUtils extends AbstractFhirToDhisTransformerUtils { private static final String SCRIPT_ATTR_NAME = "observationUtils"; public static final String COMPONENT_SEPARATOR = "\n"; - private final AbstractFhirClientTransformUtils clientTransformUtils; + private final AbstractFhirClientFhirToDhisTransformerUtils clientTransformUtils; private final AbstractCodeFhirToDhisTransformerUtils codeTransformerUtils; - protected AbstractObservationTransformUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, - @Nonnull AbstractFhirClientTransformUtils clientTransformUtils, @Nonnull AbstractCodeFhirToDhisTransformerUtils codeTransformerUtils ) + protected AbstractObservationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, + @Nonnull AbstractFhirClientFhirToDhisTransformerUtils clientTransformUtils, @Nonnull AbstractCodeFhirToDhisTransformerUtils codeTransformerUtils ) { super( scriptExecutionContext ); this.clientTransformUtils = clientTransformUtils; diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationFhirToDhisTransformerUtils.java similarity index 85% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationTransformerUtils.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationFhirToDhisTransformerUtils.java index a9cafd81..fdeb1529 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationTransformerUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractOrganizationFhirToDhisTransformerUtils.java @@ -55,7 +55,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; /** @@ -63,7 +66,7 @@ * * @author volsch */ -public abstract class AbstractOrganizationTransformerUtils extends AbstractFhirToDhisTransformerUtils +public abstract class AbstractOrganizationFhirToDhisTransformerUtils extends AbstractFhirToDhisTransformerUtils { private static final String SCRIPT_ATTR_NAME = "organizationUtils"; @@ -77,7 +80,7 @@ public abstract class AbstractOrganizationTransformerUtils extends AbstractFhirT private final RemoteHierarchicallyFhirRepository remoteHierarchicallyFhirRepository; - public AbstractOrganizationTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, + public AbstractOrganizationFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull OrganisationUnitService organisationUnitService, @Nonnull RemoteSubscriptionResourceRepository subscriptionResourceRepository, @Nonnull RemoteFhirRepository remoteFhirRepository, @@ -136,14 +139,43 @@ public String existsWithPrefix( @Nullable String code ) @Nullable public List findHierarchy( @Nullable IBaseReference childReference ) { - if ( (childReference == null) || childReference.isEmpty() ) + return findHierarchy( childReference, new HashSet<>() ); + } + + @Nullable + protected List findHierarchy( @Nullable IBaseReference childReference, @Nonnull Set processedResources ) + { + if ( childReference == null ) { return null; } + if ( childReference.getResource() != null ) + { + if ( !processedResources.add( childReference.getResource() ) ) + { + // loop within hierarchy + return null; + } + + final List foundResources = new ArrayList<>(); + foundResources.add( childReference.getResource() ); + final List remainingResources = findHierarchy( + getParentReference( childReference.getResource() ), processedResources ); + if ( remainingResources != null ) + { + foundResources.addAll( remainingResources ); + } + return foundResources; + } + + if ( childReference.isEmpty() ) + { + return null; + } if ( childReference.getReferenceElement().isLocal() ) { - throw new TransformerDataException( "Reference element refers to an embedded resource, but no resource is specified: " + childReference.getReferenceElement() ); + throw new TransformerDataException( "Reference element refers to a contained resource, but no resource is specified: " + childReference.getReferenceElement() ); } if ( childReference.getReferenceElement().hasResourceType() && !FhirResourceType.ORGANIZATION.getResourceTypeName().equals( childReference.getReferenceElement().getResourceType() ) ) { diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractPatientFhirToDhisTransformerUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractPatientFhirToDhisTransformerUtils.java new file mode 100644 index 00000000..d9d23dac --- /dev/null +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/AbstractPatientFhirToDhisTransformerUtils.java @@ -0,0 +1,60 @@ +package org.dhis2.fhir.adapter.fhir.transform.impl.util; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.dhis2.fhir.adapter.Scriptable; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IDomainResource; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +@Scriptable +public abstract class AbstractPatientFhirToDhisTransformerUtils extends AbstractFhirToDhisTransformerUtils +{ + protected static final String BIRTH_PLACE_URI = "http://hl7.org/fhir/StructureDefinition/birthPlace"; + + private static final String SCRIPT_ATTR_NAME = "patientUtils"; + + protected AbstractPatientFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext ) + { + super( scriptExecutionContext ); + } + + @Nonnull + @Override + public final String getScriptAttrName() + { + return SCRIPT_ATTR_NAME; + } + + @Nullable + public abstract ICompositeType getBirthPlaceAddress( @Nullable IDomainResource patient ); +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/ReferenceTransformUtils.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/ReferenceFhirToDhisTransformerUtils.java similarity index 71% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/ReferenceTransformUtils.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/ReferenceFhirToDhisTransformerUtils.java index fb5732db..5fba72ed 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/ReferenceTransformUtils.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/impl/util/ReferenceFhirToDhisTransformerUtils.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.context.FhirContext; import org.dhis2.fhir.adapter.Scriptable; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirResourceType; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscription; import org.dhis2.fhir.adapter.fhir.metadata.model.RemoteSubscriptionResource; import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; @@ -41,6 +42,7 @@ import org.dhis2.fhir.adapter.fhir.transform.FhirToDhisTransformerContext; import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.TransformerMappingException; +import org.dhis2.fhir.adapter.fhir.transform.impl.TransformerScriptException; import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -60,7 +62,7 @@ */ @Component @Scriptable -public class ReferenceTransformUtils extends AbstractFhirToDhisTransformerUtils +public class ReferenceFhirToDhisTransformerUtils extends AbstractFhirToDhisTransformerUtils { private static final String SCRIPT_ATTR_NAME = "referenceUtils"; @@ -70,7 +72,7 @@ public class ReferenceTransformUtils extends AbstractFhirToDhisTransformerUtils private final RemoteFhirRepository remoteFhirRepository; - public ReferenceTransformUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, + public ReferenceFhirToDhisTransformerUtils( @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull RemoteSubscriptionResourceRepository subscriptionResourceRepository, @Nonnull RemoteFhirRepository remoteFhirRepository ) { @@ -93,44 +95,83 @@ public Set getFhirVersions() return FhirVersion.ALL; } - public void initReference( @Nullable IBaseReference reference ) + public void initReference( @Nullable IBaseReference reference, @Nullable Object resourceType ) { - initReference( reference, false ); + initReference( reference, resourceType, false ); } - public void initReference( @Nullable IBaseReference reference, boolean refreshed ) + public void initReference( @Nullable IBaseReference reference, @Nullable Object resourceType, boolean refreshed ) { - getResource( reference, refreshed ); + getResource( reference, resourceType, refreshed ); } @Nullable - public IBaseResource getResource( @Nullable IBaseReference reference ) + public IBaseResource getResource( @Nullable IBaseReference reference, @Nullable Object resourceType ) { - return getResource( reference, false ); + return getResource( reference, resourceType, false ); } @Nullable - public IBaseResource getResource( @Nullable IBaseReference reference, boolean refreshed ) + public IBaseResource getResource( @Nullable IBaseReference reference, @Nullable Object resourceType, boolean refreshed ) { if ( reference == null ) { return null; } + + final FhirResourceType fhirResourceType; + if ( resourceType == null ) + { + fhirResourceType = null; + } + else + { + try + { + fhirResourceType = FhirResourceType.valueOf( resourceType.toString() ); + } + catch ( IllegalArgumentException e ) + { + throw new TransformerScriptException( "Invalid FHIR resource type: " + resourceType, e ); + } + } + if ( reference.getResource() != null ) { + if ( (fhirResourceType != null) && (FhirResourceType.getByResource( reference.getResource() ) != fhirResourceType) ) + { + logger.debug( "The referenced resource ID contains resource type {}, but requested resource type is {}.", + FhirResourceType.getByResource( reference.getResource() ), fhirResourceType ); + return null; + } + // also handles the case of a local reference return reference.getResource(); } + if ( reference.isEmpty() ) { return null; } if ( reference.getReferenceElement().isLocal() ) { - throw new TransformerDataException( "Reference element refers to an embedded resource, but no resource is specified: " + reference.getReferenceElement() ); + throw new TransformerDataException( "Reference element refers to a contained resource, but no resource is specified: " + reference.getReferenceElement() ); } - if ( !reference.getReferenceElement().hasResourceType() ) + + String finalResourceType = reference.getReferenceElement().getResourceType(); + if ( fhirResourceType != null ) + { + if ( (finalResourceType != null) && !finalResourceType.equals( fhirResourceType.getResourceTypeName() ) ) + { + logger.debug( "The referenced resource ID contains resource type {}, but requested resource type is {}.", + finalResourceType, fhirResourceType ); + return null; + } + finalResourceType = fhirResourceType.getResourceTypeName(); + } + + if ( finalResourceType == null ) { - throw new TransformerDataException( "Reference element does not include a resource type: " + reference.getReferenceElement() ); + throw new TransformerDataException( "Final resource type could not be determined for reference: " + reference.getReferenceElement() ); } if ( !reference.getReferenceElement().hasIdPart() ) { @@ -151,9 +192,9 @@ public IBaseResource getResource( @Nullable IBaseReference reference, boolean re final RemoteSubscription remoteSubscription = subscriptionResource.getRemoteSubscription(); final Optional optionalResource = refreshed ? remoteFhirRepository.findRefreshed( remoteSubscription.getId(), remoteSubscription.getFhirVersion(), remoteSubscription.getFhirEndpoint(), - reference.getReferenceElement().getResourceType(), reference.getReferenceElement().getIdPart() ) : + finalResourceType, reference.getReferenceElement().getIdPart() ) : remoteFhirRepository.find( remoteSubscription.getId(), remoteSubscription.getFhirVersion(), remoteSubscription.getFhirEndpoint(), - reference.getReferenceElement().getResourceType(), reference.getReferenceElement().getIdPart() ); + finalResourceType, reference.getReferenceElement().getIdPart() ); final IBaseResource resource = optionalResource.map( r -> BeanTransformerUtils.clone( fhirContext, r ) ) .orElseThrow( () -> new TransformerDataException( "Referenced FHIR resource " + reference.getReferenceElement() + " does not exist for remote subscription resource " + resourceId ) ); reference.setResource( resource ); diff --git a/fhir/src/main/resources/db/migration/production/V1.0.0.0_0_0__Initial.sql b/fhir/src/main/resources/db/migration/production/V1.0.0.0_0_0__Initial.sql index 08386ecd..91ec88f9 100644 --- a/fhir/src/main/resources/db/migration/production/V1.0.0.0_0_0__Initial.sql +++ b/fhir/src/main/resources/db/migration/production/V1.0.0.0_0_0__Initial.sql @@ -1,7 +1,7 @@ /* * Copyright (c) 2004-2018, University of Oslo * All rights reserved. - * + *t * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this @@ -42,6 +42,7 @@ INSERT INTO fhir_data_type_enum VALUES('DOUBLE'); INSERT INTO fhir_data_type_enum VALUES('DATE_TIME'); INSERT INTO fhir_data_type_enum VALUES('DATE_UNIT'); INSERT INTO fhir_data_type_enum VALUES('WEIGHT_UNIT'); +INSERT INTO fhir_data_type_enum VALUES('GENDER'); INSERT INTO fhir_data_type_enum VALUES('CONSTANT'); INSERT INTO fhir_data_type_enum VALUES('CODE'); INSERT INTO fhir_data_type_enum VALUES('LOCATION'); @@ -53,6 +54,7 @@ INSERT INTO fhir_data_type_enum VALUES('DATA_ELEMENT_REF'); INSERT INTO fhir_data_type_enum VALUES('PROGRAM_REF'); INSERT INTO fhir_data_type_enum VALUES('PROGRAM_STAGE_REF'); INSERT INTO fhir_data_type_enum VALUES('FHIR_RESOURCE'); +INSERT INTO fhir_data_type_enum VALUES('FHIR_RESOURCE_LIST'); INSERT INTO fhir_data_type_enum VALUES('EVENT_DECISION_TYPE'); CREATE TABLE fhir_transform_data_type_enum ( @@ -67,6 +69,7 @@ INSERT INTO fhir_transform_data_type_enum VALUES('FHIR_PATIENT'); INSERT INTO fhir_transform_data_type_enum VALUES('FHIR_IMMUNIZATION'); INSERT INTO fhir_transform_data_type_enum VALUES('FHIR_OBSERVATION'); INSERT INTO fhir_transform_data_type_enum VALUES('FHIR_DIAGNOSTIC_REPORT'); +INSERT INTO fhir_transform_data_type_enum VALUES('FHIR_RELATED_PERSON'); CREATE TABLE fhir_resource_type_enum ( value VARCHAR(30) NOT NULL, @@ -80,6 +83,7 @@ INSERT INTO fhir_resource_type_enum VALUES('MEDICATION_REQUEST'); INSERT INTO fhir_resource_type_enum VALUES('OBSERVATION'); INSERT INTO fhir_resource_type_enum VALUES('ORGANIZATION'); INSERT INTO fhir_resource_type_enum VALUES('PATIENT'); +INSERT INTO fhir_resource_type_enum VALUES('RELATED_PERSON'); CREATE TABLE fhir_dhis_resource_type_enum ( value VARCHAR(30) NOT NULL, @@ -525,20 +529,21 @@ COMMENT ON COLUMN fhir_system_code.code_id IS 'References the adapter internal c COMMENT ON COLUMN fhir_system_code.system_code_value IS 'Combination of system URI and code separated by a pipe character. This is mainly used for search purpose.'; CREATE TABLE fhir_rule ( - id UUID NOT NULL DEFAULT UUID_GENERATE_V4(), - version BIGINT NOT NULL, - created_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'), - last_updated_by VARCHAR(11), - last_updated_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'), - name VARCHAR(230) NOT NULL, - description TEXT, - enabled BOOLEAN NOT NULL DEFAULT TRUE, - evaluation_order INTEGER NOT NULL DEFAULT 0, - fhir_resource_type VARCHAR(30) NOT NULL, - dhis_resource_type VARCHAR(30) NOT NULL, - applicable_in_script_id UUID, - applicable_code_set_id UUID, - transform_in_script_id UUID NOT NULL, + id UUID NOT NULL DEFAULT UUID_GENERATE_V4(), + version BIGINT NOT NULL, + created_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'), + last_updated_by VARCHAR(11), + last_updated_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'), + name VARCHAR(230) NOT NULL, + description TEXT, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + evaluation_order INTEGER NOT NULL DEFAULT 0, + fhir_resource_type VARCHAR(30) NOT NULL, + dhis_resource_type VARCHAR(30) NOT NULL, + applicable_in_script_id UUID, + applicable_code_set_id UUID, + transform_in_script_id UUID NOT NULL, + contained_allowed BOOLEAN NOT NULL DEFAULT FALSE, CONSTRAINT fhir_rule_pk PRIMARY KEY (id), CONSTRAINT fhir_rule_uk_name UNIQUE (name), CONSTRAINT fhir_rule_fk1 FOREIGN KEY (applicable_in_script_id) REFERENCES fhir_executable_script (id), @@ -568,28 +573,63 @@ COMMENT ON COLUMN fhir_rule.dhis_resource_type IS 'The resulting DHIS2 Resource COMMENT ON COLUMN fhir_rule.applicable_in_script_id IS 'References the evaluation script that is used to evaluate if the input is applicable to be processed by this rule. If no script has been specified, the rule is applicable for further processing. The script will not be executed if the code set with applicable codes do not match.'; COMMENT ON COLUMN fhir_rule.applicable_code_set_id IS 'References the code set that is used to evaluate if the input is applicable to be processed by this rule. If no code included in the input matches the code of the specified code set, the rule will not be applicable. If no code set is specified, the applicable set (if any) will be used to evaluate if the rule is applicable for further processing.'; COMMENT ON COLUMN fhir_rule.transform_in_script_id IS 'References the transformation script that is used to transform the input to the DHIS2 Resource.'; +COMMENT ON COLUMN fhir_rule.contained_allowed IS 'Specified if this rule can process contained resources.'; + +CREATE TABLE fhir_tracked_entity ( + id UUID NOT NULL DEFAULT UUID_GENERATE_V4(), + version BIGINT NOT NULL, + created_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'), + last_updated_by VARCHAR(11), + last_updated_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'), + name VARCHAR(230) NOT NULL, + description TEXT, + enabled BOOLEAN NOT NULL DEFAULT TRUE, + tracked_entity_ref VARCHAR(230) NOT NULL, + tracked_entity_identifier_ref VARCHAR(230) NOT NULL, + CONSTRAINT fhir_tracked_entity_pk PRIMARY KEY (id), + CONSTRAINT fhir_tracked_entity_uk_name UNIQUE (name), + CONSTRAINT fhir_tracked_entity_uk_ref UNIQUE (tracked_entity_ref) +); +COMMENT ON TABLE fhir_tracked_entity IS 'Contains the configuration for DHIS2 Tracked Entity Resource Types.'; +COMMENT ON COLUMN fhir_tracked_entity.id IS 'References the rule to which this tracked entity rule belongs to.'; +COMMENT ON COLUMN fhir_tracked_entity.id IS 'Unique ID of entity.'; +COMMENT ON COLUMN fhir_tracked_entity.version IS 'The version of the entity used for optimistic locking. When changing the entity this value must be incremented.'; +COMMENT ON COLUMN fhir_tracked_entity.created_at IS 'The timestamp when the entity has been created.'; +COMMENT ON COLUMN fhir_tracked_entity.last_updated_by IS 'The ID of the user that has updated the entity the last time or NULL if the user is not known or the entity has been created with initial database setup.'; +COMMENT ON COLUMN fhir_tracked_entity.last_updated_at IS 'The timestamp when the entity has been updated the last time. When changing the entity this value must be updated to the current timestamp.'; +COMMENT ON COLUMN fhir_tracked_entity.name IS 'The unique name of the rule.'; +COMMENT ON COLUMN fhir_tracked_entity.description IS 'The detailed description about the purpose of the rule.'; +COMMENT ON COLUMN fhir_tracked_entity.enabled IS 'Specifies if the rule has been enabled and is used for transformations.'; +COMMENT ON COLUMN fhir_tracked_entity.tracked_entity_ref IS 'The reference of the DHIS2 Tracked Entity Type (e.g. "NAME:Person").'; +COMMENT ON COLUMN fhir_tracked_entity.tracked_entity_identifier_ref IS 'The reference of the DHIS2 Tracked Entity Attribute that includes the identifier value that is also used by FHIR resources (e.g. NAME:National identifier).'; CREATE TABLE fhir_tracked_entity_rule ( id UUID NOT NULL, - tracked_entity_ref VARCHAR(230) NOT NULL, - org_lookup_script_id UUID NOT NULL, - loc_lookup_script_id UUID NOT NULL, - tracked_entity_identifier_ref VARCHAR(230) NOT NULL, + tracked_entity_id UUID NOT NULL, + org_lookup_script_id UUID, + loc_lookup_script_id UUID, + tei_lookup_script_id UUID, CONSTRAINT fhir_tracked_entity_rule_pk PRIMARY KEY (id), CONSTRAINT fhir_tracked_entity_rule_fk1 FOREIGN KEY (id) REFERENCES fhir_rule (id) ON DELETE CASCADE, - CONSTRAINT fhir_tracked_entity_rule_fk2 FOREIGN KEY (org_lookup_script_id) REFERENCES fhir_executable_script (id), - CONSTRAINT fhir_tracked_entity_rule_fk3 FOREIGN KEY (loc_lookup_script_id) REFERENCES fhir_executable_script (id) + CONSTRAINT fhir_tracked_entity_rule_fk2 FOREIGN KEY (tracked_entity_id) REFERENCES fhir_tracked_entity (id), + CONSTRAINT fhir_tracked_entity_rule_fk4 FOREIGN KEY (org_lookup_script_id) REFERENCES fhir_executable_script (id), + CONSTRAINT fhir_tracked_entity_rule_fk5 FOREIGN KEY (loc_lookup_script_id) REFERENCES fhir_executable_script (id), + CONSTRAINT fhir_tracked_entity_rule_fk6 FOREIGN KEY (tei_lookup_script_id) REFERENCES fhir_executable_script (id) ); CREATE INDEX fhir_tracked_entity_rule_i1 - ON fhir_tracked_entity_rule (org_lookup_script_id); + ON fhir_tracked_entity_rule (tracked_entity_id); CREATE INDEX fhir_tracked_entity_rule_i2 + ON fhir_tracked_entity_rule (org_lookup_script_id); +CREATE INDEX fhir_tracked_entity_rule_i3 ON fhir_tracked_entity_rule (loc_lookup_script_id); +CREATE INDEX fhir_tracked_entity_rule_i4 + ON fhir_tracked_entity_rule (tei_lookup_script_id); COMMENT ON TABLE fhir_tracked_entity_rule IS 'Contains rules for DHIS2 Tracked Entity Resource Types.'; COMMENT ON COLUMN fhir_tracked_entity_rule.id IS 'References the rule to which this tracked entity rule belongs to.'; -COMMENT ON COLUMN fhir_tracked_entity_rule.tracked_entity_ref IS 'The reference of the DHIS2 Tracked Entity Type (e.g. "NAME:Person").'; +COMMENT ON COLUMN fhir_tracked_entity_rule.tracked_entity_id IS 'References the DHIS2 Tracked Entity Type.'; COMMENT ON COLUMN fhir_tracked_entity_rule.org_lookup_script_id IS 'References the executable lookup script for DHIS2 Organization Units.'; COMMENT ON COLUMN fhir_tracked_entity_rule.loc_lookup_script_id IS 'References the executable lookup script for DHIS2 coordinates (longitude and latitude).'; -COMMENT ON COLUMN fhir_tracked_entity_rule.tracked_entity_identifier_ref IS 'The reference of the DHIS2 Tracked Entity Attribute that includes the identifier value that is also used by FHIR resources (e.g. NAME:National identifier).'; +COMMENT ON COLUMN fhir_tracked_entity_rule.tei_lookup_script_id IS 'References the executable lookup script that resolves the FHIR Resource that belongs to the tracked entity.'; CREATE TABLE fhir_tracker_program ( id UUID NOT NULL DEFAULT UUID_GENERATE_V4(), @@ -929,6 +969,7 @@ CREATE TABLE fhir_remote_subscription_resource ( fhir_resource_type VARCHAR(30) NOT NULL, fhir_criteria_parameters VARCHAR(200), description TEXT, + virtual BOOLEAN NOT NULL DEFAULT FALSE, fhir_subscription_id VARCHAR(100), CONSTRAINT fhir_remote_subscription_resource_pk PRIMARY KEY (id), -- do not enable cascading dequeued since remote last update date may get lost by mistake @@ -948,6 +989,7 @@ COMMENT ON COLUMN fhir_remote_subscription_resource.fhir_resource_type IS 'The F COMMENT ON COLUMN fhir_remote_subscription_resource.fhir_criteria_parameters IS 'The parameters that have been appended to the resource type when creation the subscription on the remote FHIR Service.'; COMMENT ON COLUMN fhir_remote_subscription_resource.description IS 'The detailed purpose of subscribing this resource with the specified criteria parameters.'; COMMENT ON COLUMN fhir_remote_subscription_resource.fhir_subscription_id IS 'The ID of the subscription that has been set up on the remote FHIR service or unset if no subscription has been set up automatically.'; +COMMENT ON COLUMN fhir_remote_subscription_resource.virtual IS 'Specifies that there is no subscription for this FHIR resource since the FHIR service may not accept subscription for this resource type (just available as contained resources).'; CREATE TABLE fhir_remote_subscription_resource_update ( id UUID NOT NULL, @@ -1018,8 +1060,14 @@ INSERT INTO fhir_system (id, version, name, code, system_uri, description) VALUES ('20f6d869-a767-461e-8c68-aa46a76ec5c4', 0, 'SNOMED CT', 'SYSTEM_SCT', 'http://snomed.info/sct', 'SNOMED CT can be used to represent clinically relevant information consistently, reliably and comprehensively as an integral part of producing electronic health information.'); INSERT INTO fhir_system (id, version, name, code, system_uri, description) -VALUES ('02d5719e-8859-445a-a815-5980d418b4e6', 0, 'RxNorm', 'SYSTEM_RXNORM', ' http://www.nlm.nih.gov/research/umls/rxnorm', +VALUES ('02d5719e-8859-445a-a815-5980d418b4e6', 0, 'RxNorm', 'SYSTEM_RXNORM', 'http://www.nlm.nih.gov/research/umls/rxnorm', 'RxNorm is made available by the US National Library of Medicine at http://www.nlm.nih.gov/research/umls/rxnorm.'); +INSERT INTO fhir_system (id, version, name, code, system_uri, description) +VALUES ('3ffa1fe5-8ded-4f18-afa6-d847d2b94217', 0, 'FHIR Role Code V2', 'SYSTEM_FHIR_ROLE_CODE_V2', 'http://hl7.org/fhir/v2/0131', +'FHIR personal relationship role type V2.'); +INSERT INTO fhir_system (id, version, name, code, system_uri, description) +VALUES ('64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 0, 'FHIR Role Code V3', 'SYSTEM_FHIR_ROLE_CODE_V3', 'http://hl7.org/fhir/v3/RoleCode', +'FHIR personal relationship role type V3.'); INSERT INTO fhir_code_category (id, version, name, code, description) VALUES ('9e1c64d8-82df-40e5-927b-a34c115f14e9', 0, 'Organization Unit', 'ORGANIZATION_UNIT', 'Organization units that exists on DHIS2.'); @@ -1028,6 +1076,236 @@ VALUES ('1197b27e-3956-43dd-a75c-bfc6808dc49d', 0, 'Vital Sign', 'VITAL_SIGN', ' INSERT INTO fhir_code_category (id, version, name, code, description) VALUES ('616e470e-fe84-46ac-a523-b23bbc526ae2', 0, 'Survey', 'SURVEY', 'Survey.'); +INSERT INTO fhir_code_category (id, version, name, code, description) +VALUES ('91065580-f4fa-4135-969a-498e9518c0c8', 0, 'Role Code', 'ROLE_CODE', 'Personal relationship role type.'); + +--------------------- +-- Relationship Codes +--------------------- +INSERT INTO fhir_code(id, version, code_category_id, name, code) VALUES ('aa36eeb6-33f1-4637-936a-c934abdaf83f', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC Contact Person', 'RC_CP'); +INSERT INTO fhir_code(id, version, code_category_id, name, code) VALUES ('774ee335-a703-4753-b524-da49e4f10454', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC Emergency Contact', 'RC_C'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('4611966d-6eea-4277-bfce-a515ac879ef7', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC family member', 'RC_FAMMEMB', 'A relationship between two people characterizing their "familial" relationship'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('8ac10593-daeb-4885-ade6-abbd89d1d089', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC child', 'RC_CHILD', 'The player of the role is a child of the scoping entity.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('e0cf031a-9717-48c7-8f64-dd21a2dea323', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC adopted child', 'RC_CHLDADOPT', 'The player of the role is a child taken into a family through legal means and raised by the scoping person (parent) as his or her own child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('0836ecaa-413f-450f-9222-c648dfbe2768', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC adopted daughter', 'RC_DAUADOPT', 'The player of the role is a female child taken into a family through legal means and raised by the scoping person (parent) as his or her own child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('2ea8b30d-b468-4f03-a3fb-26cdc1531022', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC adopted son', 'RC_SONADOPT', 'The player of the role is a male child taken into a family through legal means and raised by the scoping person (parent) as his or her own child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('27521b84-cdb4-4ee0-ae4e-b2d6c982fbb0', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC foster child', 'RC_CHLDFOST', 'The player of the role is a child receiving parental care and nurture from the scoping person (parent) but not related to him or her through legal or blood ties.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('5b52db2e-9b04-464e-a6d6-3b4e1bf8760e', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC foster daughter', 'RC_DAUFOST', 'The player of the role is a female child receiving parental care and nurture from the scoping person (parent) but not related to him or her through legal or blood ties.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('033f40ad-54d5-49ad-991b-5d6f52e42dd8', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC foster son', 'RC_SONFOST', 'The player of the role is a male child receiving parental care and nurture from the scoping person (parent) but not related to him or her through legal or blood ties.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('26a2a332-9b52-4263-83fa-b4cf4e377487', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC daughter', 'RC_DAUC', 'Description: The player of the role is a female child (of any type) of scoping entity (parent)'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('2e161538-99d3-49f1-b83a-1569d7652fc7', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural daughter', 'RC_DAU', 'The player of the role is a female offspring of the scoping entity (parent).'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('f8cf5e93-551b-4da5-b4f1-e6773807faaf', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC stepdaughter', 'RC_STPDAU', 'The player of the role is a daughter of the scoping person''s spouse by a previous union.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('f1891d46-cd36-44af-a349-b7f542bb4e41', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural child', 'RC_NCHILD', 'The player of the role is an offspring of the scoping entity as determined by birth.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('81f449f6-b875-403f-96b1-7d5fe6b16152', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural son', 'RC_SON', 'The player of the role is a male offspring of the scoping entity (parent).'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('6b262cf2-7738-463f-8a7c-6b8ef4502871', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC son', 'RC_SONC', 'Description: The player of the role is a male child (of any type) of scoping entity (parent)'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('127b6fec-2887-4635-9ed7-cd46c88f0193', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC stepson', 'RC_STPSON', 'The player of the role is a son of the scoping person''s spouse by a previous union.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('fe61a98b-337a-459f-aab6-d8950f73c307', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC step child', 'RC_STPCHLD', 'The player of the role is a child of the scoping person''s spouse by a previous union.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('0337c32e-01b2-4806-a893-fe8856b9f1b5', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC extended family member', 'RC_EXT', 'Description: A family member not having an immediate genetic or legal relationship e.g. Aunt, cousin, great grandparent, grandchild, grandparent, niece, nephew or uncle.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('89b92e82-15da-4d67-8f54-9ee311f52a2a', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC aunt', 'RC_AUNT', 'The player of the role is a sister of the scoping person''s mother or father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('a047f7b3-8f44-4044-84ad-5d69ec14a5e4', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal aunt', 'RC_MAUNT', 'Description:The player of the role is a biological sister of the scoping person''s biological mother.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('92b81681-0d2a-45fb-b06e-bde340130a19', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal aunt', 'RC_PAUNT', 'Description:The player of the role is a biological sister of the scoping person''s biological father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('f88411a0-5dd6-43cc-b992-985be76c8908', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC cousin', 'RC_COUSN', 'The player of the role is a relative of the scoping person descended from a common ancestor, such as a grandparent, by two or more steps in a diverging line.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('ccba35d9-678b-49c0-8dc6-aa5908bb373d', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal cousin', 'RC_MCOUSN', 'Description:The player of the role is a biological relative of the scoping person descended from a common ancestor on the player''s mother''s side, such as a grandparent, by two or more steps in a diverging line.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('b41b75cc-e50b-480a-aefb-46ccb036fa5c', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal cousin', 'RC_PCOUSN', 'Description:The player of the role is a biological relative of the scoping person descended from a common ancestor on the player''s father''s side, such as a grandparent, by two or more steps in a diverging line.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('f2bc75a7-c0c1-4ee0-898e-e632d9c11764', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC great grandparent', 'RC_GGRPRN', 'The player of the role is a parent of the scoping person''s grandparent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('d5604d99-1664-4fd5-99ad-8cd093db8d02', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC great grandfather', 'RC_GGRFTH', 'The player of the role is the father of the scoping person''s grandparent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('fbffd3e0-07a0-49a5-a858-46c0d5d3cfec', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal great-grandfather', 'RC_MGGRFTH', 'Description:The player of the role is the biological father of the scoping person''s biological mother''s parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('d98f5389-5240-4648-955f-dbc1f74affd8', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal great-grandfather', 'RC_PGGRFTH', 'Description:The player of the role is the biological father of the scoping person''s biological father''s parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('afdc0aa7-a2fb-4829-9c6c-a248262087f5', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC great grandmother', 'RC_GGRMTH', 'The player of the role is the mother of the scoping person''s grandparent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('574b70d7-5a80-49d8-bbb8-8857d4ba7546', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal great-grandmother', 'RC_MGGRMTH', 'Description:The player of the role is the biological mother of the scoping person''s biological mother''s parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('87635859-f1b3-4d3d-ba8c-ff66f05d3a47', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal great-grandmother', 'RC_PGGRMTH', 'Description:The player of the role is the biological mother of the scoping person''s biological father''s parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('2ded7f92-9cf4-4a58-9cec-3dd8a74ee011', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal great-grandparent', 'RC_MGGRPRN', 'Description:The player of the role is a biological parent of the scoping person''s biological mother''s parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('1cb40384-22ff-4c9e-9ab4-a733e867de6b', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal great-grandparent', 'RC_PGGRPRN', 'Description:The player of the role is a biological parent of the scoping person''s biological father''s parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('970efa00-62b2-44d9-b08d-725910450f43', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC grandchild', 'RC_GRNDCHILD', 'The player of the role is a child of the scoping person''s son or daughter.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('5a47cf78-7a74-49b1-b549-af72e442e01d', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC granddaughter', 'RC_GRNDDAU', 'The player of the role is a daughter of the scoping person''s son or daughter.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('10a8a5e9-79d5-4dcb-857c-3092cedd5591', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC grandson', 'RC_GRNDSON', 'The player of the role is a son of the scoping person''s son or daughter.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('6052f4db-8354-4c78-be2a-15fd3c89a7bd', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC grandparent', 'RC_GRPRN', 'The player of the role is a parent of the scoping person''s mother or father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('21d150c4-930e-442f-b964-b34ede4fb348', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC grandfather', 'RC_GRFTH', 'The player of the role is the father of the scoping person''s mother or father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('f17753a8-298d-49b7-8e37-ccfa15060157', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal grandfather', 'RC_MGRFTH', 'Description:The player of the role is the biological father of the scoping person''s biological mother.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('3100a18c-00a8-4aad-926b-6ebeaab4b4a1', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal grandfather', 'RC_PGRFTH', 'Description:The player of the role is the biological father of the scoping person''s biological father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('9d95989b-d071-4eb3-9ac6-d3f691bafee1', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC grandmother', 'RC_GRMTH', 'The player of the role is the mother of the scoping person''s mother or father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('ec36bc5c-acf5-4ef1-b659-1ac78ee968da', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal grandmother', 'RC_MGRMTH', 'Description:The player of the role is the biological mother of the scoping person''s biological mother.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('0bbece14-c188-4d5b-91b0-8999291fefc7', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal grandmother', 'RC_PGRMTH', 'Description:The player of the role is the biological mother of the scoping person''s biological father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('e8e3924f-c2e8-45a6-9400-3727b37d7f98', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal grandparent', 'RC_MGRPRN', 'Description:The player of the role is the biological parent of the scoping person''s biological mother.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('3e3184f0-6e90-47c2-890b-b8a284e000d5', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal grandparent', 'RC_PGRPRN', 'Description:The player of the role is the biological parent of the scoping person''s biological father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('5ea797ff-ca67-4ddb-baf2-08964ca26e34', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC inlaw', 'RC_INLAW', 'A relationship between an individual and a member of their spousal partner''s immediate family.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('1e6f1da9-15e8-4512-b502-6699b7ea7c34', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC child-in-law', 'RC_CHLDINLAW', 'The player of the role is the spouse of scoping person''s child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('fcff1dc5-b860-4e8d-bee1-cfac40b33944', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC daughter in-law', 'RC_DAUINLAW', 'The player of the role is the wife of scoping person''s son.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('a8c0f886-aae3-479e-8aa3-d8e37f31b9be', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC son in-law', 'RC_SONINLAW', 'The player of the role is the husband of scoping person''s daughter.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('41d6001a-9fc9-4337-bbb6-ce1ff284e115', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC parent in-law', 'RC_PRNINLAW', 'The player of the role is the parent of scoping person''s husband or wife.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('140d4897-6355-4be8-9947-06e0ca12b1ed', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC father-in-law', 'RC_FTHINLAW', 'The player of the role is the father of the scoping person''s husband or wife.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('d5c679c8-292c-4e78-84a3-fcccbf7b4251', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC mother-in-law', 'RC_MTHINLAW', 'The player of the role is the mother of the scoping person''s husband or wife.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('8f8c7f01-9cfa-477a-b6ee-fe595f8284cd', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC sibling in-law', 'RC_SIBINLAW', 'The player of the role is: (1) a sibling of the scoping person''s spouse, or (2) the spouse of the scoping person''s sibling, or (3) the spouse of a sibling of the scoping person''s spouse.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('19d9601f-fc97-4302-9b33-8abd194b2b06', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC brother-in-law', 'RC_BROINLAW', 'The player of the role is: (1) a brother of the scoping person''s spouse, or (2) the husband of the scoping person''s sister, or (3) the husband of a sister of the scoping person''s spouse.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('7612c887-3ae6-4056-800d-3c9af1242e72', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC sister-in-law', 'RC_SISINLAW', 'The player of the role is: (1) a sister of the scoping person''s spouse, or (2) the wife of the scoping person''s brother, or (3) the wife of a brother of the scoping person''s spouse.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('9162f354-4fdc-45ed-808e-41e9335ea325', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC niece/nephew', 'RC_NIENEPH', 'The player of the role is a child of scoping person''s brother or sister or of the brother or sister of the scoping person''s spouse.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('9324244d-8199-41b1-9fb1-3bf3566d5d57', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC nephew', 'RC_NEPHEW', 'The player of the role is a son of the scoping person''s brother or sister or of the brother or sister of the scoping person''s spouse.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('02918b92-51b6-4da0-91b0-b52ddbb5e94f', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC niece', 'RC_NIECE', 'The player of the role is a daughter of the scoping person''s brother or sister or of the brother or sister of the scoping person''s spouse.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('dadccbc8-dfb2-4572-8f19-d07e325fcc7c', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC uncle', 'RC_UNCLE', 'The player of the role is a brother of the scoping person''s mother or father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('d3a4de1f-3d6d-4ab3-bff5-795e7c20a075', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC maternal uncle', 'RC_MUNCLE', 'Description:The player of the role is a biological brother of the scoping person''s biological mother.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('2d037299-1e3d-4276-8d13-1ee73cb43479', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC paternal uncle', 'RC_PUNCLE', 'Description:The player of the role is a biological brother of the scoping person''s biological father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('758fc298-86b1-4ec0-a463-91034a0e35c9', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC parent', 'RC_PRN', 'The player of the role is one who begets, gives birth to, or nurtures and raises the scoping entity (child).'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('77bf1643-3352-40b3-ae63-450b2baa37f6', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC adoptive parent', 'RC_ADOPTP', 'The player of the role (parent) has taken the scoper (child) into their family through legal means and raises them as his or her own child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('a7925456-430d-4c2d-b3b2-7afbc3bbde0f', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC adoptive father', 'RC_ADOPTF', 'The player of the role (father) is a male who has taken the scoper (child) into their family through legal means and raises them as his own child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('ddd1f6d3-e362-44cd-9887-5f7d3daaa7dd', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC adoptive mother', 'RC_ADOPTM', 'The player of the role (father) is a female who has taken the scoper (child) into their family through legal means and raises them as her own child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('5a395a3d-e1be-4b88-a91d-99b1965cfb3b', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC father', 'RC_FTH', 'The player of the role is a male who begets or raises or nurtures the scoping entity (child).'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('7ba01ba3-54e0-4034-8ffd-ccee1454f9f1', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC foster father', 'RC_FTHFOST', 'The player of the role (parent) who is a male state-certified caregiver responsible for the scoper (child) who has been placed in the parent''s care. The placement of the child is usually arranged through the government or a social-service agency, and temporary. The state, via a jurisdiction recognized child protection agency, stands as in loco parentis to the child, making all legal decisions while the foster parent is responsible for the day-to-day care of the specified child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('43cd0421-72e9-449c-a70a-fd50ffaea8c9', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural father', 'RC_NFTH', 'The player of the role is a male who begets the scoping entity (child).'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('cf62ae76-136d-4143-9a17-82ec94d4e0bf', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural father of fetus', 'RC_NFTHF', 'Indicates the biologic male parent of a fetus.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('edb48195-0644-4e15-b4a7-6edfe3c8062c', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC stepfather', 'RC_STPFTH', 'The player of the role is the husband of scoping person''s mother and not the scoping person''s natural father.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('08f80790-dda3-4ec1-939c-5884ac1a82d3', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC mother', 'RC_MTH', 'The player of the role is a female who conceives, gives birth to, or raises and nurtures the scoping entity (child).'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('fcc2d4ce-c441-4542-9f70-ba72f0a3a6df', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC gestational mother', 'RC_GESTM', 'The player is a female whose womb carries the fetus of the scoper. Generally used when the gestational mother and natural mother are not the same.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('08142067-32b2-4b19-a01c-63f336e0726b', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC foster mother', 'RC_MTHFOST', 'The player of the role (parent) who is a female state-certified caregiver responsible for the scoper (child) who has been placed in the parent''s care. The placement of the child is usually arranged through the government or a social-service agency, and temporary. The state, via a jurisdiction recognized child protection agency, stands as in loco parentis to the child, making all legal decisions while the foster parent is responsible for the day-to-day care of the specified child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('8df34897-3048-46c5-8cad-bd9b622b9bbb', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural mother', 'RC_NMTH', 'The player of the role is a female who conceives or gives birth to the scoping entity (child).'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('95f37173-8a35-4148-9cda-a9781bd19f86', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural mother of fetus', 'RC_NMTHF', 'The player is the biologic female parent of the scoping fetus.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('cd3a2e6f-f804-4c89-839b-a42917122ec9', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC stepmother', 'RC_STPMTH', 'The player of the role is the wife of scoping person''s father and not the scoping person''s natural mother.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('81965cc5-984e-4c96-9102-2ad03ffbcbef', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural parent', 'RC_NPRN', 'natural parent'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('e736e6ed-f237-48ec-91ca-60840ea56d60', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC foster parent', 'RC_PRNFOST', 'The player of the role (parent) who is a state-certified caregiver responsible for the scoper (child) who has been placed in the parent''s care. The placement of the child is usually arranged through the government or a social-service agency, and temporary. The state, via a jurisdiction recognized child protection agency, stands as in loco parentis to the child, making all legal decisions while the foster parent is responsible for the day-to-day care of the specified child.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('8457ff34-8a3c-4e83-81b6-2991b2ca4aca', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC step parent', 'RC_STPPRN', 'The player of the role is the spouse of the scoping person''s parent and not the scoping person''s natural parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('315774e5-a881-4d09-b21f-b2640c9ac0e8', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC sibling', 'RC_SIB', 'The player of the role shares one or both parents in common with the scoping entity.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('98082eca-40c5-4d15-9ed4-8ecd844e3ae7', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC brother', 'RC_BRO', 'The player of the role is a male sharing one or both parents in common with the scoping entity.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('48bcbb2b-2b95-4266-a756-f277c81857eb', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC half-brother', 'RC_HBRO', 'The player of the role is a male related to the scoping entity by sharing only one biological parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('f2be630f-1d57-4ae0-a094-b4d4257c33f9', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural brother', 'RC_NBRO', 'The player of the role is a male having the same biological parents as the scoping entity.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('5181d4e6-8c98-4b43-9dfb-fd79a2daac28', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC twin brother', 'RC_TWINBRO', 'The scoper was carried in the same womb as the male player and shares common biological parents.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('f24c3a34-b07a-4397-ae2d-44cef62e1a0a', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC fraternal twin brother', 'RC_FTWINBRO', 'The scoper was carried in the same womb as the male player and shares common biological parents but is the product of a distinct egg/sperm pair.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('39ab3576-1b7f-423e-89a1-75e52f7c9364', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC identical twin brother', 'RC_ITWINBRO', 'The male scoper is an offspring of the same egg-sperm pair as the male player.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('0a56be2e-819c-4ff1-87bf-0cafb9e217d0', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC stepbrother', 'RC_STPBRO', 'The player of the role is a son of the scoping person''s stepparent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('5d52e4bd-2347-43a1-8c61-107a371163f4', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC half-sibling', 'RC_HSIB', 'The player of the role is related to the scoping entity by sharing only one biological parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('513ec433-fb86-4c22-a8d1-c9b8244e69c2', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC half-sister', 'RC_HSIS', 'The player of the role is a female related to the scoping entity by sharing only one biological parent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('f84fece8-5ee3-43c4-9085-4ff3c43235ee', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural sibling', 'RC_NSIB', 'The player of the role has both biological parents in common with the scoping entity.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('0c409220-413e-4125-bf30-8b4f7d10ef0a', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC natural sister', 'RC_NSIS', 'The player of the role is a female having the same biological parents as the scoping entity.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('138fea24-3a1a-4e8e-a91b-d4b61fbd2917', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC twin sister', 'RC_TWINSIS', 'The scoper was carried in the same womb as the female player and shares common biological parents.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('2f9e14dd-d88e-4522-ad05-6230a49b05fb', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC fraternal twin sister', 'RC_FTWINSIS', 'The scoper was carried in the same womb as the female player and shares common biological parents but is the product of a distinct egg/sperm pair.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('a2347904-20e4-48ad-b957-f14bf7c95cd4', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC identical twin sister', 'RC_ITWINSIS', 'The female scoper is an offspring of the same egg-sperm pair as the female player.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('4754b717-8d21-4d07-957b-be834f4b0e8d', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC twin', 'RC_TWIN', 'The scoper and player were carried in the same womb and shared common biological parents.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('768a7c4c-8cb9-4851-82f8-a104778eff0c', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC fraternal twin', 'RC_FTWIN', 'The scoper and player were carried in the same womb and share common biological parents but are the product of distinct egg/sperm pairs.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('18c47e4d-1bf1-404d-a99a-356fe0a26610', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC identical twin', 'RC_ITWIN', 'The scoper and player are offspring of the same egg-sperm pair.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('913bf937-04fe-4b67-a00f-6aeafe63c78a', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC sister', 'RC_SIS', 'The player of the role is a female sharing one or both parents in common with the scoping entity.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('11e11119-083f-4e7f-8776-713468113260', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC stepsister', 'RC_STPSIS', 'The player of the role is a daughter of the scoping person''s stepparent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('b6ca53f4-29b4-42f4-baa8-e952b697d6f2', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC step sibling', 'RC_STPSIB', 'The player of the role is a child of the scoping person''s stepparent.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('6702e52b-e569-4c84-8f2f-9777c0f2276b', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC significant other', 'RC_SIGOTHR', 'A person who is important to one''s well being; especially a spouse or one in a similar relationship. (The player is the one who is important)'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('79531da0-f1e6-499e-a529-8868db390b97', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC domestic partner', 'RC_DOMPART', 'The player of the role cohabits with the scoping person but is not the scoping person''s spouse.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('dc5a3eff-d842-43a6-ae2c-63df221bb60f', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC former spouse', 'RC_FMRSPS', 'Player of the role was previously joined to the scoping person in marriage and this marriage is now dissolved and inactive. Usage Note: This is significant to indicate as some jurisdictions have different legal requirements for former spouse to access the patient''s record, from a general friend.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('bb32e6c7-667f-4cf3-9347-aa89781edc64', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC spouse', 'RC_SPS', 'The player of the role is a marriage partner of the scoping person.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('bc9352a3-63c0-4ba2-ac51-20c0cbca0ea8', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC husband', 'RC_HUSB', 'The player of the role is a man joined to a woman (scoping person) in marriage.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('b20f2abf-e6a5-4030-928f-24a71f3629bf', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC wife', 'RC_WIFE', 'The player of the role is a woman joined to a man (scoping person) in marriage.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('a543dd56-2e52-418e-a122-9372a2c26110', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC unrelated friend', 'RC_FRND', 'The player of the role is a person who is known, liked, and trusted by the scoping person.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('2f2d6977-d35a-451a-9aa8-f4aee841cbae', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC neighbor', 'RC_NBOR', 'The player of the role lives near or next to the scoping person.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('ad121009-f806-44a8-921c-cba728a81940', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC self', 'RC_ONESELF', 'The relationship that a person has with his or her self.'); +INSERT INTO fhir_code(id, version, code_category_id, name, code, description) VALUES ('935baf12-11f4-42ba-89b5-70d6acea6546', 0, '91065580-f4fa-4135-969a-498e9518c0c8', 'RC Roommate', 'RC_ROOM', 'One who shares living quarters with the subject.'); + +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('4b1d6fa3-6c6e-4643-9f6d-de14e1c36fff', 0, '3ffa1fe5-8ded-4f18-afa6-d847d2b94217', 'CP', 'aa36eeb6-33f1-4637-936a-c934abdaf83f'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('cdc6b669-7c42-460b-ad34-aa65f8655145', 0, '3ffa1fe5-8ded-4f18-afa6-d847d2b94217', 'C', '774ee335-a703-4753-b524-da49e4f10454'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('2b7b5cd5-899d-4cc7-8d66-87b90a71facc', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FAMMEMB', '4611966d-6eea-4277-bfce-a515ac879ef7'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('825d77af-3cf1-4469-8ab9-113d74909196', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'CHILD', '8ac10593-daeb-4885-ade6-abbd89d1d089'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('7bc90b3b-eb56-4969-8de7-655572cd1229', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'CHLDADOPT', 'e0cf031a-9717-48c7-8f64-dd21a2dea323'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('8bb76161-a8e9-4a96-8014-e6aecabeb83b', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'DAUADOPT', '0836ecaa-413f-450f-9222-c648dfbe2768'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('f234fc13-ed0b-4f07-a554-a3732a41f693', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SONADOPT', '2ea8b30d-b468-4f03-a3fb-26cdc1531022'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('9dc15f69-9d94-4305-9e3c-9e9639ab4b62', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'CHLDFOST', '27521b84-cdb4-4ee0-ae4e-b2d6c982fbb0'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('ed3e088e-6dc7-4a47-bc6c-a5e74d5d3200', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'DAUFOST', '5b52db2e-9b04-464e-a6d6-3b4e1bf8760e'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('5313a82e-ad97-4d22-a4b3-bdf9f4a8444d', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SONFOST', '033f40ad-54d5-49ad-991b-5d6f52e42dd8'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('a9b4ec09-1a1a-4117-b73c-6761037e6bf2', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'DAUC', '26a2a332-9b52-4263-83fa-b4cf4e377487'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('60298819-e459-4ec5-a2fe-a902b01e8f3a', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'DAU', '2e161538-99d3-49f1-b83a-1569d7652fc7'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('1de31616-8090-41b5-9ce9-7ce41ca596ac', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPDAU', 'f8cf5e93-551b-4da5-b4f1-e6773807faaf'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('8534108a-f10a-42be-b45f-492572bfe0ca', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NCHILD', 'f1891d46-cd36-44af-a349-b7f542bb4e41'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('9460db11-81ca-48c6-b64f-4e5028bac973', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SON', '81f449f6-b875-403f-96b1-7d5fe6b16152'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('9a7383d1-975c-4c55-ad48-fdff18ad993a', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SONC', '6b262cf2-7738-463f-8a7c-6b8ef4502871'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('cf5a8a43-1e24-4fd9-b91b-d710b002f4b8', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPSON', '127b6fec-2887-4635-9ed7-cd46c88f0193'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('0296ca45-fa4b-4c62-ab30-fee3db6c8523', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPCHLD', 'fe61a98b-337a-459f-aab6-d8950f73c307'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('f31157a1-b2c1-4387-a604-3f2ee1578dfe', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'EXT', '0337c32e-01b2-4806-a893-fe8856b9f1b5'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('cc0b8eca-3979-45a6-826f-11397bcaf741', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'AUNT', '89b92e82-15da-4d67-8f54-9ee311f52a2a'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('74fd41e8-ca04-47c1-a31a-6d4fae3c94f1', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MAUNT', 'a047f7b3-8f44-4044-84ad-5d69ec14a5e4'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('795d157a-74bd-4506-b93e-55f5533ebccd', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PAUNT', '92b81681-0d2a-45fb-b06e-bde340130a19'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('df3c11ac-2e51-472d-bb0f-f2e3ea9bb91c', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'COUSN', 'f88411a0-5dd6-43cc-b992-985be76c8908'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('35bd825f-8477-431b-a944-5557f81d64be', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MCOUSN', 'ccba35d9-678b-49c0-8dc6-aa5908bb373d'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('856dad6f-bad9-4425-ae8e-c9485dabe3e8', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PCOUSN', 'b41b75cc-e50b-480a-aefb-46ccb036fa5c'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('122ec321-b442-42f8-81dc-931396c92418', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GGRPRN', 'f2bc75a7-c0c1-4ee0-898e-e632d9c11764'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('c00dfac9-84d9-4f20-9b31-c4f8cf689524', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GGRFTH', 'd5604d99-1664-4fd5-99ad-8cd093db8d02'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('59976345-fbff-48d2-9996-e5f195ed09f7', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MGGRFTH', 'fbffd3e0-07a0-49a5-a858-46c0d5d3cfec'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('5bd0877e-b204-47eb-9152-04aa8e084fc4', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PGGRFTH', 'd98f5389-5240-4648-955f-dbc1f74affd8'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('e78b7bb6-4df8-4bda-996c-dbffd8b6b1cf', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GGRMTH', 'afdc0aa7-a2fb-4829-9c6c-a248262087f5'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('daeb08d9-399e-433f-b130-1adf7efb348d', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MGGRMTH', '574b70d7-5a80-49d8-bbb8-8857d4ba7546'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('b7243ce1-b362-4b7b-8fe7-99d8b6f8f64f', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PGGRMTH', '87635859-f1b3-4d3d-ba8c-ff66f05d3a47'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('75c86b99-f708-4889-b88f-7e0d6b593481', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MGGRPRN', '2ded7f92-9cf4-4a58-9cec-3dd8a74ee011'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('bdc1ef53-e560-4436-9979-f49b7d3558c0', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PGGRPRN', '1cb40384-22ff-4c9e-9ab4-a733e867de6b'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('bc4cf886-7bc2-47c4-9fa9-65d31fe87e79', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GRNDCHILD', '970efa00-62b2-44d9-b08d-725910450f43'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('e4661b7e-c645-43f9-b5b6-704b163a7560', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GRNDDAU', '5a47cf78-7a74-49b1-b549-af72e442e01d'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('85e11477-ef37-45f7-86ba-e1785bdf7817', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GRNDSON', '10a8a5e9-79d5-4dcb-857c-3092cedd5591'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('248ab307-e0e0-4340-845a-457d6a06a826', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GRPRN', '6052f4db-8354-4c78-be2a-15fd3c89a7bd'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('6896ef65-a0a4-496e-b1b9-9892d36ee220', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GRFTH', '21d150c4-930e-442f-b964-b34ede4fb348'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('19e236bf-2ef0-4b69-ae4f-0eeabb9a7fef', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MGRFTH', 'f17753a8-298d-49b7-8e37-ccfa15060157'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('b95a4171-911f-4ff3-940b-a61ab504ba75', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PGRFTH', '3100a18c-00a8-4aad-926b-6ebeaab4b4a1'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('afa24847-2883-4a08-94f8-f23461bb0a13', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GRMTH', '9d95989b-d071-4eb3-9ac6-d3f691bafee1'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('d919dec1-a86e-4441-ad34-6b9c39b12cbb', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MGRMTH', 'ec36bc5c-acf5-4ef1-b659-1ac78ee968da'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('527c2659-ca60-4cb7-8a2c-f211639dcb8c', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PGRMTH', '0bbece14-c188-4d5b-91b0-8999291fefc7'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('83cfd41f-5a49-4286-937e-c49e8a0e23a0', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MGRPRN', 'e8e3924f-c2e8-45a6-9400-3727b37d7f98'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('985439cc-4f63-4b64-9a7f-314b9ebcfdc6', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PGRPRN', '3e3184f0-6e90-47c2-890b-b8a284e000d5'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('4aa6a4cb-cb9c-4a58-bfa9-bfa2aa2f00c7', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'INLAW', '5ea797ff-ca67-4ddb-baf2-08964ca26e34'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('bcf6e655-dbb9-4fe3-87df-b5734db2d2f6', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'CHLDINLAW', '1e6f1da9-15e8-4512-b502-6699b7ea7c34'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('8203f346-229b-45d3-9a41-ff9d6bdb930b', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'DAUINLAW', 'fcff1dc5-b860-4e8d-bee1-cfac40b33944'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('1065fdc4-3478-42f8-badf-824ba7153a7a', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SONINLAW', 'a8c0f886-aae3-479e-8aa3-d8e37f31b9be'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('e79c9ccf-b0b4-4b0d-9a3b-94dd20c060e6', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PRNINLAW', '41d6001a-9fc9-4337-bbb6-ce1ff284e115'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('e1a154e5-628e-4c96-810b-f180556a0821', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FTHINLAW', '140d4897-6355-4be8-9947-06e0ca12b1ed'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('9cdf2bc5-7522-4b17-bb27-b519e9f3c248', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MTHINLAW', 'd5c679c8-292c-4e78-84a3-fcccbf7b4251'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('dafa6c72-59fa-4af2-9857-bb79b63404ec', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SIBINLAW', '8f8c7f01-9cfa-477a-b6ee-fe595f8284cd'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('e628ef9d-d33c-4635-927a-9a4d5544d001', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'BROINLAW', '19d9601f-fc97-4302-9b33-8abd194b2b06'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('ade7723a-01eb-4d3d-af98-a972a8965989', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SISINLAW', '7612c887-3ae6-4056-800d-3c9af1242e72'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('68da313d-8c1e-4aab-b3c1-26fbc913b423', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NIENEPH', '9162f354-4fdc-45ed-808e-41e9335ea325'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('7a031568-c381-4444-9483-b4b9b95828e5', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NEPHEW', '9324244d-8199-41b1-9fb1-3bf3566d5d57'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('21f3d7eb-6beb-4a97-a8d6-cab3db10a632', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NIECE', '02918b92-51b6-4da0-91b0-b52ddbb5e94f'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('c60311c2-2c5a-400c-9312-f7cf383b5aba', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'UNCLE', 'dadccbc8-dfb2-4572-8f19-d07e325fcc7c'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('46814289-a8af-4305-a31b-a36d2ca89ff4', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MUNCLE', 'd3a4de1f-3d6d-4ab3-bff5-795e7c20a075'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('64153ad7-785a-462e-802e-4723ced03d96', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PUNCLE', '2d037299-1e3d-4276-8d13-1ee73cb43479'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('14116083-429a-437c-9023-c0f4095679a6', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PRN', '758fc298-86b1-4ec0-a463-91034a0e35c9'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('0a5fa555-57d7-482b-907f-0b76e3ae9489', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'ADOPTP', '77bf1643-3352-40b3-ae63-450b2baa37f6'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('41b1d0ae-68b0-433f-a2a1-f57bbce57802', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'ADOPTF', 'a7925456-430d-4c2d-b3b2-7afbc3bbde0f'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('4491d5e3-30d5-40d3-b63a-4c4d6ada0045', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'ADOPTM', 'ddd1f6d3-e362-44cd-9887-5f7d3daaa7dd'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('7e5d8430-8ccb-4a6d-8e16-a11a0c0d2f75', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FTH', '5a395a3d-e1be-4b88-a91d-99b1965cfb3b'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('1c4b6432-c710-474c-84e0-508be93b558c', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FTHFOST', '7ba01ba3-54e0-4034-8ffd-ccee1454f9f1'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('c00c8856-b5b9-4ab5-b194-097d0dfb0c66', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NFTH', '43cd0421-72e9-449c-a70a-fd50ffaea8c9'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('78f78af7-6b94-44ff-bc19-7f6243c2eb08', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NFTHF', 'cf62ae76-136d-4143-9a17-82ec94d4e0bf'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('23f2f8a8-5e80-4f52-bd28-35c33db1bc90', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPFTH', 'edb48195-0644-4e15-b4a7-6edfe3c8062c'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('0b2ca2b9-4edb-4da6-9e34-b783575eea0b', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MTH', '08f80790-dda3-4ec1-939c-5884ac1a82d3'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('a903d161-4440-4a22-9e04-53472db3b73c', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'GESTM', 'fcc2d4ce-c441-4542-9f70-ba72f0a3a6df'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('1dd9a9c4-ed3a-4ae8-90a3-ad8efe290195', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'MTHFOST', '08142067-32b2-4b19-a01c-63f336e0726b'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('2ccf11b2-dfc0-46a1-bf0f-74fff221e6a3', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NMTH', '8df34897-3048-46c5-8cad-bd9b622b9bbb'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('d79585a2-4ac9-4c60-8bfa-2716bbe76a24', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NMTHF', '95f37173-8a35-4148-9cda-a9781bd19f86'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('c95f69c3-bdea-4532-9a18-85adb6e9bfe1', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPMTH', 'cd3a2e6f-f804-4c89-839b-a42917122ec9'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('4a58a518-d876-4626-8c72-41b9a67ca61a', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NPRN', '81965cc5-984e-4c96-9102-2ad03ffbcbef'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('4d7e0578-78b5-46b6-bb09-f9290db3eda3', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'PRNFOST', 'e736e6ed-f237-48ec-91ca-60840ea56d60'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('a5737fa7-4b83-43c6-bd8c-3db6833aa4e8', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPPRN', '8457ff34-8a3c-4e83-81b6-2991b2ca4aca'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('82378873-9b73-4aee-a90d-f048c70c11a0', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SIB', '315774e5-a881-4d09-b21f-b2640c9ac0e8'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('c2e9aab2-e1ad-40f8-b7da-5e347846013b', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'BRO', '98082eca-40c5-4d15-9ed4-8ecd844e3ae7'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('b7321721-23c7-491c-a525-62d5c3c93e0f', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'HBRO', '48bcbb2b-2b95-4266-a756-f277c81857eb'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('949aa131-603e-46c6-bbe8-32914f9a7dcc', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NBRO', 'f2be630f-1d57-4ae0-a094-b4d4257c33f9'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('7aa5d3da-f022-46bf-85ef-a696d68cd86b', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'TWINBRO', '5181d4e6-8c98-4b43-9dfb-fd79a2daac28'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('943d057d-16bb-417b-855e-05212deb63a9', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FTWINBRO', 'f24c3a34-b07a-4397-ae2d-44cef62e1a0a'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('d8a5541c-3798-4d7b-8bc8-afcc0facfe1c', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'ITWINBRO', '39ab3576-1b7f-423e-89a1-75e52f7c9364'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('6ee1dbd0-ef9c-4f1b-9c88-cc2c4814f291', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPBRO', '0a56be2e-819c-4ff1-87bf-0cafb9e217d0'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('fa46c072-cce5-4d64-bc51-959b7be7e235', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'HSIB', '5d52e4bd-2347-43a1-8c61-107a371163f4'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('355d3c20-9919-4ec5-9b1e-7f635ca45889', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'HSIS', '513ec433-fb86-4c22-a8d1-c9b8244e69c2'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('391e1838-e07b-4262-b44f-65bc1964f7ab', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NSIB', 'f84fece8-5ee3-43c4-9085-4ff3c43235ee'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('3070d89e-1e8e-42b4-88e4-76f2bee9753d', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NSIS', '0c409220-413e-4125-bf30-8b4f7d10ef0a'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('7efd2326-c215-4167-ae8e-13e780d76eb0', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'TWINSIS', '138fea24-3a1a-4e8e-a91b-d4b61fbd2917'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('f5b76291-e710-4596-a78a-d61737faa4de', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FTWINSIS', '2f9e14dd-d88e-4522-ad05-6230a49b05fb'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('2da5c71c-32d4-4128-a34a-61414e51b6ec', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'ITWINSIS', 'a2347904-20e4-48ad-b957-f14bf7c95cd4'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('9a7af4c5-390e-437f-a4f7-b0fe82c4a1ab', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'TWIN', '4754b717-8d21-4d07-957b-be834f4b0e8d'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('2b4058d9-299a-4a04-b01f-4cfe7d6d96a9', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FTWIN', '768a7c4c-8cb9-4851-82f8-a104778eff0c'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('9b13d40a-2388-4ca7-9203-5703e0a64d1b', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'ITWIN', '18c47e4d-1bf1-404d-a99a-356fe0a26610'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('96982b5c-921f-46a9-9081-99d5374e5212', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SIS', '913bf937-04fe-4b67-a00f-6aeafe63c78a'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('c02ef913-df68-40e4-b8c9-5fa76f7b276e', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPSIS', '11e11119-083f-4e7f-8776-713468113260'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('3f9feec5-e018-4a68-8d13-623a14a3a6af', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'STPSIB', 'b6ca53f4-29b4-42f4-baa8-e952b697d6f2'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('42fd8888-a40f-4d8a-95d2-ac6f1d4f6b0c', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SIGOTHR', '6702e52b-e569-4c84-8f2f-9777c0f2276b'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('458f4bd2-5eb2-409e-91f7-a53ba4cd5e8c', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'DOMPART', '79531da0-f1e6-499e-a529-8868db390b97'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('74c1b842-1eaa-46cc-bbeb-a5794ff18864', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FMRSPS', 'dc5a3eff-d842-43a6-ae2c-63df221bb60f'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('5ff6010f-e121-4203-8f70-ef2df4e2085b', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'SPS', 'bb32e6c7-667f-4cf3-9347-aa89781edc64'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('3fbea517-a9a0-410b-b70a-c47f5f436009', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'HUSB', 'bc9352a3-63c0-4ba2-ac51-20c0cbca0ea8'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('75d2ae13-0c2b-4228-9ce9-a85b78b1cdc7', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'WIFE', 'b20f2abf-e6a5-4030-928f-24a71f3629bf'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('3ce08cb5-7dd4-45c1-86ae-04c02e8dd497', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'FRND', 'a543dd56-2e52-418e-a122-9372a2c26110'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('df8cbca0-54bb-4966-ae42-be09618eff2e', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'NBOR', '2f2d6977-d35a-451a-9aa8-f4aee841cbae'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('d5ef19da-6881-4acb-b5d1-f49715bb53ee', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'ONESELF', 'ad121009-f806-44a8-921c-cba728a81940'); +INSERT INTO fhir_system_code(id, version, system_id, system_code, code_id) VALUES ('b71b8bbc-121d-4974-82c2-31d5a6cb7302', 0, '64d3e785-d6a5-4be5-9bc9-5ed9b938e2f6', 'ROOM', '935baf12-11f4-42ba-89b5-70d6acea6546'); + ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- BEGIN LOINC Code Descriptions -- The following copyright applies to the following section that contains description of LOINC codes: @@ -1587,7 +1865,7 @@ if (input.managingOrganization) } if (((organizationReference == null) || organizationReference.isEmpty()) && args[''useLocations''] && input.location && !input.getLocation().isEmpty()) { - var location = referenceUtils.getResource(input.location); + var location = referenceUtils.getResource(input.location, ''LOCATION''); if ((location != null) && location.managingOrganization && !location.getManagingOrganization.isEmpty()) { organizationReference = location.managingOrganization; @@ -1686,6 +1964,117 @@ INSERT INTO fhir_executable_script (id, version, script_id, name, code, descript VALUES ('a52945b5-94b9-48d4-9c49-f67b43d9dfbc', 0, '0e780e50-9a7e-4d4a-a9fd-1b8607d17fbb', 'Org Unit Reference Code from Patient Organization', 'EXTRACT_TEI_DHIS_ORG_UNIT_ID', 'Extracts the organization unit ID reference from the tracked entity instance.'); +-- Script that transforms a Related Person to contact details of a Person +INSERT INTO fhir_script (id, version, name, code, description, script_type, return_type, input_type, output_type) +VALUES ('05fb37c2-7e68-45c0-bfe7-86999492e202', 0, 'Transforms FHIR Related Person to DHIS Person', 'TRANSFORM_FHIR_RELATED_PERSON_DHIS_PERSON', +'Transforms FHIR Related Person to DHIS Person.', 'TRANSFORM_TO_DHIS', 'BOOLEAN', 'FHIR_RELATED_PERSON', +'DHIS_TRACKED_ENTITY_INSTANCE'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('05fb37c2-7e68-45c0-bfe7-86999492e202', 'CONTEXT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('05fb37c2-7e68-45c0-bfe7-86999492e202', 'INPUT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('05fb37c2-7e68-45c0-bfe7-86999492e202', 'OUTPUT'); +INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) +VALUES ('4476320e-3efa-40cc-a2b2-67c9b1255ce6', 0, '05fb37c2-7e68-45c0-bfe7-86999492e202', +'personFirstNameAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, 'ID:ftFBu8mHZ4H', +'The reference of the tracked entity attribute that contains the first name of the related Person.'); +INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) +VALUES ('46936186-a2df-4f3d-9d8f-8adb7b3028cd', 0, '05fb37c2-7e68-45c0-bfe7-86999492e202', +'personLastNameAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, 'ID:EpbquVl5OD6', +'The reference of the tracked entity attribute that contains the last name of the related Person.'); +INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) +VALUES ('a8a6a2d5-1bef-4497-a0f4-85727a79be0e', 0, '05fb37c2-7e68-45c0-bfe7-86999492e202', +'personPhoneAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, 'ID:pjexi5YaAPa', +'The reference of the tracked entity attribute that contains the phone number of the related Person.'); +INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, array_value, mandatory, default_value, description) +VALUES ('198df286-b613-479a-8b4d-3cb9961105e1', 0, '05fb37c2-7e68-45c0-bfe7-86999492e202', +'relationshipTypeCodes', 'CODE', TRUE, TRUE, 'RC_C|RC_CP|RC_PRN|RC_ADOPTM|RC_ADOPTF|RC_MTH|RC_FTH', +'Codes of patient relationship types in their preferred order.'); +INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) +VALUES ('bee0e77c-d2a5-4950-ae2e-2e399c1c3629', 0, '05fb37c2-7e68-45c0-bfe7-86999492e202', +'preferredGender', 'GENDER', FALSE, 'FEMALE', +'The preferred gender of the person (e.g. female in case of a mother).'); +INSERT INTO fhir_script_source (id, version, script_id, source_text, source_type) VALUES ('0cb05413-c78a-4b27-97fc-a73c3cb9c430', 0, '05fb37c2-7e68-45c0-bfe7-86999492e202', +'function getMatchingRelatedPerson(relatedPersons, relationshipTypeCodes, preferredGender) +{ + var groupedSystemCodeValues = codeUtils.getSystemCodeValuesByCodes(relationshipTypeCodes); + for (var j = 0; j < relationshipTypeCodes.length; j++ ) + { + var systemCodeValues = groupedSystemCodeValues[relationshipTypeCodes[j]]; + if (systemCodeValues != null) + { + var matchingRelatedPerson = null; + for (var k = 0; k < relatedPersons.length; k++) + { + if (codeUtils.containsAnyCode(relatedPersons[k].getRelationship(), systemCodeValues)) + { + if (preferredGender == null) + { + return relatedPersons[k]; + } + if ((relatedPersons[k].getGender() != null) && (relatedPersons[k].getGender().name() === preferredGender)) + { + return relatedPersons[k]; + } + if (matchingRelatedPerson == null) + { + matchingRelatedPerson = relatedPersons[k]; + } + } + } + if (matchingRelatedPerson != null) + { + return matchingRelatedPerson; + } + } + } + return null; +} +var patient = referenceUtils.getResource(input.patient, ''PATIENT'', true); +if ((patient != null) && patient.hasLink()) +{ + var relatedPersons = []; + for (var i = 0; i < patient.getLink().size(); i++) + { + var link = patient.getLink().get(i); + if (!link.isEmpty() && !link.getOther().isEmpty()) + { + var relatedPerson = referenceUtils.getResource(link.getOther(), ''RELATED_PERSON''); + if ((relatedPerson != null) && (!relatedPerson.hasActive() || relatedPerson.getActive()) && dateTimeUtils.isValidNow(relatedPerson.getPeriod())) + { + relatedPersons.push(relatedPerson); + } + } + } + if (relatedPersons.length > 0) + { + var matchingRelatedPerson = getMatchingRelatedPerson(relatedPersons, args[''relationshipTypeCodes''], args[''preferredGender'']); + if (matchingRelatedPerson != null) + { + var lastName = humanNameUtils.getPrimaryName(matchingRelatedPerson.name).family; + if ((lastName != null) || args[''resetDhisValue'']) + { + output.setOptionalValue( args[''personLastNameAttribute''], lastName, context.getFhirRequest().getLastUpdated() ); + } + var firstName = humanNameUtils.getSingleGiven(humanNameUtils.getPrimaryName(matchingRelatedPerson.name)); + if ((firstName != null) || args[''resetDhisValue'']) + { + output.setOptionalValue( args[''personFirstNameAttribute''], firstName, context.getFhirRequest().getLastUpdated() ); + } + var phone = contactPointUtils.getPhoneContactPointValue(matchingRelatedPerson.telecom); + if ((phone != null) || args[''resetDhisValue'']) + { + output.setOptionalValue( args[''personPhoneAttribute''], phone, context.getFhirRequest().getLastUpdated() ); + } + } + } +} +true', 'JAVASCRIPT'); +INSERT INTO fhir_script_source_version (script_source_id, fhir_version) +VALUES ('0cb05413-c78a-4b27-97fc-a73c3cb9c430', 'DSTU3'); +INSERT INTO fhir_executable_script (id, version, script_id, name, code, description) +VALUES ('c6b2d08d-3a73-434e-a5af-ee0ff13549a1', 0, '05fb37c2-7e68-45c0-bfe7-86999492e202', +'Transforms FHIR Related Person to DHIS Person', 'TRANSFORM_FHIR_RELATED_PERSON_DHIS_PERSON', +'Transforms FHIR Related Person to DHIS Person.'); + -- Script that transforms Patient to Person INSERT INTO fhir_script (id, version, name, code, description, script_type, return_type, input_type, output_type) VALUES ('ea887943-5e94-4e31-9441-c7661fe1063e', 0, 'Transforms FHIR Patient to DHIS Person', 'TRANSFORM_FHIR_PATIENT_DHIS_PERSON', 'Transforms FHIR Patient to DHIS Person.', 'TRANSFORM_TO_DHIS', 'BOOLEAN', 'FHIR_PATIENT', 'DHIS_TRACKED_ENTITY_INSTANCE'); @@ -1693,58 +2082,56 @@ INSERT INTO fhir_script_variable (script_id, variable) VALUES ('ea887943-5e94-4e INSERT INTO fhir_script_variable (script_id, variable) VALUES ('ea887943-5e94-4e31-9441-c7661fe1063e', 'INPUT'); INSERT INTO fhir_script_variable (script_id, variable) VALUES ('ea887943-5e94-4e31-9441-c7661fe1063e', 'OUTPUT'); INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) +VALUES ('276b26f2-ba01-41e6-89c6-b1100580b1f3', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', +'uniqueIdAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, 'ID:KSr2yTdu1AI', +'The reference of the tracked entity attribute that contains a unique ID and should be set to the identifier that is used by FHIR.'); +INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) VALUES ('0a7c26cb-7bd3-4394-9d47-a610ac231f8a', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'lastNameAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', TRUE, 'NAME:Last name', +'lastNameAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', TRUE, 'ID:aW66s2QSosT', 'The reference of the tracked entity attribute that contains the last name of the Person.'); INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) VALUES ('b41dd571-a129-4fa6-a807-35ea5663e8e3', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'firstNameAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', TRUE, 'CODE:MMD_PER_NAM', +'firstNameAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', TRUE, 'ID:TfdH5KvFmMy', 'The reference of the tracked entity attribute that contains the first name of the Person.'); INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) +VALUES ('9c62145d-55a6-4e3f-bfb8-df81ae43146a', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', +'resetDhisValue', 'BOOLEAN', TRUE, 'false', 'Specifies if existing values in DHIS can be reset by null values (except first and last name).'); +INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) VALUES ('90b3c110-38e4-4291-934c-e2569e8af1ba', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'birthDateAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, NULL, +'birthDateAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, 'ID:BiTsLcJQ95V', 'The reference of the tracked entity attribute that contains the birth date of the Person.'); INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) VALUES ('8e3efdc7-6ce4-4899-bb20-faed7d5e3279', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'genderAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, NULL, +'genderAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, 'ID:CklPZdOd6H1', 'The reference of the tracked entity attribute that contains the gender of the Person.'); INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) -VALUES ('40a28a9c-82e3-46e8-9eb9-44aaf2f5eacc', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'addressLineAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, NULL, -'The reference of the tracked entity attribute that contains the address line (e.g. street, house number) of the Person.'); -INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) VALUES ('ae13ceca-86d7-4f60-8d54-25587d53a5bd', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'cityAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, NULL, -'The reference of the tracked entity attribute that contains the city of the Person.'); -INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) -VALUES ('6fb6bfe4-5b44-42a1-812f-be1dc8413d6e', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'stateOfCountryAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, NULL, -'The reference of the tracked entity attribute that contains the state (i.e. state of country) of the Person.'); -INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandatory, default_value, description) -VALUES ('a77ef245-e65e-4a87-9c96-5047911f9830', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'countryAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, NULL, -'The reference of the tracked entity attribute that contains the country of the Person.'); +'addressTextAttribute', 'TRACKED_ENTITY_ATTRIBUTE_REF', FALSE, 'ID:Y0i71Y6CVdy', +'The reference of the tracked entity attribute that contains as most as possible from the address of the Person.'); INSERT INTO fhir_script_source (id, version, script_id, source_text, source_type) VALUES ('b2cfaf30-6ede-41f2-bd6c-448e76c429a1', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', -'output.setValue(args[''lastNameAttribute''], humanNameUtils.getPrimaryName(input.name).family, context.getFhirRequest().getLastUpdated()); +'output.setOptionalValue(args[''uniqueIdAttribute''], output.getIdentifier()); +output.setValue(args[''lastNameAttribute''], humanNameUtils.getPrimaryName(input.name).family, context.getFhirRequest().getLastUpdated()); output.setValue(args[''firstNameAttribute''], humanNameUtils.getSingleGiven(humanNameUtils.getPrimaryName(input.name)), context.getFhirRequest().getLastUpdated()); -output.setOptionalValue(args[''birthDateAttribute''], dateTimeUtils.getPreciseDate(input.birthDateElement), context.getFhirRequest().getLastUpdated()); -output.setOptionalValue(args[''genderAttribute''], input.gender, context.getFhirRequest().getLastUpdated()); -output.setOptionalValue(args[''addressLineAttribute''], addressUtils.getSingleLine(addressUtils.getPrimaryAddress(input.address)), context.getFhirRequest().getLastUpdated()); -output.setOptionalValue(args[''cityAttribute''], addressUtils.getPrimaryAddress(input.address).city, context.getFhirRequest().getLastUpdated()); -output.setOptionalValue(args[''stateOfCountryAttribute''], addressUtils.getPrimaryAddress(input.address).state, context.getFhirRequest().getLastUpdated()); -output.setOptionalValue(args[''countryAttribute''], addressUtils.getPrimaryAddress(input.address).country, context.getFhirRequest().getLastUpdated()); +var birthDate = dateTimeUtils.getPreciseDate(input.birthDateElement); +if ((birthDate != null) || args[''resetDhisValue'']) +{ + output.setOptionalValue(args[''birthDateAttribute''], birthDate, context.getFhirRequest().getLastUpdated()); +} +if ((input.gender != null) || args[''resetDhisValue'']) +{ + output.setOptionalValue(args[''genderAttribute''], input.gender, context.getFhirRequest().getLastUpdated()); +} +var addressText = addressUtils.getConstructedText(addressUtils.getPrimaryAddress(input.address)); +if ((addressText != null) || args[''resetDhisValue'']) +{ + output.setOptionalValue(args[''addressTextAttribute''], addressText, context.getFhirRequest().getLastUpdated()); +} true', 'JAVASCRIPT'); INSERT INTO fhir_script_source_version (script_source_id, fhir_version) VALUES ('b2cfaf30-6ede-41f2-bd6c-448e76c429a1', 'DSTU3'); INSERT INTO fhir_executable_script (id, version, script_id, name, code, description) VALUES ('72451c8f-7492-4707-90b8-a3e0796de19e', 0, 'ea887943-5e94-4e31-9441-c7661fe1063e', 'Transforms FHIR Patient to DHIS Person', 'TRANSFORM_FHIR_PATIENT_DHIS_PERSON', 'Transforms FHIR Patient to DHIS Person.'); -INSERT INTO fhir_executable_script_argument(id, executable_script_id, script_argument_id, override_value) -VALUES ('9b832b2c-0a57-4441-8411-47b5dc65ec91', '72451c8f-7492-4707-90b8-a3e0796de19e', '90b3c110-38e4-4291-934c-e2569e8af1ba', 'CODE:MMD_PER_DOB'); -INSERT INTO fhir_executable_script_argument(id, executable_script_id, script_argument_id, override_value) -VALUES ('5ce705ce-415c-4fb3-baa7-d3ae67823ac9', '72451c8f-7492-4707-90b8-a3e0796de19e', '8e3efdc7-6ce4-4899-bb20-faed7d5e3279', 'NAME:Gender'); -INSERT INTO fhir_executable_script_argument(id, executable_script_id, script_argument_id, override_value) -VALUES ('871dde31-8da8-4345-b38a-e065236a7ffa', '72451c8f-7492-4707-90b8-a3e0796de19e', 'ae13ceca-86d7-4f60-8d54-25587d53a5bd', 'CODE:City'); -- Script that performs the lookup of TEI FHIR Resource from FHIR Observation INSERT INTO fhir_script (id, version, name, code, description, script_type, return_type, input_type, output_type) @@ -1752,7 +2139,7 @@ VALUES ('8b5ab5f1-363d-4ccb-8e63-d6ecf25b3017', 0, 'Observation TEI Lookup', 'OB INSERT INTO fhir_script_variable (script_id, variable) VALUES ('8b5ab5f1-363d-4ccb-8e63-d6ecf25b3017', 'CONTEXT'); INSERT INTO fhir_script_variable (script_id, variable) VALUES ('8b5ab5f1-363d-4ccb-8e63-d6ecf25b3017', 'INPUT'); INSERT INTO fhir_script_source (id,version,script_id,source_text,source_type) -VALUES ('960d2e6c-2479-48a2-b04e-b14879e71d14', 0, '8b5ab5f1-363d-4ccb-8e63-d6ecf25b3017', 'referenceUtils.getResource(input.subject)', 'JAVASCRIPT'); +VALUES ('960d2e6c-2479-48a2-b04e-b14879e71d14', 0, '8b5ab5f1-363d-4ccb-8e63-d6ecf25b3017', 'referenceUtils.getResource(input.subject, ''PATIENT'')', 'JAVASCRIPT'); INSERT INTO fhir_script_source_version (script_source_id,fhir_version) VALUES ('960d2e6c-2479-48a2-b04e-b14879e71d14', 'DSTU3'); INSERT INTO fhir_executable_script (id, version, script_id, name, code, description) @@ -1813,7 +2200,7 @@ INSERT INTO fhir_script_argument(id, version, script_id, name, data_type, mandat VALUES ('f08bc5b3-0aef-4707-8959-65d7cf690134', 0, '73175d8e-faad-458f-b38f-14ff87032720', 'mappedObservationCodes', 'CODE', TRUE, TRUE, NULL, 'Mapped observation codes that define if the FHIR Observation is applicable for processing.'); INSERT INTO fhir_script_source (id,version,script_id,source_text,source_type) -VALUES ('376386c8-e306-49c7-9395-0d334aab60fc', 0, '73175d8e-faad-458f-b38f-14ff87032720', 'codeUtils.containsMappedCode(input.code, args[''mappedObservationCodes''])', 'JAVASCRIPT'); +VALUES ('376386c8-e306-49c7-9395-0d334aab60fc', 0, '73175d8e-faad-458f-b38f-14ff87032720', 'codeUtils.containsMappingCode(input.code, args[''mappedObservationCodes''])', 'JAVASCRIPT'); INSERT INTO fhir_script_source_version (script_source_id,fhir_version) VALUES ('376386c8-e306-49c7-9395-0d334aab60fc', 'DSTU3'); @@ -1907,13 +2294,26 @@ VALUES ('8d26fe6a-d9ac-40ab-abc8-5a87ab340762', 0, 'f1da6937-e2fe-47a4-b0f3-8bbf INSERT INTO fhir_script_source_version (script_source_id, fhir_version) VALUES ('8d26fe6a-d9ac-40ab-abc8-5a87ab340762', 'DSTU3'); +-- Script that performs the lookup of TEI FHIR Resource (Patient) from FHIR Related Person +INSERT INTO fhir_script (id, version, name, code, description, script_type, return_type, input_type, output_type) +VALUES ('0cf40900-9de3-468c-bfd0-fa2a4703fb66', 0, 'Related Person TEI Lookup', 'RELATED_PERSON_TEI_LOOKUP', 'Lookup of the Tracked Entity Instance FHIR Resource from FHIR Related Person.', 'EVALUATE', 'FHIR_RESOURCE', 'FHIR_RELATED_PERSON', NULL); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('0cf40900-9de3-468c-bfd0-fa2a4703fb66', 'CONTEXT'); +INSERT INTO fhir_script_variable (script_id, variable) VALUES ('0cf40900-9de3-468c-bfd0-fa2a4703fb66', 'INPUT'); +INSERT INTO fhir_script_source (id,version,script_id,source_text,source_type) +VALUES ('1f94dda8-28ec-480f-8c6b-d8d734612414', 0, '0cf40900-9de3-468c-bfd0-fa2a4703fb66', 'referenceUtils.getResource(input.patient, ''PATIENT'')', 'JAVASCRIPT'); +INSERT INTO fhir_script_source_version (script_source_id,fhir_version) +VALUES ('1f94dda8-28ec-480f-8c6b-d8d734612414', 'DSTU3'); +INSERT INTO fhir_executable_script (id, version, script_id, name, code, description) +VALUES ('26e88808-64ee-4469-8083-7962b74ac48a', 0, '0cf40900-9de3-468c-bfd0-fa2a4703fb66', +'Related Person TEI Lookup', 'RELATED_PERSON_TEI_LOOKUP', 'Lookup of the Tracked Entity Instance FHIR Resource from FHIR Related Person.'); + -- Script that performs the lookup of TEI FHIR Resource from FHIR Immunization INSERT INTO fhir_script (id, version, name, code, description, script_type, return_type, input_type, output_type) VALUES ('d4e2822a-4422-46a3-badc-cf5604c6e11f', 0, 'Immunization TEI Lookup', 'IMMUNIZATION_TEI_LOOKUP', 'Lookup of the Tracked Entity Instance FHIR Resource from FHIR Immunization.', 'EVALUATE', 'FHIR_RESOURCE', 'FHIR_IMMUNIZATION', NULL); INSERT INTO fhir_script_variable (script_id, variable) VALUES ('d4e2822a-4422-46a3-badc-cf5604c6e11f', 'CONTEXT'); INSERT INTO fhir_script_variable (script_id, variable) VALUES ('d4e2822a-4422-46a3-badc-cf5604c6e11f', 'INPUT'); INSERT INTO fhir_script_source (id,version,script_id,source_text,source_type) -VALUES ('85b3c460-6c2a-4f50-af46-ff09bf2e69df', 0, 'd4e2822a-4422-46a3-badc-cf5604c6e11f', 'referenceUtils.getResource(input.patient)', 'JAVASCRIPT'); +VALUES ('85b3c460-6c2a-4f50-af46-ff09bf2e69df', 0, 'd4e2822a-4422-46a3-badc-cf5604c6e11f', 'referenceUtils.getResource(input.patient, ''PATIENT'')', 'JAVASCRIPT'); INSERT INTO fhir_script_source_version (script_source_id,fhir_version) VALUES ('85b3c460-6c2a-4f50-af46-ff09bf2e69df', 'DSTU3'); INSERT INTO fhir_executable_script (id, version, script_id, name, code, description) @@ -1929,7 +2329,7 @@ INSERT INTO fhir_script_variable (script_id, variable) VALUES ('a5079830-f04c-45 INSERT INTO fhir_script_variable (script_id, variable) VALUES ('a5079830-f04c-4575-af5d-1d6fa0bf844b', 'INPUT'); INSERT INTO fhir_script_source (id, version, script_id, source_text, source_type) VALUES ('6427e6de-8426-4ab1-b66a-edd5b0e5f410', 0, 'a5079830-f04c-4575-af5d-1d6fa0bf844b', 'var location = null; -var locationResource = referenceUtils.getResource(input.getLocation()); +var locationResource = referenceUtils.getResource(input.getLocation(), ''LOCATION''); if ((locationResource != null) && locationResource.hasPosition()) { var position = locationResource.getPosition(); @@ -2010,11 +2410,21 @@ VALUES ('081c4642-bb83-44ab-b90f-aa206ad347aa', 0, 'f18acd12-bc85-4f79-935d-3539 INSERT INTO fhir_script_source_version (script_source_id, fhir_version) VALUES ('081c4642-bb83-44ab-b90f-aa206ad347aa', 'DSTU3'); +-- Tracked Entity Person +INSERT INTO fhir_tracked_entity(id, version, name, description, tracked_entity_ref, tracked_entity_identifier_ref) +VALUES ('4203754d-2177-4a44-86aa-2de31ee4c8ee', 0, 'Person', 'Tracked entity for a patient.', 'NAME:Person', 'ID:Ewi7FUfcHAD'); + -- Rule FHIR Patient to tracked entity type Person -INSERT INTO fhir_rule (id, version, name, description, enabled, evaluation_order, fhir_resource_type, dhis_resource_type, applicable_in_script_id, transform_in_script_id) -VALUES ('5f9ebdc9-852e-4c83-87ca-795946aabc35', 0, 'FHIR Patient to Person', NULL, TRUE, 0, 'PATIENT', 'TRACKED_ENTITY', '9299b82e-b90a-4542-8b78-200cadff3d7d', '72451c8f-7492-4707-90b8-a3e0796de19e'); -INSERT INTO fhir_tracked_entity_rule (id, tracked_entity_ref, org_lookup_script_id, loc_lookup_script_id, tracked_entity_identifier_ref) -VALUES ('5f9ebdc9-852e-4c83-87ca-795946aabc35', 'NAME:Person', '25a97bb4-7b39-4ed4-8677-db4bcaa28ccf', 'ef90531f-4438-48bd-83b3-6370dd65875a', 'CODE:National identifier'); +INSERT INTO fhir_rule (id, version, name, description, enabled, evaluation_order, fhir_resource_type, dhis_resource_type, transform_in_script_id) +VALUES ('5f9ebdc9-852e-4c83-87ca-795946aabc35', 0, 'FHIR Patient to Person', NULL, TRUE, 0, 'PATIENT', 'TRACKED_ENTITY', '72451c8f-7492-4707-90b8-a3e0796de19e'); +INSERT INTO fhir_tracked_entity_rule (id, tracked_entity_id, org_lookup_script_id, loc_lookup_script_id) +VALUES ('5f9ebdc9-852e-4c83-87ca-795946aabc35', '4203754d-2177-4a44-86aa-2de31ee4c8ee', '25a97bb4-7b39-4ed4-8677-db4bcaa28ccf', 'ef90531f-4438-48bd-83b3-6370dd65875a'); + +-- Rule FHIR Related Person to tracked entity type Person +INSERT INTO fhir_rule (id, version, name, description, enabled, evaluation_order, fhir_resource_type, dhis_resource_type, transform_in_script_id, contained_allowed) +VALUES ('52227dd9-c79c-478b-92af-9aa1f33c76fd', 0, 'FHIR Related Person to Person', NULL, TRUE, 0, 'RELATED_PERSON', 'TRACKED_ENTITY', 'c6b2d08d-3a73-434e-a5af-ee0ff13549a1', TRUE); +INSERT INTO fhir_tracked_entity_rule (id, tracked_entity_id, tei_lookup_script_id) +VALUES ('52227dd9-c79c-478b-92af-9aa1f33c76fd', '4203754d-2177-4a44-86aa-2de31ee4c8ee', '26e88808-64ee-4469-8083-7962b74ac48a'); UPDATE fhir_system_code sc SET system_code_value = (SELECT system_uri FROM fhir_system s WHERE s.id=sc.system_id) || '|' || system_code; ALTER TABLE fhir_system_code ALTER COLUMN system_code_value SET NOT NULL; diff --git a/fhir/src/main/resources/static/dhis/metadata/person-metadata.json b/fhir/src/main/resources/static/dhis/metadata/person-metadata.json new file mode 100644 index 00000000..d050da9c --- /dev/null +++ b/fhir/src/main/resources/static/dhis/metadata/person-metadata.json @@ -0,0 +1,568 @@ +{ + "system": { + "id": "2db2129c-7fec-4495-b7e0-8480a93b1d5e", + "rev": "faabb37", + "version": "2.30", + "date": "2018-11-14T09:55:12.673" + }, + "trackedEntityAttributes": [ + { + "lastUpdated": "2018-06-11T14:59:59.397", + "id": "VCtm2pySeEV", + "created": "2018-05-29T21:07:28.490", + "name": "Address", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": false, + "pattern": "", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-06-11T14:57:10.955", + "id": "BiTsLcJQ95V", + "created": "2016-05-21T12:24:28.916", + "name": "Date of birth", + "shortName": "Date of birth", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": false, + "pattern": "", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "DATE", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-10-23T14:53:38.967", + "id": "TfdH5KvFmMy", + "created": "2016-05-21T12:23:24.765", + "name": "First Name", + "shortName": "First Name", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": true, + "pattern": "", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-10-12T06:21:13.292", + "id": "CklPZdOd6H1", + "created": "2016-05-21T12:26:38.491", + "name": "Gender", + "shortName": "Gender", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": false, + "pattern": "", + "description": "Gender of Person", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "optionSet": { + "id": "hiQ3QFheQ3O" + }, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-10-23T14:53:16.309", + "id": "aW66s2QSosT", + "created": "2016-05-21T12:23:38.798", + "name": "Last Name", + "shortName": "Last Name", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": true, + "pattern": "", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-10-11T09:53:30.658", + "id": "pjexi5YaAPa", + "created": "2015-12-08T11:14:33.639", + "name": "Mother/Caregiver's contact number", + "shortName": "Mother/Caregiver's contact number", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": false, + "pattern": "", + "skipSynchronization": false, + "sortOrderInListNoProgram": 0, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "PHONE_NUMBER", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-10-11T09:53:30.658", + "id": "ftFBu8mHZ4H", + "created": "2015-11-05T20:12:21.038", + "name": "Mother/Caregiver's first name", + "shortName": "Mother/Caregiver's first name", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": false, + "pattern": "", + "skipSynchronization": false, + "sortOrderInListNoProgram": 0, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-10-11T09:53:30.658", + "id": "EpbquVl5OD6", + "created": "2015-11-05T20:12:02.476", + "name": "Mother/Caregiver's last name", + "shortName": "Mother/Caregiver's last name", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": false, + "pattern": "", + "skipSynchronization": false, + "sortOrderInListNoProgram": 0, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-05-29T15:45:31.525", + "id": "Ewi7FUfcHAD", + "created": "2016-05-21T12:22:40.091", + "name": "Patient ID", + "shortName": "Patient ID", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": true, + "pattern": "", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": true, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-10-11T09:53:30.658", + "id": "cGs3k0IhQmQ", + "created": "2015-11-05T20:15:25.212", + "name": "Place of Birth", + "shortName": "Place of Birth", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": false, + "pattern": "", + "skipSynchronization": false, + "sortOrderInListNoProgram": 0, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "optionSet": { + "id": "OhW3bWNSTum" + }, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "code": "UID", + "lastUpdated": "2018-10-23T14:54:00.165", + "id": "KSr2yTdu1AI", + "created": "2017-11-26T15:41:07.322", + "name": "Unique ID", + "shortName": "UID", + "aggregationType": "COUNT", + "programScope": false, + "displayInListNoProgram": true, + "pattern": "", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": true, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + }, + { + "lastUpdated": "2018-06-11T15:00:28.779", + "id": "Y0i71Y6CVdy", + "created": "2018-01-19T12:41:37.476", + "name": "Village Name", + "aggregationType": "NONE", + "programScope": false, + "displayInListNoProgram": false, + "pattern": "", + "skipSynchronization": false, + "generated": false, + "displayOnVisitSchedule": false, + "valueType": "TEXT", + "orgunitScope": false, + "confidential": false, + "unique": false, + "inherit": false, + "translations": [ ], + "attributeValues": [ ], + "userAccesses": [ ], + "legendSets": [ ] + } + ], + "trackedEntityTypes": [ + { + "code": "Person", + "created": "2017-11-01T00:00:00.000", + "lastUpdated": "2018-10-23T14:34:43.826", + "name": "Person", + "id": "MCPQUTHX1Ze", + "description": "Person", + "maxTeiCountToReturn": 0, + "allowAuditLog": false, + "minAttributesRequiredToSearch": 1, + "attributeValues": [ ], + "trackedEntityTypeAttributes": [ + { + "lastUpdated": "2018-11-15T12:38:45.733", + "id": "yFfy1y9vvZ3", + "created": "2018-11-15T12:38:45.702", + "name": "Person Unique ID", + "displayName": "Person Unique ID", + "mandatory": false, + "displayShortName": "null UID", + "externalAccess": false, + "valueType": "TEXT", + "searchable": false, + "displayInList": true, + "favorite": false, + "access": { + "read": true, + "update": true, + "externalize": true, + "delete": true, + "write": true, + "manage": true + }, + "trackedEntityAttribute": { + "id": "KSr2yTdu1AI" + }, + "trackedEntityType": { + "id": "MCPQUTHX1Ze" + }, + "favorites": [ ], + "translations": [ ], + "userGroupAccesses": [ ], + "attributeValues": [ ], + "userAccesses": [ ] + }, + { + "lastUpdated": "2018-11-15T12:38:45.734", + "id": "eUP78IY4rUF", + "created": "2018-11-15T12:38:45.702", + "name": "Person Patient ID", + "displayName": "Person Patient ID", + "displayShortName": "null Patient ID", + "externalAccess": false, + "valueType": "TEXT", + "searchable": false, + "displayInList": true, + "favorite": false, + "access": { + "read": true, + "update": true, + "externalize": true, + "delete": true, + "write": true, + "manage": true + }, + "trackedEntityAttribute": { + "id": "Ewi7FUfcHAD" + }, + "trackedEntityType": { + "id": "MCPQUTHX1Ze" + }, + "favorites": [ ], + "translations": [ ], + "userGroupAccesses": [ ], + "attributeValues": [ ], + "userAccesses": [ ] + }, + { + "lastUpdated": "2018-11-15T12:38:45.735", + "id": "XznTxIRztFR", + "created": "2018-11-15T12:38:45.702", + "name": "Person First Name", + "displayName": "Person First Name", + "displayShortName": "null First Name", + "externalAccess": false, + "valueType": "TEXT", + "searchable": false, + "displayInList": true, + "favorite": false, + "access": { + "read": true, + "update": true, + "externalize": true, + "delete": true, + "write": true, + "manage": true + }, + "trackedEntityAttribute": { + "id": "TfdH5KvFmMy" + }, + "trackedEntityType": { + "id": "MCPQUTHX1Ze" + }, + "favorites": [ ], + "translations": [ ], + "userGroupAccesses": [ ], + "attributeValues": [ ], + "userAccesses": [ ] + }, + { + "lastUpdated": "2018-11-15T12:38:45.735", + "id": "aobAjew2sJt", + "created": "2018-11-15T12:38:45.702", + "name": "Person Last Name", + "displayName": "Person Last Name", + "displayShortName": "null Last Name", + "externalAccess": false, + "valueType": "TEXT", + "searchable": false, + "displayInList": true, + "favorite": false, + "access": { + "read": true, + "update": true, + "externalize": true, + "delete": true, + "write": true, + "manage": true + }, + "trackedEntityAttribute": { + "id": "aW66s2QSosT" + }, + "trackedEntityType": { + "id": "MCPQUTHX1Ze" + }, + "favorites": [ ], + "translations": [ ], + "userGroupAccesses": [ ], + "attributeValues": [ ], + "userAccesses": [ ] + }, + { + "lastUpdated": "2018-11-15T12:38:45.735", + "id": "D011up7xrF1", + "created": "2018-11-15T12:38:45.702", + "name": "Person Gender", + "displayName": "Person Gender", + "displayShortName": "null Gender", + "externalAccess": false, + "valueType": "TEXT", + "searchable": false, + "displayInList": true, + "favorite": false, + "access": { + "read": true, + "update": true, + "externalize": true, + "delete": true, + "write": true, + "manage": true + }, + "trackedEntityAttribute": { + "id": "CklPZdOd6H1" + }, + "trackedEntityType": { + "id": "MCPQUTHX1Ze" + }, + "favorites": [ ], + "translations": [ ], + "userGroupAccesses": [ ], + "attributeValues": [ ], + "userAccesses": [ ] + } + ], + "translations": [ ] + } + ], + "options": [ + { + "code": "FEMALE", + "created": "2016-05-21T12:26:12.090", + "lastUpdated": "2018-01-17T10:49:48.072", + "name": "Female", + "id": "AZK4rjJCss5", + "sortOrder": 2, + "optionSet": { + "id": "hiQ3QFheQ3O" + }, + "attributeValues": [ ], + "translations": [ ] + }, + { + "code": "MALE", + "created": "2016-05-21T12:26:04.313", + "lastUpdated": "2018-01-17T10:49:48.071", + "name": "Male", + "id": "UrUdMteQzlT", + "sortOrder": 1, + "optionSet": { + "id": "hiQ3QFheQ3O" + }, + "attributeValues": [ ], + "translations": [ ] + }, + { + "code": "Health Facility", + "created": "2015-11-07T11:56:02.000", + "lastUpdated": "2018-10-11T09:53:27.923", + "name": "Health Facility", + "id": "PmBE4gFEY5P", + "sortOrder": 2, + "optionSet": { + "id": "OhW3bWNSTum" + }, + "attributeValues": [ ], + "translations": [ ] + }, + { + "code": "Home", + "created": "2015-11-07T11:56:02.000", + "lastUpdated": "2018-10-11T09:53:27.924", + "name": "Home", + "id": "LwrVZvQy5A5", + "sortOrder": 1, + "optionSet": { + "id": "OhW3bWNSTum" + }, + "attributeValues": [ ], + "translations": [ ] + } + ], + "optionSets": [ + { + "created": "2016-05-21T12:25:50.167", + "lastUpdated": "2018-01-19T13:42:50.187", + "name": "Sex", + "id": "hiQ3QFheQ3O", + "version": 1, + "valueType": "TEXT", + "attributeValues": [ ], + "translations": [ ], + "userAccesses": [ ], + "options": [ + { + "id": "UrUdMteQzlT" + }, + { + "id": "AZK4rjJCss5" + } + ] + }, + { + "created": "2015-11-05T19:54:03.134", + "lastUpdated": "2018-10-11T09:53:29.080", + "name": "Place of birth", + "id": "OhW3bWNSTum", + "version": 1, + "valueType": "TEXT", + "attributeValues": [ ], + "translations": [ ], + "userAccesses": [ ], + "options": [ + { + "id": "LwrVZvQy5A5" + }, + { + "id": "PmBE4gFEY5P" + } + ] + } + ] +} diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionResourceRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionResourceRepositoryRestDocsTest.java index 3caabbd0..9692d310 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionResourceRepositoryRestDocsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RemoteSubscriptionResourceRepositoryRestDocsTest.java @@ -119,6 +119,8 @@ public void readRemoteSubscriptionResource() throws Exception fields.withPath( "fhirResourceType" ).description( "The type of the subscribed FHIR resource." ).type( JsonFieldType.STRING ), fields.withPath( "description" ).description( "The detailed description of the purpose of the subscribed FHIR resource." ).type( JsonFieldType.STRING ).optional(), fields.withPath( "fhirCriteriaParameters" ).description( "The prefix that should be added to the codes when mapping them to DHIS2." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "virtual" ).description( "Specifies that there is no subscription for this FHIR resource since the FHIR service may not accept subscription for this resource type (just available as contained resources)." ) + .type( JsonFieldType.BOOLEAN ).optional(), fields.withPath( "hirSubscriptionId" ).description( "The ID of the automatically created FHIR subscription on the FHIR service." ).type( JsonFieldType.STRING ).optional(), subsectionWithPath( "_links" ).description( "Links to other resources" ) ) ) ); diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RuleRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RuleRepositoryRestDocsTest.java index c0313c5c..ab5d0af9 100644 --- a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RuleRepositoryRestDocsTest.java +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/RuleRepositoryRestDocsTest.java @@ -33,6 +33,7 @@ import org.dhis2.fhir.adapter.fhir.ConstrainedFields; import org.dhis2.fhir.adapter.fhir.metadata.model.AbstractRule; import org.dhis2.fhir.adapter.fhir.metadata.model.ExecutableScript; +import org.dhis2.fhir.adapter.fhir.metadata.model.MappedTrackedEntity; import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -65,14 +66,19 @@ public class RuleRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTes @Autowired private RuleRepository ruleRepository; + @Autowired + private MappedTrackedEntityRepository mappedTrackedEntityRepository; + @Autowired private ExecutableScriptRepository executableScriptRepository; @Test public void createTrackedEntityRule() throws Exception { + final MappedTrackedEntity mappedTrackedEntity = loadTrackedEntity( "Person" ); final ConstrainedFields fields = new ConstrainedFields( TrackedEntityRule.class, constraintDescriptionResolver ); final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntityRule.json", StandardCharsets.UTF_8 ) + .replace( "$trackedEntityId", API_BASE_URI + "/trackedEntities/" + mappedTrackedEntity.getId().toString() ) .replace( "$transformInScriptId", API_BASE_URI + "/executableScripts/" + loadScript( "TRANSFORM_FHIR_PATIENT_DHIS_PERSON" ).getId().toString() ) .replace( "$orgUnitLookupScriptId", API_BASE_URI + "/executableScripts/" + loadScript( "EXTRACT_FHIR_PATIENT_DHIS_ORG_UNIT_CODE" ).getId().toString() ) .replace( "$locationLookupScriptId", API_BASE_URI + "/executableScripts/" + loadScript( "EXTRACT_FHIR_PATIENT_GEO_LOCATION" ).getId().toString() ); @@ -92,16 +98,14 @@ public void createTrackedEntityRule() throws Exception fields.withPath( "applicableInScript" ).description( "Link to the executable script reference that is used to check if the incoming request is applicable for this rule. " + "The script must be an evaluation script that returns a boolean value." ).type( JsonFieldType.STRING ).optional(), fields.withPath( "transformInScript" ).description( "Link to the executable script reference that is used to transform the FHIR resource input to the DHIS2 resource." ).type( JsonFieldType.STRING ), + fields.withPath( "trackedEntity" ).description( "The reference to the DHIS2 Tracked Entity resource." ).type( JsonFieldType.STRING ), fields.withPath( "orgUnitLookupScript" ).description( "Link to the executable script reference that is used to extract the organization unit reference. " + "The script must be an evaluation script that return a reference." ).type( JsonFieldType.STRING ), fields.withPath( "locationLookupScript" ).description( "Link to the executable script reference that is used to extract the location reference. " + "The script must be an evaluation script that return a location (geo coordinates)." ).type( JsonFieldType.STRING ), - fields.withPath( "trackedEntityReference" ).description( "The reference to the DHIS2 Tracked Entity Type." ).type( JsonFieldType.OBJECT ), - fields.withPath( "trackedEntityReference.value" ).description( "The unique ID/code/name of the Tracked Entity Type." ).type( JsonFieldType.STRING ), - fields.withPath( "trackedEntityReference.type" ).description( "The type of reference value of the Tracked Entity Type." ).type( JsonFieldType.STRING ), - fields.withPath( "trackedEntityIdentifierReference" ).description( "The reference to the DHIS2 Tracked Entity Attribute that is used as national identifier." ).type( JsonFieldType.OBJECT ), - fields.withPath( "trackedEntityIdentifierReference.value" ).description( "The unique ID/code/name of the Tracked Entity Attribute." ).type( JsonFieldType.STRING ), - fields.withPath( "trackedEntityIdentifierReference.type" ).description( "The type of reference value of the Tracked Entity Attribute." ).type( JsonFieldType.STRING ) + fields.withPath( "teiLookupScript" ).description( + "Link to the executable script reference that is used to extract the tracked entity related FHIR resource from the input. The script must be an evaluation script that returns a FHIR resource." ) + .type( JsonFieldType.STRING ).optional() ) ) ).andReturn().getResponse().getHeader( "Location" ); mockMvc @@ -113,10 +117,6 @@ public void createTrackedEntityRule() throws Exception .andExpect( jsonPath( "evaluationOrder", is( 0 ) ) ) .andExpect( jsonPath( "dhisResourceType", is( "TRACKED_ENTITY" ) ) ) .andExpect( jsonPath( "fhirResourceType", is( "PATIENT" ) ) ) - .andExpect( jsonPath( "trackedEntityReference.value", is( "Person" ) ) ) - .andExpect( jsonPath( "trackedEntityReference.type", is( "NAME" ) ) ) - .andExpect( jsonPath( "trackedEntityIdentifierReference.value", is( "National identifier" ) ) ) - .andExpect( jsonPath( "trackedEntityIdentifierReference.type", is( "CODE" ) ) ) .andExpect( jsonPath( "_links.self.href", is( location ) ) ); } @@ -133,27 +133,33 @@ public void readTrackedEntityRule() throws Exception linkWithRel( "applicableCodeSet" ).description( "Link to the code set reference that is used to check if the incoming request is applicable for this rule." ).optional(), linkWithRel( "applicableInScript" ).description( "Link to the executable script reference that is used to check if the incoming request is applicable for this rule. The script must be an evaluation script that returns a boolean value." ).optional(), linkWithRel( "transformInScript" ).description( "Link to the executable script reference that is used to transform the FHIR resource input to the DHIS2 resource." ), - linkWithRel( "orgUnitLookupScript" ).description( "Link to the executable script reference that is used to extract the organization unit reference. The script must be an evaluation script that return a reference." ), - linkWithRel( "locationLookupScript" ).description( "Link to the executable script reference that is used to extract the location reference. The script must be an evaluation script that return a location (geo coordinates)." ) ), + linkWithRel( "trackedEntity" ).description( "Link to the tracked entity resource that describes the tracked entity of the transformation." ), + linkWithRel( "orgUnitLookupScript" ).description( "Link to the executable script reference that is used to extract the organization unit reference. The script must be an evaluation script that return a reference." ).optional(), + linkWithRel( "locationLookupScript" ).description( "Link to the executable script reference that is used to extract the location reference. The script must be an evaluation script that return a location (geo coordinates)." ).optional(), + linkWithRel( "teiLookupScript" ).description( "Link to the executable script reference that is used to extract the tracked entity related FHIR resource from the input. The script must be an evaluation script that returns a FHIR resource." ).optional() ), responseFields( - attributes( key( "title" ).value( "Fields for rule reading" ) ), - fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), - fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), - fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), - fields.withPath( "name" ).description( "The unique name of the rule." ).type( JsonFieldType.STRING ), - fields.withPath( "description" ).description( "The detailed description that describes for which purpose the rule is used." ).type( JsonFieldType.STRING ).optional(), + attributes( key( "title" ).value( "Fields for rule reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The unique name of the rule." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the rule is used." ).type( JsonFieldType.STRING ).optional(), fields.withPath( "dhisResourceType" ).description( "The type of the rule and the type of the data that is stored in DHIS2." ).type( JsonFieldType.STRING ), fields.withPath( "fhirResourceType" ).description( "The FHIR resource type of the incoming resource." ).type( JsonFieldType.STRING ), fields.withPath( "enabled" ).description( "Specifies if this rule is enabled." ).type( JsonFieldType.BOOLEAN ), fields.withPath( "evaluationOrder" ).description( "Specifies the precedence of this rule when several rules match. Higher values define a higher precedence." ).type( JsonFieldType.NUMBER ), - fields.withPath( "trackedEntityReference" ).description( "The reference to the DHIS2 Tracked Entity Type." ).type( JsonFieldType.OBJECT ), - fields.withPath( "trackedEntityReference.value" ).description( "The unique ID/code/name of the Tracked Entity Type." ).type( JsonFieldType.STRING ), - fields.withPath( "trackedEntityReference.type" ).description( "The type of reference value of the Tracked Entity Type." ).type( JsonFieldType.STRING ), - fields.withPath( "trackedEntityIdentifierReference" ).description( "The reference to the DHIS2 Tracked Entity Attribute that is used as national identifier." ).type( JsonFieldType.OBJECT ), - fields.withPath( "trackedEntityIdentifierReference.value" ).description( "The unique ID/code/name of the Tracked Entity Attribute." ).type( JsonFieldType.STRING ), - fields.withPath( "trackedEntityIdentifierReference.type" ).description( "The type of reference value of the Tracked Entity Attribute." ).type( JsonFieldType.STRING ), - subsectionWithPath( "_links" ).description( "Links to other resources" ) - ) ) ); + fields.withPath( "containedAllowed" ).description( "Specified if this rule can process contained resources." ).type( JsonFieldType.BOOLEAN ).optional(), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected MappedTrackedEntity loadTrackedEntity( @Nonnull String name ) + { + final MappedTrackedEntity example = new MappedTrackedEntity(); + example.setName( name ); + return mappedTrackedEntityRepository.findOne( Example.of( example, ExampleMatcher.matching().withIgnorePaths( "enabled" ) ) ) + .orElseThrow( () -> new AssertionError( "Mapped tracked entity does not exist: " + name ) ); } @Nonnull diff --git a/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRepositoryRestDocsTest.java b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRepositoryRestDocsTest.java new file mode 100644 index 00000000..35386fbf --- /dev/null +++ b/fhir/src/test/java/org/dhis2/fhir/adapter/fhir/metadata/repository/TrackedEntityRepositoryRestDocsTest.java @@ -0,0 +1,138 @@ +package org.dhis2.fhir.adapter.fhir.metadata.repository; + +/* + * Copyright (c) 2004-2018, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.apache.commons.io.IOUtils; +import org.dhis2.fhir.adapter.fhir.AbstractJpaRepositoryRestDocsTest; +import org.dhis2.fhir.adapter.fhir.ConstrainedFields; +import org.dhis2.fhir.adapter.fhir.metadata.model.MappedTrackedEntity; +import org.dhis2.fhir.adapter.fhir.metadata.model.TrackedEntityRule; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +import javax.annotation.Nonnull; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.attributes; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * Tests for {@link MappedTrackedEntityRepository}. + * + * @author volsch + */ +public class TrackedEntityRepositoryRestDocsTest extends AbstractJpaRepositoryRestDocsTest +{ + @Autowired + private MappedTrackedEntityRepository mappedTrackedEntityRepository; + + @Test + public void createTrackedEntity() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( TrackedEntityRule.class, constraintDescriptionResolver ); + final String request = IOUtils.resourceToString( "/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntity.json", StandardCharsets.UTF_8 ); + final String location = docMockMvc.perform( post( "/api/trackedEntities" ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) + .contentType( MediaType.APPLICATION_JSON ).content( request ) ) + .andExpect( status().isCreated() ) + .andExpect( header().exists( "Location" ) ) + .andDo( documentationHandler.document( requestFields( + attributes( key( "title" ).value( "Fields for tracked entity creation" ) ), + fields.withPath( "name" ).description( "The unique name of the rule." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the rule is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "enabled" ).description( "Specifies if this rule is enabled." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "trackedEntityReference" ).description( "The reference to the DHIS2 Tracked Entity Type." ).type( JsonFieldType.OBJECT ), + fields.withPath( "trackedEntityReference.value" ).description( "The unique ID/code/name of the Tracked Entity Type." ).type( JsonFieldType.STRING ), + fields.withPath( "trackedEntityReference.type" ).description( "The type of reference value of the Tracked Entity Type." ).type( JsonFieldType.STRING ), + fields.withPath( "trackedEntityIdentifierReference" ).description( "The reference to the DHIS2 Tracked Entity Attribute that is used as national identifier." ).type( JsonFieldType.OBJECT ), + fields.withPath( "trackedEntityIdentifierReference.value" ).description( "The unique ID/code/name of the Tracked Entity Attribute." ).type( JsonFieldType.STRING ), + fields.withPath( "trackedEntityIdentifierReference.type" ).description( "The type of reference value of the Tracked Entity Attribute." ).type( JsonFieldType.STRING ) + ) ) ).andReturn().getResponse().getHeader( "Location" ); + + mockMvc + .perform( get( Objects.requireNonNull( location ) ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andExpect( jsonPath( "lastUpdatedBy", is( "2h2maqu827d" ) ) ) + .andExpect( jsonPath( "name", is( "Person Tracked Entity (disabled)" ) ) ) + .andExpect( jsonPath( "enabled", is( false ) ) ) + .andExpect( jsonPath( "trackedEntityReference.value", is( "Obsolete Person" ) ) ) + .andExpect( jsonPath( "trackedEntityReference.type", is( "NAME" ) ) ) + .andExpect( jsonPath( "trackedEntityIdentifierReference.value", is( "National identifier" ) ) ) + .andExpect( jsonPath( "trackedEntityIdentifierReference.type", is( "CODE" ) ) ) + .andExpect( jsonPath( "_links.self.href", is( location ) ) ); + } + + @Test + public void readTrackedEntity() throws Exception + { + final ConstrainedFields fields = new ConstrainedFields( TrackedEntityRule.class, constraintDescriptionResolver ); + final String trackedEntityId = loadTrackedEntity( "Person" ).getId().toString(); + docMockMvc.perform( get( "/api/trackedEntities/{trackedEntityId}", trackedEntityId ).header( AUTHORIZATION_HEADER_NAME, DATA_MAPPING_AUTHORIZATION_HEADER_VALUE ) ) + .andExpect( status().isOk() ) + .andDo( documentationHandler.document( links( + linkWithRel( "self" ).description( "Link to this resource itself." ), + linkWithRel( "trackedEntity" ).description( "Link to this resource itself." ) ), + responseFields( + attributes( key( "title" ).value( "Fields for tracked entity reading" ) ), + fields.withPath( "createdAt" ).description( "The timestamp when the resource has been created." ).type( JsonFieldType.STRING ), + fields.withPath( "lastUpdatedBy" ).description( "The ID of the user that has updated the user the last time or null if the data has been imported to the database directly." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "lastUpdatedAt" ).description( "The timestamp when the resource has been updated the last time." ).type( JsonFieldType.STRING ), + fields.withPath( "name" ).description( "The unique name of the rule." ).type( JsonFieldType.STRING ), + fields.withPath( "description" ).description( "The detailed description that describes for which purpose the rule is used." ).type( JsonFieldType.STRING ).optional(), + fields.withPath( "enabled" ).description( "Specifies if this rule is enabled." ).type( JsonFieldType.BOOLEAN ), + fields.withPath( "trackedEntityReference" ).description( "The reference to the DHIS2 Tracked Entity Type." ).type( JsonFieldType.OBJECT ), + fields.withPath( "trackedEntityReference.value" ).description( "The unique ID/code/name of the Tracked Entity Type." ).type( JsonFieldType.STRING ), + fields.withPath( "trackedEntityReference.type" ).description( "The type of reference value of the Tracked Entity Type." ).type( JsonFieldType.STRING ), + fields.withPath( "trackedEntityIdentifierReference" ).description( "The reference to the DHIS2 Tracked Entity Attribute that is used as national identifier." ).type( JsonFieldType.OBJECT ), + fields.withPath( "trackedEntityIdentifierReference.value" ).description( "The unique ID/code/name of the Tracked Entity Attribute." ).type( JsonFieldType.STRING ), + fields.withPath( "trackedEntityIdentifierReference.type" ).description( "The type of reference value of the Tracked Entity Attribute." ).type( JsonFieldType.STRING ), + subsectionWithPath( "_links" ).description( "Links to other resources" ) + ) ) ); + } + + @Nonnull + protected MappedTrackedEntity loadTrackedEntity( @Nonnull String name ) + { + final MappedTrackedEntity example = new MappedTrackedEntity(); + example.setName( name ); + return mappedTrackedEntityRepository.findOne( Example.of( example, ExampleMatcher.matching().withIgnorePaths( "enabled" ) ) ) + .orElseThrow( () -> new AssertionError( "Mapped tracked entity does not exist: " + name ) ); + } +} \ No newline at end of file diff --git a/fhir/src/test/resources/data.sql b/fhir/src/test/resources/data.sql index b69092a7..5701f1d3 100644 --- a/fhir/src/test/resources/data.sql +++ b/fhir/src/test/resources/data.sql @@ -90,8 +90,8 @@ VALUES ('73cd99c50ca842ada53b1891fccce08f', 0, CURRENT_TIMESTAMP(), CURRENT_TIME INSERT INTO fhir_remote_subscription_header (remote_subscription_id, name, value, secure) VALUES ('73cd99c50ca842ada53b1891fccce08f', 'Authorization', 'Bearer jshru38jsHdsdfy38sh38H3d', TRUE); -INSERT INTO fhir_remote_subscription_resource (id, version, created_at, last_updated_at, last_updated_by, remote_subscription_id, fhir_resource_type, fhir_criteria_parameters, description) -VALUES ('667bfa41867c479686b6eb9f9ed4dc94', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', '73cd99c50ca842ada53b1891fccce08f', 'PATIENT', '_format=json', 'Subscription for all Patients.'); +INSERT INTO fhir_remote_subscription_resource (id, version, created_at, last_updated_at, last_updated_by, remote_subscription_id, fhir_resource_type, fhir_criteria_parameters, description, virtual) +VALUES ('667bfa41867c479686b6eb9f9ed4dc94', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', '73cd99c50ca842ada53b1891fccce08f', 'PATIENT', '_format=json', 'Subscription for all Patients.', false); INSERT INTO fhir_remote_subscription_system (id, version, created_at, last_updated_at, last_updated_by, remote_subscription_id, fhir_resource_type, system_id) VALUES ('ea9804a39e824d0d9cd2e417b32b1c0c', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', '73cd99c50ca842ada53b1891fccce08f', 'ORGANIZATION', 'c4e9ac6acc8f4c73aab60fa6775c0ca3'); @@ -175,9 +175,13 @@ VALUES ('72451c8f7492470790b8a3e0796de19e', 0, CURRENT_TIMESTAMP(), CURRENT_TIME 'Transforms FHIR Patient to DHIS Person', 'TRANSFORM_FHIR_PATIENT_DHIS_PERSON', 'Transforms FHIR Patient to DHIS Person.'); INSERT INTO fhir_executable_script_argument(id, executable_script_id, script_argument_id, override_value, enabled) VALUES ('9b832b2c0a574441841147b5dc65ec91', '72451c8f7492470790b8a3e0796de19e', '90b3c11038e44291934ce2569e8af1ba', 'CODE:MMD_PER_DOB', TRUE); - -INSERT INTO fhir_rule (id, version, created_at, last_updated_at, last_updated_by, name, description, enabled, evaluation_order, fhir_resource_type, dhis_resource_type, applicable_in_script_id, transform_in_script_id) -VALUES ('5f9ebdc9852e4c8387ca795946aabc35', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'FHIR Patient to Person', NULL, TRUE, 1, 'PATIENT', 'TRACKED_ENTITY', NULL, '72451c8f7492470790b8a3e0796de19e'); -INSERT INTO fhir_tracked_entity_rule (id, tracked_entity_ref, org_lookup_script_id, loc_lookup_script_id, tracked_entity_identifier_ref) -VALUES ('5f9ebdc9852e4c8387ca795946aabc35', 'NAME:Person', '25a97bb47b394ed48677db4bcaa28ccf', 'ef90531f443848bd83b36370dd65875a', 'CODE:National identifier'); + +-- Tracked Entity Person +INSERT INTO fhir_tracked_entity(id, version, created_at, last_updated_at, last_updated_by, name, enabled, description, tracked_entity_ref, tracked_entity_identifier_ref) +VALUES ('4203754d21774a4486aa2de31ee4c8ee', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'Person', TRUE, 'Tracked entity for a patient.', 'NAME:Person', 'CODE:National identifier'); + +INSERT INTO fhir_rule (id, version, created_at, last_updated_at, last_updated_by, name, description, enabled, evaluation_order, fhir_resource_type, dhis_resource_type, applicable_in_script_id, transform_in_script_id, contained_allowed) +VALUES ('5f9ebdc9852e4c8387ca795946aabc35', 0, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP(), '2h2maqu827d', 'FHIR Patient to Person', NULL, TRUE, 1, 'PATIENT', 'TRACKED_ENTITY', NULL, '72451c8f7492470790b8a3e0796de19e', FALSE); +INSERT INTO fhir_tracked_entity_rule (id, tracked_entity_id, org_lookup_script_id, loc_lookup_script_id) +VALUES ('5f9ebdc9852e4c8387ca795946aabc35', '4203754d21774a4486aa2de31ee4c8ee', '25a97bb47b394ed48677db4bcaa28ccf', 'ef90531f443848bd83b36370dd65875a'); diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntity.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntity.json new file mode 100644 index 00000000..2f976c03 --- /dev/null +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntity.json @@ -0,0 +1,13 @@ +{ + "name": "Person Tracked Entity (disabled)", + "description": "Transforms a FHIR Patient to a Person (obsolete).", + "enabled": false, + "trackedEntityReference": { + "value": "Obsolete Person", + "type": "NAME" + }, + "trackedEntityIdentifierReference": { + "value": "National identifier", + "type": "CODE" + } +} diff --git a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntityRule.json b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntityRule.json index 0257c559..440ec429 100644 --- a/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntityRule.json +++ b/fhir/src/test/resources/org/dhis2/fhir/adapter/fhir/metadata/repository/createTrackedEntityRule.json @@ -5,16 +5,9 @@ "evaluationOrder": 0, "dhisResourceType": "TRACKED_ENTITY", "fhirResourceType": "PATIENT", - "trackedEntityReference": { - "value": "Person", - "type": "NAME" - }, - "trackedEntityIdentifierReference": { - "value": "National identifier", - "type": "CODE" - }, "applicableInScript": null, "applicableCodeSet": null, + "trackedEntity": "$trackedEntityId", "transformInScript": "$transformInScriptId", "orgUnitLookupScript": "$orgUnitLookupScriptId", "locationLookupScript": "$locationLookupScriptId"