From 7c3f203884a7fe1f92385fd762a61d7b33b47fc7 Mon Sep 17 00:00:00 2001 From: David Katuscak Date: Fri, 9 Aug 2019 14:07:20 +0200 Subject: [PATCH 1/2] PeriodUtils and not complete MeasureReport implementation for R4 D2INT-36 --- .../dhis2/fhir/adapter/util/PeriodUtils.java | 221 ++++++++++++++++++ .../adapter/dhis/aggregate/DataValueSet.java | 3 +- .../sync/impl/DhisResourceRepositoryImpl.java | 38 +-- .../r4/R4MeasureResourceProvider.java | 16 +- ...easureReportToDataValueSetTransformer.java | 212 +++++++++++++++++ .../fhir/metadata/model/DataValueSetRule.java | 17 +- ...easureReportToDataValueSetTranformer.java} | 100 ++++---- 7 files changed, 509 insertions(+), 98 deletions(-) create mode 100644 common/src/main/java/org/dhis2/fhir/adapter/util/PeriodUtils.java rename fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/dstu3/Dstu3MeasureReportResourceProvider.java => fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/r4/R4MeasureResourceProvider.java (84%) create mode 100644 fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/aggregate/r4/R4FhirMeasureReportToDataValueSetTransformer.java rename fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/{FhirToDataValueSetTransformer.java => AbstractFhirMeasureReportToDataValueSetTranformer.java} (65%) diff --git a/common/src/main/java/org/dhis2/fhir/adapter/util/PeriodUtils.java b/common/src/main/java/org/dhis2/fhir/adapter/util/PeriodUtils.java new file mode 100644 index 00000000..def9778d --- /dev/null +++ b/common/src/main/java/org/dhis2/fhir/adapter/util/PeriodUtils.java @@ -0,0 +1,221 @@ +package org.dhis2.fhir.adapter.util; + +/* + * Copyright (c) 2004-2019, 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.util.StringUtils; + +import java.time.DayOfWeek; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.WeekFields; +import java.util.Date; + +/** + * @author David Katuscak + */ +public class PeriodUtils +{ + private static final String BI_MONTHLY_SUFFIX = "B"; + private static final String SIX_MONTH_ONE_SUFFIX = "S1"; + private static final String SIX_MONTH_TWO_SUFFIX = "S2"; + private static final String Q1_SUFFIX = "Q1"; + private static final String Q2_SUFFIX = "Q2"; + private static final String Q3_SUFFIX = "Q3"; + private static final String Q4_SUFFIX = "Q4"; + private static final String BI_WEEK_PREFIX = "Bi"; + private static final String WEEK_PREFIX = "W"; + + private static final ZoneId zoneId = ZoneId.systemDefault(); + + public static String getDHIS2PeriodString( Date periodStart, Date periodEnd ) + { + ZonedDateTime startDate = ZonedDateTime.ofInstant( periodStart.toInstant(), zoneId ); + ZonedDateTime endDate = ZonedDateTime.ofInstant( periodEnd.toInstant(), zoneId ); + + Period period = Period.between( startDate.toLocalDate(), endDate.toLocalDate() ); + + int totalMonths = period.getMonths(); + int month = startDate.getMonthValue(); + + String periodString = String.valueOf( startDate.getYear() ); + + //Period is year + if ( totalMonths == 12 ) + { + return getYearPeriod( startDate, month, periodString ); + } + //Period is six-month + else if ( totalMonths == 6 ) + { + return getSixMonthPeriod( startDate, month, periodString ); + } + else if ( totalMonths == 3 ) + { + return getQuarterPeriod( startDate, month, periodString ); + } + else if ( totalMonths == 2 ) + { + return getBiMonthPeriod( startDate, month, periodString ); + } + else if ( period.getMonths() == 1 ) + { + return getMonthPeriod( month, periodString ); + } + else if ( period.getMonths() == 0 ) + { + if ( period.getDays() == 14 || period.getDays() == 7 ) + { + return getWeekAndBiWeekPeriod( startDate, period, periodString ); + } + else if ( period.getDays() == 1 ) + { + return startDate.toLocalDate().format( DateTimeFormatter.BASIC_ISO_DATE ); + } + } + + throw new IllegalArgumentException( "Provided dates ('" + periodStart + "', '" + periodEnd + + "' ) do not represent a valid DHIS2 Period. " ); + } + + private static String getWeekAndBiWeekPeriod( ZonedDateTime startDate, Period period, String periodString ) + { + int weekNumber = startDate.toLocalDate().get( WeekFields.ISO.weekOfWeekBasedYear() ); + DayOfWeek dayOfWeek = startDate.getDayOfWeek(); + if ( period.getDays() == 14 ) + { + if ( (weekNumber % 2 ) == 1 ) + { + return periodString + BI_WEEK_PREFIX + WEEK_PREFIX + weekNumber; + } + + throw new IllegalArgumentException( "Bi-weekly period cannot start in odd week: " + startDate ); + } + else + { + if ( dayOfWeek == DayOfWeek.MONDAY ) + { + return periodString + WEEK_PREFIX + weekNumber; + } + else + { + String dayOfWeekShort = StringUtils.capitalize( dayOfWeek.name().substring( 0, 3 ) ); + return periodString + dayOfWeekShort + WEEK_PREFIX + weekNumber; + } + } + } + + private static String getYearPeriod( ZonedDateTime startDate, int month, String periodString ) + { + //Regular year. Starts in January + if ( month == 1 ) + { + return periodString; + } + //Financial year that starts in other month than January + else + { + return periodString + StringUtils.capitalize( startDate.getMonth().name().toLowerCase() ); + } + } + + private static String getSixMonthPeriod( ZonedDateTime startDate, int month, String periodString ) + { + if ( month == 1 ) + { + return periodString + SIX_MONTH_ONE_SUFFIX; + } + else if ( month == 7 ) + { + return periodString + SIX_MONTH_TWO_SUFFIX; + } + else if ( month > 1 && month < 7 ) + { + return periodString + StringUtils.capitalize( startDate.getMonth().name().toLowerCase() ) + + SIX_MONTH_ONE_SUFFIX; + } + else + { + return periodString + StringUtils.capitalize( startDate.getMonth().name().toLowerCase() ) + + SIX_MONTH_TWO_SUFFIX; + } + } + + private static String getQuarterPeriod( ZonedDateTime startDate, int month, String periodString ) + { + if ( ( startDate.getMonthValue() % 3 ) == 1 ) + { + if ( month == 1 ) + { + return periodString + Q1_SUFFIX; + } + else if ( month == 4 ) + { + return periodString + Q2_SUFFIX; + } + else if ( month == 7 ) + { + return periodString + Q3_SUFFIX; + } + else + { + return periodString + Q4_SUFFIX; + } + } + else + { + throw new IllegalArgumentException( "Quarter period cannot start in month: " + startDate.getMonth().name() ); + } + } + + private static String getBiMonthPeriod( ZonedDateTime startDate, int month, String periodString ) + { + if ( ( startDate.getMonthValue() % 2 ) == 1 ) + { + return getMonthPeriod( month, periodString ) + BI_MONTHLY_SUFFIX; + } + else + { + throw new IllegalArgumentException( "Bi-month period cannot start in month: " + startDate.getMonth().name() ); + } + } + + private static String getMonthPeriod( int month, String periodString ) + { + if ( month < 10 ) + { + return periodString + "0" + month; + } + else + { + return periodString + month; + } + } +} diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSet.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSet.java index 5d22a0e7..a7dfb729 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSet.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/aggregate/DataValueSet.java @@ -102,8 +102,9 @@ public DataValueSet( @Nonnull String id ) this.id = id; } - public DataValueSet( boolean newResource ) + public DataValueSet( @Nonnull String id, boolean newResource ) { + this.id = id; this.newResource = newResource; this.modified = newResource; this.local = newResource; diff --git a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java index 24c14251..313f83c4 100644 --- a/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java +++ b/dhis/src/main/java/org/dhis2/fhir/adapter/dhis/sync/impl/DhisResourceRepositoryImpl.java @@ -280,36 +280,20 @@ else if ( enrollment.isModified() ) private boolean saveDataValueSet( @Nonnull DataValueSet dataValueSet ) { - if ( validateDataValueSet( dataValueSet ) ) + if ( dataValueSet.isNewResource() ) { - if ( dataValueSet.isNewResource() ) - { - logger.info( "Creating new DataValueSet." ); - dataValueSetService.createOrUpdate( dataValueSet ); - logger.info( "Created new DataValueSet for dataSetId: {}, orgUnit: {}, period: {}.", - dataValueSet.getDataSetId(), dataValueSet.getOrgUnitId(), dataValueSet.getPeriod() ); - return true; - } - else if ( dataValueSet.isModified() ) - { - logger.info( "Updating existing DataValueSet." ); - dataValueSetService.createOrUpdate( dataValueSet ); - logger.info( "Created new DataValueSet for dataSetId: {}, orgUnit: {}, period: {}.", - dataValueSet.getDataSetId(), dataValueSet.getOrgUnitId(), dataValueSet.getPeriod() ); - return true; - } + logger.info( "Creating new DataValueSet." ); + dataValueSetService.createOrUpdate( dataValueSet ); + logger.info( "Created new DataValueSet for dataSetId: {}, orgUnit: {}, period: {}.", + dataValueSet.getDataSetId(), dataValueSet.getOrgUnitId(), dataValueSet.getPeriod() ); + return true; } - - return false; - } - - private boolean validateDataValueSet( @Nonnull DataValueSet dataValueSet ) - { - if ( dataValueSet.getDataSetId() != null && !dataValueSet.getDataSetId().isEmpty() && - dataValueSet.getOrgUnitId() != null && !dataValueSet.getOrgUnitId().isEmpty() && - dataValueSet.getPeriod() != null && !dataValueSet.getPeriod().isEmpty() && - dataValueSet.getDataValues() != null && !dataValueSet.getDataValues().isEmpty() ) + else if ( dataValueSet.isModified() ) { + logger.info( "Updating existing DataValueSet." ); + dataValueSetService.createOrUpdate( dataValueSet ); + logger.info( "Created new DataValueSet for dataSetId: {}, orgUnit: {}, period: {}.", + dataValueSet.getDataSetId(), dataValueSet.getOrgUnitId(), dataValueSet.getPeriod() ); return true; } diff --git a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/dstu3/Dstu3MeasureReportResourceProvider.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/r4/R4MeasureResourceProvider.java similarity index 84% rename from fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/dstu3/Dstu3MeasureReportResourceProvider.java rename to fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/r4/R4MeasureResourceProvider.java index 7ee3bc84..edd90a81 100644 --- a/fhir-dstu3/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/dstu3/Dstu3MeasureReportResourceProvider.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/server/provider/r4/R4MeasureResourceProvider.java @@ -1,4 +1,4 @@ -package org.dhis2.fhir.adapter.fhir.server.provider.dstu3; +package org.dhis2.fhir.adapter.fhir.server.provider.r4; /* * Copyright (c) 2004-2019, University of Oslo @@ -41,8 +41,7 @@ import org.dhis2.fhir.adapter.fhir.repository.DhisRepository; import org.dhis2.fhir.adapter.fhir.repository.FhirRepository; import org.dhis2.fhir.adapter.fhir.server.provider.AbstractReadWriteResourceProvider; -import org.hl7.fhir.dstu3.model.MeasureReport; -import org.springframework.stereotype.Component; +import org.hl7.fhir.r4.model.Measure; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -50,25 +49,24 @@ import java.util.Map; /** - * DSTU3 resource provider + * r4 resource provider * * @author David Katuscak */ -@Component -public class Dstu3MeasureReportResourceProvider extends AbstractReadWriteResourceProvider +public class R4MeasureResourceProvider extends AbstractReadWriteResourceProvider { - public Dstu3MeasureReportResourceProvider( @Nonnull FhirClientResourceRepository fhirClientResourceRepository, + public R4MeasureResourceProvider( @Nonnull FhirClientResourceRepository fhirClientResourceRepository, @Nonnull FhirClientSystemRepository fhirClientSystemRepository, @Nonnull FhirRepository fhirRepository, @Nonnull DhisRepository dhisRepository ) { - super( MeasureReport.class, fhirClientResourceRepository, fhirClientSystemRepository, fhirRepository, dhisRepository ); + super( Measure.class, fhirClientResourceRepository, fhirClientSystemRepository, fhirRepository, dhisRepository ); } @Nonnull @Override public FhirVersion getFhirVersion() { - return FhirVersion.DSTU3; + return FhirVersion.R4; } @Search( allowUnknownParams = true ) diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/aggregate/r4/R4FhirMeasureReportToDataValueSetTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/aggregate/r4/R4FhirMeasureReportToDataValueSetTransformer.java new file mode 100644 index 00000000..468ab64c --- /dev/null +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/aggregate/r4/R4FhirMeasureReportToDataValueSetTransformer.java @@ -0,0 +1,212 @@ +package org.dhis2.fhir.adapter.fhir.transform.dhis.impl.aggregate.r4; + +/* + * Copyright (c) 2004-2019, 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.aggregate.DataValueSet; +import org.dhis2.fhir.adapter.dhis.converter.ValueConverter; +import org.dhis2.fhir.adapter.dhis.model.WritableDataValue; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnit; +import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; +import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; +import org.dhis2.fhir.adapter.fhir.metadata.model.DataValueSetRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClientResource; +import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; +import org.dhis2.fhir.adapter.fhir.metadata.model.ScriptVariable; +import org.dhis2.fhir.adapter.fhir.model.FhirVersion; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; +import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.TransformerException; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformOutcome; +import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; +import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.aggregate.AbstractFhirMeasureReportToDataValueSetTranformer; +import org.dhis2.fhir.adapter.fhir.transform.util.TransformerUtils; +import org.dhis2.fhir.adapter.util.PeriodUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.MeasureReport; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.math.BigDecimal; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * R4 specific version of FHIR MeasureReport to DHIS2 DataValueSet transformer. + * + * @author David Katuscak + */ + +@Component +public class R4FhirMeasureReportToDataValueSetTransformer extends AbstractFhirMeasureReportToDataValueSetTranformer +{ + private final ZoneId zoneId = ZoneId.systemDefault(); + + public R4FhirMeasureReportToDataValueSetTransformer( @Nonnull final ScriptExecutor scriptExecutor, + @Nonnull final TrackedEntityMetadataService trackedEntityMetadataService, + @Nonnull final OrganizationUnitService organizationUnitService, + @Nonnull final TrackedEntityService trackedEntityService, + @Nonnull final FhirDhisAssignmentRepository fhirDhisAssignmentRepository, + @Nonnull ScriptExecutionContext scriptExecutionContext, + @Nonnull ValueConverter valueConverter ) + { + super( scriptExecutor, trackedEntityMetadataService, organizationUnitService, + trackedEntityService, fhirDhisAssignmentRepository, scriptExecutionContext, + valueConverter ); + } + + @Nonnull + @Override + public Set getFhirVersions() + { + return FhirVersion.R4_ONLY; + } + + @Override + public FhirToDhisTransformOutcome transformInternal( @Nonnull final FhirClientResource fhirClientResource, + @Nonnull final FhirToDhisTransformerContext context, @Nonnull final IBaseResource input, + @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) + throws TransformerException + { + final Map variables = new HashMap<>( scriptVariables ); + + final DataValueSet dataValueSet = getResource( fhirClientResource, context, ruleInfo, scriptVariables ).orElse( null ); + if ( dataValueSet == null ) + { + return null; + } + + final MeasureReport mr = (MeasureReport) input; + + final Optional organizationUnit; + if ( ruleInfo.getRule().getOrgUnitLookupScript() == null ) + { + logger.info( "Rule does not define an organization unit lookup script and data value set does not yet include one." ); + return null; + } + else + { + organizationUnit = getOrgUnit( context, ruleInfo, ruleInfo.getRule().getOrgUnitLookupScript(), variables ); + organizationUnit.ifPresent( ou -> dataValueSet.setOrgUnitId( ou.getId() ) ); + } + + if ( !organizationUnit.isPresent() ) + { + return null; + } + + //------------------------------ + if ( ruleInfo.getRule().getDataSetIdLookupScript() == null ) + { + logger.info( "Rule does not define a data value set lookup script and data value set does not yet include one." ); + return null; + } + else + { + Optional dataSetId = getDataSetId( context, ruleInfo, ruleInfo.getRule().getDataSetIdLookupScript(), variables ); + dataSetId.ifPresent( dataValueSet::setDataSetId ); + } + + if ( dataValueSet.getDataSetId() == null ) + { + return null; + } + //------------------------------ + + ZonedDateTime lastUpdated = ZonedDateTime.ofInstant( mr.getMeta().getLastUpdated().toInstant(), zoneId ); + // String version = mr.getMeta().getVersionId(); + + dataValueSet.setId( createArtificialDataValueSetId( mr ) ); + dataValueSet.setLastUpdated( lastUpdated ); + dataValueSet.setPeriod( PeriodUtils.getDHIS2PeriodString( mr.getPeriod().getStart(), mr.getPeriod().getEnd() ) ); + transformDataValues( mr, dataValueSet, lastUpdated ); + + return new FhirToDhisTransformOutcome<>( ruleInfo.getRule(), dataValueSet, dataValueSet.isNewResource() ); + } + + private String createArtificialDataValueSetId( MeasureReport measureReport ) + { + String measureUuid = measureReport.getMeasure().substring( 8 ); + String locationId = measureReport.getReporter().getReference().substring( 9 ); + String period = PeriodUtils.getDHIS2PeriodString( measureReport.getPeriod().getStart(), measureReport.getPeriod().getEnd() ); + + //DataValueSet doesn't use IDs. Therefore, creating the artificial one only for the Adapter purposes. It consists of: + //measure UUID (equivalent of DHIS2 DataSetId), location UUID (equivalent of DHIS2 OrgUnitUID) and period in DHIS2 format + return measureUuid + ":" + locationId + ":" + period; + } + + private void transformDataValues( MeasureReport measureReport, DataValueSet dataValueSet, ZonedDateTime lastUpdated ) + { + for ( int i = 1; i < measureReport.getGroup().size(); i++ ) + { + MeasureReport.MeasureReportGroupComponent group = measureReport.getGroup().get( i ); + + String dataElementCode = group.getCode().getText(); + BigDecimal dataValueNumeric = group.getMeasureScore().getValue(); + + WritableDataValue dv = new WritableDataValue(); + //Today, the DataElement code is used. Therefore, a `dataElementIdScheme=CODE` parameter has to be used when sending a request + //In the future a better mapping, probably some DataElementService should be provided. + dv.setDataElementId( dataElementCode ); + dv.setValue( dataValueNumeric.toString() ); + dv.setModified(); + dv.setNewResource( true ); + dv.setProvidedElsewhere( false ); + dv.setLastUpdated( lastUpdated ); + + dataValueSet.getDataValues().add( dv ); + } + } + + @Nullable + @Override + protected DataValueSet createResource( @Nonnull final FhirToDhisTransformerContext context, + @Nonnull final RuleInfo ruleInfo, @Nullable final String id, + @Nonnull final Map scriptVariables, final boolean sync, final boolean refreshed ) + throws TransformerException + { + if ( context.isCreationDisabled() ) + { + return null; + } + + final MeasureReport measureReport = (MeasureReport) TransformerUtils + .getScriptVariable( scriptVariables, ScriptVariable.INPUT, IBaseResource.class ); + + String identifier = createArtificialDataValueSetId( measureReport ); + + return new DataValueSet( identifier, true ); + } +} diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataValueSetRule.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataValueSetRule.java index 8289d5a1..b94ab9ea 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataValueSetRule.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/metadata/model/DataValueSetRule.java @@ -55,18 +55,15 @@ public class DataValueSetRule extends AbstractRule { private static final long serialVersionUID = 5463810804987445631L; - //TODO: Not sure about scripts yet private ExecutableScript orgUnitLookupScript; - private ExecutableScript locationLookupScript; + private ExecutableScript dataSetIdLookupScript; public DataValueSetRule() { super( DhisResourceType.DATA_VALUE_SET ); } - //TODO: Not sure about scripts yet - BEGIN - @JsonCacheId @ManyToOne @JoinColumn( name = "org_lookup_script_id" ) @@ -82,19 +79,17 @@ public void setOrgUnitLookupScript( ExecutableScript orgUnitLookupScript ) @JsonCacheId @ManyToOne - @JoinColumn( name = "loc_lookup_script_id" ) - public ExecutableScript getLocationLookupScript() + @JoinColumn( name = "data_set_lookup_script_id" ) + public ExecutableScript getDataSetIdLookupScript() { - return locationLookupScript; + return dataSetIdLookupScript; } - public void setLocationLookupScript( ExecutableScript locationLookupScript ) + public void setDataSetIdLookupScript( ExecutableScript dataSetIdLookupScript ) { - this.locationLookupScript = locationLookupScript; + this.dataSetIdLookupScript = dataSetIdLookupScript; } - //Not sure about scripts yet - END - @Override @Transient @JsonIgnore diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/FhirToDataValueSetTransformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/AbstractFhirMeasureReportToDataValueSetTranformer.java similarity index 65% rename from fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/FhirToDataValueSetTransformer.java rename to fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/AbstractFhirMeasureReportToDataValueSetTranformer.java index 64f2bb43..6906fbc8 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/FhirToDataValueSetTransformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/AbstractFhirMeasureReportToDataValueSetTranformer.java @@ -29,15 +29,17 @@ */ import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSet; -import org.dhis2.fhir.adapter.dhis.aggregate.DataValueSetService; 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.orgunit.OrganizationUnitService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; +import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; import org.dhis2.fhir.adapter.fhir.data.repository.FhirDhisAssignmentRepository; import org.dhis2.fhir.adapter.fhir.metadata.model.DataValueSetRule; +import org.dhis2.fhir.adapter.fhir.metadata.model.ExecutableScript; import org.dhis2.fhir.adapter.fhir.metadata.model.FhirClientResource; import org.dhis2.fhir.adapter.fhir.metadata.model.RuleInfo; -import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.repository.DhisFhirResourceId; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; @@ -48,33 +50,25 @@ import org.dhis2.fhir.adapter.fhir.transform.fhir.impl.AbstractFhirToDhisTransformer; import org.dhis2.fhir.adapter.spring.StaticObjectProvider; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.stereotype.Component; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Map; import java.util.Optional; -import java.util.Set; -/** - * @author David Katuscak - */ -@Component -public class FhirToDataValueSetTransformer extends AbstractFhirToDhisTransformer +public abstract class AbstractFhirMeasureReportToDataValueSetTranformer extends AbstractFhirToDhisTransformer { - private final DataValueSetService dataValueSetService; - - public FhirToDataValueSetTransformer( - @Nonnull ScriptExecutor scriptExecutor, - @Nonnull OrganizationUnitService organizationUnitService, - @Nonnull FhirDhisAssignmentRepository fhirDhisAssignmentRepository, - @Nonnull DataValueSetService dataValueSetService, - @Nonnull ScriptExecutionContext scriptExecutionContext, @Nonnull ValueConverter valueConverter ) + public AbstractFhirMeasureReportToDataValueSetTranformer( @Nonnull final ScriptExecutor scriptExecutor, + @Nonnull final TrackedEntityMetadataService trackedEntityMetadataService, + @Nonnull final OrganizationUnitService organizationUnitService, + @Nonnull final TrackedEntityService trackedEntityService, + @Nonnull final FhirDhisAssignmentRepository fhirDhisAssignmentRepository, + @Nonnull ScriptExecutionContext scriptExecutionContext, + @Nonnull ValueConverter valueConverter) { - super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( null ), new StaticObjectProvider<>( null ), - fhirDhisAssignmentRepository, scriptExecutionContext, valueConverter ); - - this.dataValueSetService = dataValueSetService; + super( scriptExecutor, organizationUnitService, new StaticObjectProvider<>( trackedEntityMetadataService ), + new StaticObjectProvider<>( trackedEntityService ), fhirDhisAssignmentRepository, scriptExecutionContext, + valueConverter ); } @Nonnull @@ -98,13 +92,6 @@ public Class getRuleClass() return DataValueSetRule.class; } - @Nonnull - @Override - public Set getFhirVersions() - { - return FhirVersion.ALL; - } - @Nonnull @Override protected Optional getResourceById( @Nullable final String id ) @@ -137,19 +124,20 @@ protected boolean isAlwaysActiveResource( @Nonnull final RuleInfo ruleInfo, @Nullable final String id, - @Nonnull final Map scriptVariables, final boolean sync, final boolean refreshed ) + protected boolean isSyncRequired( @Nonnull final FhirToDhisTransformerContext context, + @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) throws TransformerException { - if ( context.isCreationDisabled() ) - { - return null; - } + return context.getFhirRequest().isSync(); + } - //TODO: + @Nullable + @Override + public FhirToDhisDeleteTransformOutcome transformDeletion( + @Nonnull final FhirClientResource fhirClientResource, @Nonnull final RuleInfo ruleInfo, + @Nonnull final DhisFhirResourceId dhisFhirResourceId ) throws TransformerException + { return null; } @@ -160,24 +148,36 @@ public FhirToDhisTransformOutcome transform( @Nonnull final FhirCl @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) throws TransformerException { - //TODO: - return null; + return transformInternal( fhirClientResource, context, input, ruleInfo, scriptVariables ); } - @Nullable - @Override - public FhirToDhisDeleteTransformOutcome transformDeletion( - @Nonnull final FhirClientResource fhirClientResource, @Nonnull final RuleInfo ruleInfo, - @Nonnull final DhisFhirResourceId dhisFhirResourceId ) throws TransformerException + abstract protected FhirToDhisTransformOutcome transformInternal( @Nonnull final FhirClientResource fhirClientResource, + @Nonnull final FhirToDhisTransformerContext context, @Nonnull final IBaseResource input, + @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) throws TransformerException; + + @Nonnull + protected Optional getDataSetId( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull ExecutableScript lookupScript, @Nonnull Map scriptVariables ) { - return null; + //TODO: I need to figure out how to obtain DataSetId -> It should be done in similar way as it is done for OrgUnits + final Reference orgUnitReference = executeScript( context, ruleInfo, lookupScript, scriptVariables, Reference.class ); +// if ( orgUnitReference == null ) +// { +// logger.info( "Could not extract organization unit reference." ); +// return Optional.empty(); +// } + return getDataSetId( context, orgUnitReference, scriptVariables ); } - @Override - protected boolean isSyncRequired( @Nonnull final FhirToDhisTransformerContext context, - @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) - throws TransformerException + @Nonnull + protected Optional getDataSetId( @Nonnull FhirToDhisTransformerContext context, @Nonnull Reference orgUnitReference, @Nonnull Map scriptVariables ) { - return context.getFhirRequest().isSync(); +// final Optional organisationUnit = organizationUnitService.findMetadataByReference( orgUnitReference ); +// if ( !organisationUnit.isPresent() ) +// { +// logger.info( "Organization unit of reference does not exist: " + orgUnitReference ); +// } +// return organisationUnit; + + return Optional.empty(); } } From 83dffb881772c2d40c17194b6fcf1539d169d897 Mon Sep 17 00:00:00 2001 From: David Katuscak Date: Tue, 13 Aug 2019 14:32:32 +0200 Subject: [PATCH 2/2] Fix issues mentioned in QA comments --- .../dhis2/fhir/adapter/util/PeriodUtils.java | 3 +- ...easureReportToDataValueSetTransformer.java | 70 +++++++++---------- ...MeasureReportToDataValueSetTranformer.java | 33 ++++++++- 3 files changed, 66 insertions(+), 40 deletions(-) diff --git a/common/src/main/java/org/dhis2/fhir/adapter/util/PeriodUtils.java b/common/src/main/java/org/dhis2/fhir/adapter/util/PeriodUtils.java index def9778d..85e0925c 100644 --- a/common/src/main/java/org/dhis2/fhir/adapter/util/PeriodUtils.java +++ b/common/src/main/java/org/dhis2/fhir/adapter/util/PeriodUtils.java @@ -30,6 +30,7 @@ import org.springframework.util.StringUtils; +import javax.annotation.Nonnull; import java.time.DayOfWeek; import java.time.Period; import java.time.ZoneId; @@ -55,7 +56,7 @@ public class PeriodUtils private static final ZoneId zoneId = ZoneId.systemDefault(); - public static String getDHIS2PeriodString( Date periodStart, Date periodEnd ) + public static String getDHIS2PeriodString( @Nonnull Date periodStart, @Nonnull Date periodEnd ) { ZonedDateTime startDate = ZonedDateTime.ofInstant( periodStart.toInstant(), zoneId ); ZonedDateTime endDate = ZonedDateTime.ofInstant( periodEnd.toInstant(), zoneId ); diff --git a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/aggregate/r4/R4FhirMeasureReportToDataValueSetTransformer.java b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/aggregate/r4/R4FhirMeasureReportToDataValueSetTransformer.java index 468ab64c..63f23407 100644 --- a/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/aggregate/r4/R4FhirMeasureReportToDataValueSetTransformer.java +++ b/fhir-r4/src/main/java/org/dhis2/fhir/adapter/fhir/transform/dhis/impl/aggregate/r4/R4FhirMeasureReportToDataValueSetTransformer.java @@ -43,6 +43,7 @@ import org.dhis2.fhir.adapter.fhir.model.FhirVersion; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformOutcome; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformerContext; @@ -60,6 +61,7 @@ import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -97,37 +99,13 @@ public Set getFhirVersions() @Override public FhirToDhisTransformOutcome transformInternal( @Nonnull final FhirClientResource fhirClientResource, @Nonnull final FhirToDhisTransformerContext context, @Nonnull final IBaseResource input, - @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) + @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables, + @Nonnull final DataValueSet dataValueSet ) throws TransformerException { final Map variables = new HashMap<>( scriptVariables ); - - final DataValueSet dataValueSet = getResource( fhirClientResource, context, ruleInfo, scriptVariables ).orElse( null ); - if ( dataValueSet == null ) - { - return null; - } - final MeasureReport mr = (MeasureReport) input; - final Optional organizationUnit; - if ( ruleInfo.getRule().getOrgUnitLookupScript() == null ) - { - logger.info( "Rule does not define an organization unit lookup script and data value set does not yet include one." ); - return null; - } - else - { - organizationUnit = getOrgUnit( context, ruleInfo, ruleInfo.getRule().getOrgUnitLookupScript(), variables ); - organizationUnit.ifPresent( ou -> dataValueSet.setOrgUnitId( ou.getId() ) ); - } - - if ( !organizationUnit.isPresent() ) - { - return null; - } - - //------------------------------ if ( ruleInfo.getRule().getDataSetIdLookupScript() == null ) { logger.info( "Rule does not define a data value set lookup script and data value set does not yet include one." ); @@ -141,30 +119,48 @@ public FhirToDhisTransformOutcome transformInternal( @Nonnull fina if ( dataValueSet.getDataSetId() == null ) { - return null; + throw new TransformerDataException( "Data Value Set ID cannot be decided." ); + } + + try + { + if ( Objects.isNull( mr.getPeriod() ) || Objects.isNull( mr.getPeriod().getStart() ) || Objects.isNull( mr.getPeriod().getEnd() ) ) + { + throw new TransformerDataException( "Period start and/or end is not provided. Period cannot be decided." ); + } + + dataValueSet.setPeriod( PeriodUtils.getDHIS2PeriodString( mr.getPeriod().getStart(), mr.getPeriod().getEnd() ) ); + } + catch ( IllegalArgumentException e ) + { + throw new TransformerDataException( e.getMessage() ); } - //------------------------------ ZonedDateTime lastUpdated = ZonedDateTime.ofInstant( mr.getMeta().getLastUpdated().toInstant(), zoneId ); + transformDataValues( mr, dataValueSet, lastUpdated ); // String version = mr.getMeta().getVersionId(); dataValueSet.setId( createArtificialDataValueSetId( mr ) ); - dataValueSet.setLastUpdated( lastUpdated ); - dataValueSet.setPeriod( PeriodUtils.getDHIS2PeriodString( mr.getPeriod().getStart(), mr.getPeriod().getEnd() ) ); - transformDataValues( mr, dataValueSet, lastUpdated ); return new FhirToDhisTransformOutcome<>( ruleInfo.getRule(), dataValueSet, dataValueSet.isNewResource() ); } private String createArtificialDataValueSetId( MeasureReport measureReport ) { - String measureUuid = measureReport.getMeasure().substring( 8 ); - String locationId = measureReport.getReporter().getReference().substring( 9 ); - String period = PeriodUtils.getDHIS2PeriodString( measureReport.getPeriod().getStart(), measureReport.getPeriod().getEnd() ); + try + { + String measureUuid = measureReport.getMeasure().substring( 8 ); + String locationId = measureReport.getReporter().getReference().substring( 9 ); + String period = PeriodUtils.getDHIS2PeriodString( measureReport.getPeriod().getStart(), measureReport.getPeriod().getEnd() ); - //DataValueSet doesn't use IDs. Therefore, creating the artificial one only for the Adapter purposes. It consists of: - //measure UUID (equivalent of DHIS2 DataSetId), location UUID (equivalent of DHIS2 OrgUnitUID) and period in DHIS2 format - return measureUuid + ":" + locationId + ":" + period; + //DataValueSet doesn't use IDs. Therefore, creating the artificial one only for the Adapter purposes. It consists of: + //measure UUID (equivalent of DHIS2 DataSetId), location UUID (equivalent of DHIS2 OrgUnitUID) and period in DHIS2 format + return measureUuid + ":" + locationId + ":" + period; + } + catch ( IllegalArgumentException e ) + { + throw new TransformerDataException( e.getMessage() ); + } } private void transformDataValues( MeasureReport measureReport, DataValueSet dataValueSet, ZonedDateTime lastUpdated ) diff --git a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/AbstractFhirMeasureReportToDataValueSetTranformer.java b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/AbstractFhirMeasureReportToDataValueSetTranformer.java index 6906fbc8..d9fd6f0f 100644 --- a/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/AbstractFhirMeasureReportToDataValueSetTranformer.java +++ b/fhir/src/main/java/org/dhis2/fhir/adapter/fhir/transform/fhir/impl/aggregate/AbstractFhirMeasureReportToDataValueSetTranformer.java @@ -32,6 +32,7 @@ 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.orgunit.OrganizationUnit; import org.dhis2.fhir.adapter.dhis.orgunit.OrganizationUnitService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityMetadataService; import org.dhis2.fhir.adapter.dhis.tracker.trackedentity.TrackedEntityService; @@ -43,6 +44,7 @@ import org.dhis2.fhir.adapter.fhir.repository.DhisFhirResourceId; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutionContext; import org.dhis2.fhir.adapter.fhir.script.ScriptExecutor; +import org.dhis2.fhir.adapter.fhir.transform.TransformerDataException; import org.dhis2.fhir.adapter.fhir.transform.TransformerException; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisDeleteTransformOutcome; import org.dhis2.fhir.adapter.fhir.transform.fhir.FhirToDhisTransformOutcome; @@ -53,6 +55,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -148,12 +151,38 @@ public FhirToDhisTransformOutcome transform( @Nonnull final FhirCl @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) throws TransformerException { - return transformInternal( fhirClientResource, context, input, ruleInfo, scriptVariables ); + final Map variables = new HashMap<>( scriptVariables ); + + final DataValueSet dataValueSet = getResource( fhirClientResource, context, ruleInfo, scriptVariables ).orElse( null ); + if ( dataValueSet == null ) + { + return null; + } + + final Optional organizationUnit; + if ( ruleInfo.getRule().getOrgUnitLookupScript() == null ) + { + logger.info( "Rule does not define an organization unit lookup script and data value set does not yet include one." ); + return null; + } + else + { + organizationUnit = getOrgUnit( context, ruleInfo, ruleInfo.getRule().getOrgUnitLookupScript(), variables ); + organizationUnit.ifPresent( ou -> dataValueSet.setOrgUnitId( ou.getId() ) ); + } + + if ( !organizationUnit.isPresent() ) + { + throw new TransformerDataException( "Organization unit ID cannot be decided." ); + } + + return transformInternal( fhirClientResource, context, input, ruleInfo, scriptVariables, dataValueSet ); } abstract protected FhirToDhisTransformOutcome transformInternal( @Nonnull final FhirClientResource fhirClientResource, @Nonnull final FhirToDhisTransformerContext context, @Nonnull final IBaseResource input, - @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables ) throws TransformerException; + @Nonnull final RuleInfo ruleInfo, @Nonnull final Map scriptVariables, + @Nonnull final DataValueSet dataValueSet ) throws TransformerException; @Nonnull protected Optional getDataSetId( @Nonnull FhirToDhisTransformerContext context, @Nonnull RuleInfo ruleInfo, @Nonnull ExecutableScript lookupScript, @Nonnull Map scriptVariables )