Skip to content

Commit

Permalink
Added WHO tracked entity type and its attributes.
Browse files Browse the repository at this point in the history
  • Loading branch information
volsch committed Nov 16, 2018
1 parent 8487761 commit 9df9f91
Show file tree
Hide file tree
Showing 80 changed files with 3,491 additions and 310 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<dhis2.username>admin</dhis2.username>
<dhis2.password>district</dhis2.password>
<dhis2.url>http://localhost:8080</dhis2.url>
<dhis2.apiVersion>29</dhis2.apiVersion>
<dhis2.apiVersion>30</dhis2.apiVersion>
</properties>

<dependencies>
Expand Down
71 changes: 70 additions & 1 deletion app/src/main/java/org/dhis2/fhir/adapter/DemoClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
Expand Down Expand Up @@ -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)
//////////////////////////////////
Expand All @@ -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 )
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
118 changes: 118 additions & 0 deletions app/src/main/java/org/dhis2/fhir/adapter/setup/ReferenceSetup.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
17 changes: 17 additions & 0 deletions app/src/main/java/org/dhis2/fhir/adapter/setup/Setup.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
*/

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
Expand All @@ -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;
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() )
{
Expand Down
Loading

0 comments on commit 9df9f91

Please sign in to comment.