diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/tei/query/context/querybuilder/ProgramIndicatorQueryBuilder.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/tei/query/context/querybuilder/ProgramIndicatorQueryBuilder.java index 7a3089f2ec23..c81c413bef9c 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/tei/query/context/querybuilder/ProgramIndicatorQueryBuilder.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/tei/query/context/querybuilder/ProgramIndicatorQueryBuilder.java @@ -45,6 +45,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; +import org.hisp.dhis.analytics.AggregationType; import org.hisp.dhis.analytics.DataType; import org.hisp.dhis.analytics.common.params.AnalyticsSortingParams; import org.hisp.dhis.analytics.common.params.dimension.DimensionIdentifier; @@ -61,11 +62,11 @@ import org.hisp.dhis.analytics.tei.query.context.sql.RenderableSqlQuery; import org.hisp.dhis.analytics.tei.query.context.sql.SqlQueryBuilder; import org.hisp.dhis.analytics.tei.query.context.sql.SqlQueryBuilders; +import org.hisp.dhis.commons.util.TextUtils; import org.hisp.dhis.program.AnalyticsType; import org.hisp.dhis.program.Program; import org.hisp.dhis.program.ProgramIndicator; import org.hisp.dhis.program.ProgramIndicatorService; -import org.hisp.dhis.program.ProgramStage; import org.springframework.stereotype.Service; /** @@ -158,23 +159,8 @@ private void buildLeftJoins( ProgramIndicator programIndicator = (ProgramIndicator) param.getDimensionIdentifier().getDimension().getQueryItem().getItem(); - String expression = - programIndicatorService.getAnalyticsSql( - programIndicator.getExpression(), - DataType.NUMERIC, - programIndicator, - null, - null, - SUBQUERY_TABLE_ALIAS); - - String filter = - programIndicatorService.getAnalyticsSql( - programIndicator.getFilter(), - DataType.BOOLEAN, - programIndicator, - null, - null, - SUBQUERY_TABLE_ALIAS); + ProgramIndicatorQueryParts programIndicatorQueryParts = + getProgramIndicatorQueryParts(programIndicator); builder.selectField( Field.ofUnquoted( @@ -188,7 +174,9 @@ private void buildLeftJoins( () -> "(" + enrollmentProgramIndicatorSelect( - param.getDimensionIdentifier().getProgram(), expression, filter, true) + param.getDimensionIdentifier().getProgram(), + programIndicatorQueryParts, + true) + ") as " + assignedAlias, fieldsEqual(TEI_ALIAS, TEI_UID, assignedAlias, TEI_UID))); @@ -201,8 +189,7 @@ private void buildLeftJoins( "(" + enrollmentProgramIndicatorSelect( param.getDimensionIdentifier().getProgram(), - expression, - filter, + programIndicatorQueryParts, false) + ") as " + enrollmentAlias, @@ -213,9 +200,7 @@ private void buildLeftJoins( "(" + eventProgramIndicatorSelect( param.getDimensionIdentifier().getProgram(), - param.getDimensionIdentifier().getProgramStage(), - expression, - filter) + programIndicatorQueryParts) + ") as " + assignedAlias, fieldsEqual(enrollmentAlias, PI_UID, assignedAlias, PI_UID))); @@ -223,11 +208,40 @@ private void buildLeftJoins( } } + private record ProgramIndicatorQueryParts(String function, String expression, String filter) {} + + private ProgramIndicatorQueryParts getProgramIndicatorQueryParts( + ProgramIndicator programIndicator) { + + return new ProgramIndicatorQueryParts( + // function + TextUtils.emptyIfEqual( + programIndicator.getAggregationTypeFallback().getValue(), + AggregationType.CUSTOM.getValue()), + // expression + programIndicatorService.getAnalyticsSql( + programIndicator.getExpression(), + DataType.NUMERIC, + programIndicator, + null, + null, + SUBQUERY_TABLE_ALIAS), + // filter + programIndicatorService.getAnalyticsSql( + programIndicator.getFilter(), + DataType.BOOLEAN, + programIndicator, + null, + null, + SUBQUERY_TABLE_ALIAS)); + } + private static String enrollmentProgramIndicatorSelect( ElementWithOffset program, - String expression, - String filter, + ProgramIndicatorQueryParts programIndicatorQueryParts, boolean needsExpressions) { + String expression = programIndicatorQueryParts.expression(); + String filter = programIndicatorQueryParts.filter(); return "select innermost_enr.*" + " from (select tei as " + TEI_UID @@ -246,29 +260,31 @@ private static String enrollmentProgramIndicatorSelect( } static String eventProgramIndicatorSelect( - ElementWithOffset program, - ElementWithOffset programStage, - String expression, - String filter) { - String condition = SUBQUERY_TABLE_ALIAS + ".ps = '" + programStage.getElement().getUid() + "'"; + ElementWithOffset program, ProgramIndicatorQueryParts programIndicatorQueryParts) { + + String filter = programIndicatorQueryParts.filter(); + String function = programIndicatorQueryParts.function(); + String expression = programIndicatorQueryParts.expression(); + + String whereCondition = ""; + if (StringUtils.isNotBlank(filter)) { - condition = condition + " and " + filter; + whereCondition = " where " + filter; } - return "select innermost_evt.*" + return "select innermost_evt.programinstanceuid, " + + function + + "(innermost_evt.value) as value" + " from (select pi as " + PI_UID + ", " + expression - + " as value, " - + " row_number() over (partition by pi order by occurreddate desc) as rn " + + " as value " + " from analytics_event_" + program.getElement().getUid() + " as " + SUBQUERY_TABLE_ALIAS - + " where " - + condition - + ") innermost_evt" - + " where innermost_evt.rn = 1"; + + whereCondition + + ") innermost_evt group by innermost_evt.programinstanceuid"; } @Getter diff --git a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/analytics/tei/TrackedEntityQueryTest.java b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/analytics/tei/TrackedEntityQueryTest.java index 1aec4a759b5e..51376377c2fc 100644 --- a/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/analytics/tei/TrackedEntityQueryTest.java +++ b/dhis-2/dhis-test-e2e/src/test/java/org/hisp/dhis/analytics/tei/TrackedEntityQueryTest.java @@ -2695,4 +2695,69 @@ public void queryWithEventStatus() { "", "COMPLETED")); } + + @Test + public void queryProgramIndicator() { + // Given + QueryParamsBuilder params = + new QueryParamsBuilder() + .add("program=IpHINAT79UW") + .add( + "dimension=IpHINAT79UW.GxdhnY5wmHq,w75KJ2mc4zz:eq:Justin,zDhUuAYrxNC:eq:Hayes,ou:eqPIdr5yD1Q") + .add("desc=IpHINAT79UW.GxdhnY5wmHq") + .add("lastUpdated=LAST_YEAR") + .add("relativePeriodDate=2016-01-01"); + + // When + ApiResponse response = analyticsTeiActions.query().get("nEenWmSyUEp", JSON, JSON, params); + + // Then + response + .validate() + .statusCode(200) + .body("rows", hasSize(equalTo(1))) + .body("height", equalTo(1)) + .body("width", equalTo(17)) + .body("headerWidth", equalTo(17)) + .body("headers", hasSize(equalTo(17))) + .body("metaData.pager.page", equalTo(1)) + .body("metaData.pager.pageSize", equalTo(50)) + .body("metaData.pager.isLastPage", is(true)) + .body("metaData.pager", not(hasKey("total"))) + .body("metaData.pager", not(hasKey("pageCount"))) + .body("metaData.items.GxdhnY5wmHq.name", equalTo("Average weight (g)")) + .body("metaData.dimensions", hasKey("pe")); + + validateHeader( + response, + 16, + "IpHINAT79UW.GxdhnY5wmHq", + "Average weight (g)", + "NUMBER", + "java.lang.Double", + false, + true); + + validateRow( + response, + 0, + List.of( + "a04hYxjC8lM", + "2015-08-06 21:20:52.547", + "", + "2015-08-06 21:20:52.547", + "", + "", + "", + "", + "", + "Rokolon MCHP", + "OU_707826", + "Sierra Leone / Moyamba / Ribbi / Rokolon MCHP", + "Justin", + "Hayes", + "Male", + "", + "2994.5")); + } } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/PageRequestParams.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/PageRequestParams.java index 0baedb648fb5..b4cbdb40f28e 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/PageRequestParams.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/PageRequestParams.java @@ -27,7 +27,6 @@ */ package org.hisp.dhis.webapi.controller.tracker.export; -import java.util.Objects; import org.hisp.dhis.common.OpenApi; /** @@ -61,7 +60,7 @@ public interface PageRequestParams { */ @OpenApi.Ignore default boolean isPaged() { - return Objects.requireNonNullElse(getSkipPaging(), true); + return !Boolean.TRUE.equals(getSkipPaging()); } /** Indicates whether to include the total number of items and pages in the paginated response. */ diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/RequestParamsValidator.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/RequestParamsValidator.java index 722bd5c29bba..f5c7aeebe9a1 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/RequestParamsValidator.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/export/RequestParamsValidator.java @@ -404,7 +404,7 @@ private static boolean orgUnitModeDoesNotRequireOrgUnit( public static void validatePaginationParameters(PageRequestParams params) throws BadRequestException { - if (Boolean.TRUE.equals(params.getSkipPaging()) + if (!params.isPaged() && (ObjectUtils.firstNonNull(params.getPage(), params.getPageSize()) != null || Boolean.TRUE.equals(params.getTotalPages()))) { throw new BadRequestException( diff --git a/dhis-2/dhis-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/PageImportRequestParamsTest.java b/dhis-2/dhis-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/PageImportRequestParamsTest.java index e91aa9135d5c..c54d9cf55d35 100644 --- a/dhis-2/dhis-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/PageImportRequestParamsTest.java +++ b/dhis-2/dhis-web-api/src/test/java/org/hisp/dhis/webapi/controller/tracker/export/PageImportRequestParamsTest.java @@ -51,9 +51,9 @@ void shouldBePagedIfSkipPagingIsNull() { } @Test - void shouldBePagedIfSkipPagingIsTrue() { + void shouldBePagedIfSkipPagingIsFalse() { PaginationParameters parameters = new PaginationParameters(); - parameters.setSkipPaging(true); + parameters.setSkipPaging(false); assertTrue(parameters.isPaged()); } @@ -61,7 +61,7 @@ void shouldBePagedIfSkipPagingIsTrue() { @Test void shouldBeUnpagedIfSkipPagingIsTrue() { PaginationParameters parameters = new PaginationParameters(); - parameters.setSkipPaging(false); + parameters.setSkipPaging(true); assertFalse(parameters.isPaged()); }