diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/EnrollmentAnalyticsQueryCriteria.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/EnrollmentAnalyticsQueryCriteria.java index 80344c18210a..223ea1fffe06 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/EnrollmentAnalyticsQueryCriteria.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/EnrollmentAnalyticsQueryCriteria.java @@ -115,4 +115,9 @@ public class EnrollmentAnalyticsQueryCriteria extends AnalyticsPagingCriteria { /** flag to enable row context in grid response */ private boolean rowContext; + + /** Returns true when parameters are incoming from analytics enrollments/aggregate endpoint. */ + public boolean isAggregatedEnrollments() { + return isAggregateEndpoint() && isEnrollmentEndpointItem(); + } } diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/EventDataQueryRequest.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/EventDataQueryRequest.java index 52da449e07bd..15038e4e0a29 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/EventDataQueryRequest.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/EventDataQueryRequest.java @@ -373,7 +373,7 @@ public EventDataQueryRequestBuilder fromCriteria(EnrollmentAnalyticsQueryCriteri } Set dimensions; - if (criteria.isQueryEndpoint()) { + if (criteria.isQueryEndpoint() || criteria.isAggregatedEnrollments()) { /* * for each AnalyticsDateFilter whose enrollment extractor is * set, concatenates the timeField with the extracted value: diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/RequestTypeAware.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/RequestTypeAware.java index ea2721869760..a9ef60251a54 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/RequestTypeAware.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/RequestTypeAware.java @@ -60,6 +60,10 @@ public boolean isAggregateEndpoint() { return AGGREGATE == endpointAction; } + public boolean isEnrollmentEndpointItem() { + return EndpointItem.ENROLLMENT == endpointItem; + } + public enum EndpointAction { AGGREGATE, QUERY, diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java index 2ad6ebcc442b..2ac392bd0f40 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventQueryParams.java @@ -871,6 +871,11 @@ public int getSortOrderAsInt() { return DESC == sortOrder ? 1 : 0; } + /** Returns true when parameters are incoming from analytics enrollments/aggregate entry point */ + public boolean isAggregatedEnrollments() { + return endpointAction == EndpointAction.AGGREGATE && endpointItem == EndpointItem.ENROLLMENT; + } + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractAnalyticsService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractAnalyticsService.java index a22c9800fcac..27306187facc 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractAnalyticsService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractAnalyticsService.java @@ -48,6 +48,7 @@ import static org.hisp.dhis.common.IdentifiableObjectUtils.getLocalPeriodIdentifiers; import static org.hisp.dhis.common.IdentifiableObjectUtils.getUids; import static org.hisp.dhis.common.ValueType.COORDINATE; +import static org.hisp.dhis.commons.util.TextUtils.EMPTY; import static org.hisp.dhis.organisationunit.OrganisationUnit.getParentGraphMap; import static org.hisp.dhis.organisationunit.OrganisationUnit.getParentNameGraphMap; @@ -123,6 +124,7 @@ protected Grid getGrid(EventQueryParams params) { .flatMap(dk -> dk.getKeywords().stream()) .collect(toList()); + List periods = getPeriods(params); params = new EventQueryParams.Builder(params).withStartEndDatesForPeriods().build(); // --------------------------------------------------------------------- @@ -141,6 +143,16 @@ protected Grid getGrid(EventQueryParams params) { true)); } + for (DimensionalObject dimension : periods) { + grid.addHeader( + new GridHeader( + dimension.getDimension(), + dimension.getDimensionDisplayName(), + ValueType.TEXT, + false, + true)); + } + final DisplayProperty displayProperty = params.getDisplayProperty(); Map repeatedNames = params.getItems().stream() @@ -211,7 +223,14 @@ protected Grid getGrid(EventQueryParams params) { long count = 0; if (!params.isSkipData() || params.analyzeOnly()) { - count = addEventData(grid, params); + if (!periods.isEmpty()) { + params = + new EventQueryParams.Builder(params) + .withPeriods( + periods.stream().flatMap(p -> p.getItems().stream()).collect(toList()), EMPTY) + .build(); + } + count = addData(grid, params); } // --------------------------------------------------------------------- @@ -319,7 +338,7 @@ private String getItemUid(QueryItem item) { protected abstract Grid createGridWithHeaders(EventQueryParams params); - protected abstract long addEventData(Grid grid, EventQueryParams params); + protected abstract long addData(Grid grid, EventQueryParams params); private void maybeApplyHeaders(EventQueryParams params, Grid grid) { if (params.hasHeaders()) { @@ -686,6 +705,16 @@ private List getDimensionItemUidsFrom( return dimensionUids; } + /** + * retrieve all periods as list of dimensional objects + * + * @param params + * @return {@link EventQueryParams} object + */ + protected List getPeriods(EventQueryParams params) { + return List.of(); + } + /** * Substitutes metadata in the given grid. * diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractJdbcEventAnalyticsManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractJdbcEventAnalyticsManager.java index 0b62495a7748..3a40b4074970 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractJdbcEventAnalyticsManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/AbstractJdbcEventAnalyticsManager.java @@ -37,8 +37,10 @@ import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.SPACE; +import static org.apache.commons.lang3.StringUtils.isBlank; import static org.hisp.dhis.analytics.AggregationType.CUSTOM; import static org.hisp.dhis.analytics.AggregationType.NONE; +import static org.hisp.dhis.analytics.DataQueryParams.LEVEL_PREFIX; import static org.hisp.dhis.analytics.DataQueryParams.NUMERATOR_DENOMINATOR_PROPERTIES_COUNT; import static org.hisp.dhis.analytics.DataType.NUMERIC; import static org.hisp.dhis.analytics.QueryKey.NV; @@ -56,6 +58,8 @@ import static org.hisp.dhis.analytics.util.AnalyticsUtils.withExceptionHandling; import static org.hisp.dhis.common.DimensionItemType.DATA_ELEMENT; import static org.hisp.dhis.common.DimensionItemType.PROGRAM_INDICATOR; +import static org.hisp.dhis.common.DimensionalObject.ORGUNIT_DIM_ID; +import static org.hisp.dhis.common.DimensionalObject.PERIOD_DIM_ID; import static org.hisp.dhis.common.DimensionalObjectUtils.COMPOSITE_DIM_OBJECT_PLAIN_SEP; import static org.hisp.dhis.common.QueryOperator.IN; import static org.hisp.dhis.common.RequestTypeAware.EndpointItem.ENROLLMENT; @@ -101,6 +105,7 @@ import org.hisp.dhis.common.GridHeader; import org.hisp.dhis.common.IdScheme; import org.hisp.dhis.common.InQueryFilter; +import org.hisp.dhis.common.OrganisationUnitSelectionMode; import org.hisp.dhis.common.QueryFilter; import org.hisp.dhis.common.QueryItem; import org.hisp.dhis.common.Reference; @@ -111,6 +116,7 @@ import org.hisp.dhis.feedback.ErrorCode; import org.hisp.dhis.jdbc.StatementBuilder; import org.hisp.dhis.option.Option; +import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.period.Period; import org.hisp.dhis.program.AnalyticsType; import org.hisp.dhis.program.ProgramIndicator; @@ -137,6 +143,8 @@ public abstract class AbstractJdbcEventAnalyticsManager { private static final String COL_VALUE = "value"; + private static final String OUTER_SQL_ALIAS = "t1"; + private static final String AND = " and "; private static final String OR = " or "; @@ -353,6 +361,17 @@ private void addDimensionSelectColumns( .getDimensions() .forEach( dimension -> { + if (params.isAggregatedEnrollments() + && dimension.getDimensionType() == DimensionType.PERIOD) { + dimension + .getItems() + .forEach( + it -> + columns.add( + ((Period) it).getPeriodType().getPeriodTypeEnum().getName())); + return; + } + if (isGroupByClause && dimension.getDimensionType() == DimensionType.PERIOD && params.hasNonDefaultBoundaries()) { @@ -886,7 +905,7 @@ private String getTableAndColumn( * @param maxLimit max number of records to return. * @return a SQL query. */ - protected String getEventsOrEnrollmentsSql(EventQueryParams params, int maxLimit) { + protected String getAggregatedEnrollmentsSql(EventQueryParams params, int maxLimit) { String sql = getSelectClause(params); @@ -901,6 +920,93 @@ protected String getEventsOrEnrollmentsSql(EventQueryParams params, int maxLimit return sql; } + /** + * Template method that generates a SQL query for retrieving aggregated enrollments. + * + * @param params the {@link List} to drive the query generation. + * @param params the {@link EventQueryParams} to drive the query generation. + * @return a SQL query. + */ + protected String getAggregatedEnrollmentsSql(List headers, EventQueryParams params) { + String sql = getSelectClause(params); + + sql += getFromClause(params); + + sql += getWhereClause(params); + + final String tempSql = sql; + + String headerColumns = + headers.stream() + .filter( + header -> + !header.getName().equalsIgnoreCase(COL_VALUE) + && !header.getName().equalsIgnoreCase(PERIOD_DIM_ID) + && !header.getName().equalsIgnoreCase(ORGUNIT_DIM_ID)) + .map( + header -> { + String headerName = header.getName(); + if (tempSql.contains(headerName)) { + return OUTER_SQL_ALIAS + "." + quote(headerName); + } + if (headerName.contains(".")) { + headerName = headerName.split("\\.")[1]; + } + + return OUTER_SQL_ALIAS + "." + quote(headerName); + }) + .collect(joining(",")); + + String orgColumns = EMPTY; + + if (!params.isOrganisationUnitMode(OrganisationUnitSelectionMode.SELECTED) + && !params.isOrganisationUnitMode(OrganisationUnitSelectionMode.CHILDREN)) { + + orgColumns = + params.getDimensionOrFilterItems(ORGUNIT_DIM_ID).stream() + .map(d -> LEVEL_PREFIX + ((OrganisationUnit) d).getLevel()) + .distinct() + .collect(joining(",")); + } + + String periodColumns = + params.getDimensions().stream() + .filter(d -> d.getDimensionType() == DimensionType.PERIOD) + .flatMap( + d -> + d.getItems().stream() + .map( + it -> + OUTER_SQL_ALIAS + + "." + + ((Period) it).getPeriodType().getPeriodTypeEnum().getName()) + .collect(Collectors.toList()) + .stream() + .distinct()) + .collect(joining(",")); + + String columns = + (!isBlank(orgColumns) ? orgColumns : "," + ORGUNIT_DIM_ID) + + (!isBlank(periodColumns) ? "," + periodColumns : EMPTY) + + (!isBlank(headerColumns) ? "," + headerColumns : EMPTY); + + sql = + "select count(" + + OUTER_SQL_ALIAS + + ".pi) as " + + COL_VALUE + + ", " + + columns + + " from (" + + sql + + ") " + + OUTER_SQL_ALIAS + + " group by " + + columns; + + return sql; + } + /** * Adds a value from the given row set to the grid. * diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEnrollmentAnalyticsService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEnrollmentAnalyticsService.java index d3d83c233f4d..288c5baee0dd 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEnrollmentAnalyticsService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEnrollmentAnalyticsService.java @@ -28,10 +28,14 @@ package org.hisp.dhis.analytics.event.data; import static com.google.common.base.Preconditions.checkNotNull; +import static org.hisp.dhis.analytics.DataQueryParams.VALUE_HEADER_NAME; +import static org.hisp.dhis.analytics.DataQueryParams.VALUE_ID; import static org.hisp.dhis.common.ValueType.DATETIME; import static org.hisp.dhis.common.ValueType.NUMBER; import static org.hisp.dhis.common.ValueType.TEXT; +import java.util.List; +import java.util.stream.Collectors; import org.hisp.dhis.analytics.AnalyticsSecurityManager; import org.hisp.dhis.analytics.data.handler.SchemaIdResponseMapper; import org.hisp.dhis.analytics.event.EnrollmentAnalyticsManager; @@ -40,8 +44,11 @@ import org.hisp.dhis.analytics.event.EventQueryPlanner; import org.hisp.dhis.analytics.event.EventQueryValidator; import org.hisp.dhis.analytics.event.LabelMapper; +import org.hisp.dhis.common.DimensionType; +import org.hisp.dhis.common.DimensionalObject; import org.hisp.dhis.common.Grid; import org.hisp.dhis.common.GridHeader; +import org.hisp.dhis.common.RequestTypeAware; import org.hisp.dhis.system.grid.ListGrid; import org.hisp.dhis.util.Timer; import org.springframework.stereotype.Service; @@ -111,6 +118,11 @@ public Grid getEnrollments(EventQueryParams params) { @Override protected Grid createGridWithHeaders(EventQueryParams params) { + if (params.getEndpointAction() == RequestTypeAware.EndpointAction.AGGREGATE) { + return new ListGrid() + .addHeader(new GridHeader(VALUE_ID, VALUE_HEADER_NAME, NUMBER, false, false)); + } + return new ListGrid() .addHeader(new GridHeader(ITEM_PI, NAME_PI, TEXT, false, true)) .addHeader(new GridHeader(ITEM_TEI, NAME_TEI, TEXT, false, true)) @@ -148,24 +160,42 @@ protected Grid createGridWithHeaders(EventQueryParams params) { .addHeader(new GridHeader(ITEM_PROGRAM_STATUS, NAME_PROGRAM_STATUS, TEXT, false, true)); } - @Override - protected long addEventData(Grid grid, EventQueryParams params) { + protected long addData(Grid grid, EventQueryParams params) { Timer timer = new Timer().start().disablePrint(); - params = queryPlanner.planEnrollmentQuery(params); + List paramsList; - timer.getSplitTime("Planned event query, got partitions: " + params.getPartitions()); + if (params.getEndpointAction() == RequestTypeAware.EndpointAction.AGGREGATE) { + paramsList = queryPlanner.planAggregateQuery(params); + } else { + paramsList = List.of(queryPlanner.planEnrollmentQuery(params)); + } long count = 0; - if (params.isTotalPages()) { - count += enrollmentAnalyticsManager.getEnrollmentCount(params); - } + for (EventQueryParams queryParams : paramsList) { + timer.getSplitTime("Planned event query, got partitions: " + queryParams.getPartitions()); + if (queryParams.isTotalPages() && !params.isAggregatedEnrollments()) { + count += enrollmentAnalyticsManager.getEnrollmentCount(queryParams); + } - enrollmentAnalyticsManager.getEnrollments(params, grid, queryValidator.getMaxLimit()); + enrollmentAnalyticsManager.getEnrollments(queryParams, grid, queryValidator.getMaxLimit()); - timer.getTime("Got enrollments " + grid.getHeight()); + timer.getTime("Got enrollments " + grid.getHeight()); + } return count; } + + @Override + protected List getPeriods(EventQueryParams params) { + // for aggregated enrollments only + if (!params.isAggregatedEnrollments()) { + return List.of(); + } + + return params.getDimensions().stream() + .filter(d -> d.getDimensionType() == DimensionType.PERIOD) + .collect(Collectors.toList()); + } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventAnalyticsService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventAnalyticsService.java index ef34365f5acf..0bf655962db0 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventAnalyticsService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventAnalyticsService.java @@ -763,7 +763,7 @@ protected Grid createGridWithHeaders(EventQueryParams params) { * @return the count of events. */ @Override - protected long addEventData(Grid grid, EventQueryParams params) { + protected long addData(Grid grid, EventQueryParams params) { Timer timer = new Timer().start().disablePrint(); params = queryPlanner.planEventQuery(params); diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventQueryPlanner.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventQueryPlanner.java index f6b6e81464eb..8060acc02dc9 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventQueryPlanner.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventQueryPlanner.java @@ -76,10 +76,10 @@ public List planAggregateQuery(EventQueryParams params) { List>> groupers = new ImmutableList.Builder>>() - .add(q -> groupByQueryItems(q)) - .add(q -> groupByOrgUnitLevel(q)) - .add(q -> groupByPeriodType(q)) - .add(q -> groupByPeriod(q)) + .add(this::groupByQueryItems) + .add(this::groupByOrgUnitLevel) + .add(this::groupByPeriodType) + .add(this::groupByPeriod) .build(); for (Function> grouper : groupers) { @@ -127,7 +127,7 @@ private EventQueryParams withTableNameAndPartitions(EventQueryParams params) { : PartitionUtils.getPartitions(params.getAllPeriods()); String baseName = - params.hasEnrollmentProgramIndicatorDimension() + params.hasEnrollmentProgramIndicatorDimension() || params.isAggregatedEnrollments() ? AnalyticsTableType.ENROLLMENT.getTableName() : AnalyticsTableType.EVENT.getTableName(); diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java index 25e2976c8c26..594c0fe63f4a 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java @@ -134,7 +134,10 @@ public JdbcEnrollmentAnalyticsManager( @Override public void getEnrollments(EventQueryParams params, Grid grid, int maxLimit) { - String sql = getEventsOrEnrollmentsSql(params, maxLimit); + String sql = + params.isAggregatedEnrollments() + ? getAggregatedEnrollmentsSql(grid.getHeaders(), params) + : getAggregatedEnrollmentsSql(params, maxLimit); if (params.analyzeOnly()) { withExceptionHandling( @@ -449,7 +452,10 @@ protected String getWhereClause(EventQueryParams params) { @Override protected String getSelectClause(EventQueryParams params) { - List selectCols = ListUtils.distinctUnion(COLUMNS, getSelectColumns(params, false)); + List selectCols = + ListUtils.distinctUnion( + params.isAggregatedEnrollments() ? List.of("pi") : COLUMNS, + getSelectColumns(params, false)); return "select " + StringUtils.join(selectCols, ",") + " "; } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java index dfbb6b45dc64..54949db15469 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEventAnalyticsManager.java @@ -129,7 +129,7 @@ public JdbcEventAnalyticsManager( @Override public Grid getEvents(EventQueryParams params, Grid grid, int maxLimit) { - String sql = getEventsOrEnrollmentsSql(params, maxLimit); + String sql = getAggregatedEnrollmentsSql(params, maxLimit); if (params.analyzeOnly()) { withExceptionHandling( @@ -256,7 +256,7 @@ public long getEventCount(EventQueryParams params) { } else { count = withExceptionHandling(() -> jdbcTemplate.queryForObject(finalSqlValue, Long.class)) - .orElse(0l); + .orElse(0L); } return count; diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/AbstractAnalyticsServiceTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/AbstractAnalyticsServiceTest.java index 287f76a9d96b..d835de7a46b0 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/AbstractAnalyticsServiceTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/AbstractAnalyticsServiceTest.java @@ -266,7 +266,7 @@ protected Grid createGridWithHeaders(EventQueryParams params) { } @Override - protected long addEventData(Grid grid, EventQueryParams params) { + protected long addData(Grid grid, EventQueryParams params) { return 0; } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/EventAnalyticsManagerTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/EventAnalyticsManagerTest.java index d7e745485bf1..ad63b0dea9b0 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/EventAnalyticsManagerTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/EventAnalyticsManagerTest.java @@ -615,7 +615,7 @@ ORGUNIT_DIM_ID, DimensionType.ORGANISATION_UNIT, getList(ouA))) .addDescSortItem(new QueryItem(piB)) .addAscSortItem(new QueryItem(deA)); - final String sql = subject.getEventsOrEnrollmentsSql(eventQueryParamsBuilder.build(), 100); + final String sql = subject.getAggregatedEnrollmentsSql(eventQueryParamsBuilder.build(), 100); assertThat( sql, diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EnrollmentAnalyticsController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EnrollmentAnalyticsController.java index 915843c972d0..ddf50a9d2f7b 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EnrollmentAnalyticsController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EnrollmentAnalyticsController.java @@ -27,6 +27,7 @@ */ package org.hisp.dhis.webapi.controller; +import static org.hisp.dhis.common.RequestTypeAware.EndpointAction.AGGREGATE; import static org.hisp.dhis.common.RequestTypeAware.EndpointAction.QUERY; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -35,6 +36,7 @@ import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotNull; import lombok.AllArgsConstructor; +import lombok.SneakyThrows; import org.hisp.dhis.analytics.analyze.ExecutionPlanStore; import org.hisp.dhis.analytics.dimensions.AnalyticsDimensionsPagingWrapper; import org.hisp.dhis.analytics.event.EnrollmentAnalyticsDimensionsService; @@ -90,6 +92,140 @@ public class EnrollmentAnalyticsController { @NotNull private final SystemSettingManager systemSettingManager; + @PreAuthorize("hasRole('ALL') or hasRole('F_PERFORM_ANALYTICS_EXPLAIN')") + @GetMapping( + value = "/aggregate/{program}/explain", + produces = {APPLICATION_JSON_VALUE, "application/javascript"}) + public @ResponseBody Grid getExplainAggregateJson( // JSON, JSONP + @PathVariable String program, + EnrollmentAnalyticsQueryCriteria criteria, + DhisApiVersion apiVersion, + HttpServletResponse response) { + EventQueryParams params = getEventQueryParams(program, criteria, apiVersion, true, AGGREGATE); + + Grid grid = analyticsService.getEnrollments(params); + contextUtils.configureResponse( + response, ContextUtils.CONTENT_TYPE_JSON, CacheStrategy.RESPECT_SYSTEM_SETTING); + + if (params.analyzeOnly()) { + String key = params.getExplainOrderId(); + grid.addPerformanceMetrics(executionPlanStore.getExecutionPlans(key)); + } + + return grid; + } + + @GetMapping( + value = "/aggregate/{program}", + produces = {APPLICATION_JSON_VALUE, "application/javascript"}) + public @ResponseBody Grid getAggregateJson( // JSON, JSONP + @PathVariable String program, + EnrollmentAnalyticsQueryCriteria criteria, + DhisApiVersion apiVersion, + HttpServletResponse response) { + EventQueryParams params = getEventQueryParams(program, criteria, apiVersion, false, AGGREGATE); + + contextUtils.configureResponse( + response, ContextUtils.CONTENT_TYPE_JSON, CacheStrategy.RESPECT_SYSTEM_SETTING); + + return analyticsService.getEnrollments(params); + } + + @SneakyThrows + @GetMapping("/aggregate/{program}.xml") + public void getAggregateXml( + @PathVariable String program, + EnrollmentAnalyticsQueryCriteria criteria, + DhisApiVersion apiVersion, + HttpServletResponse response) { + EventQueryParams params = getEventQueryParams(program, criteria, apiVersion, false, AGGREGATE); + + contextUtils.configureResponse( + response, + ContextUtils.CONTENT_TYPE_XML, + CacheStrategy.RESPECT_SYSTEM_SETTING, + "enrollments.xml", + false); + Grid grid = analyticsService.getEnrollments(params); + GridUtils.toXml(grid, response.getOutputStream()); + } + + @SneakyThrows + @GetMapping("/aggregate/{program}.xls") + public void getAggregateXls( + @PathVariable String program, + EnrollmentAnalyticsQueryCriteria criteria, + DhisApiVersion apiVersion, + HttpServletResponse response) { + EventQueryParams params = getEventQueryParams(program, criteria, apiVersion, false, AGGREGATE); + + contextUtils.configureResponse( + response, + ContextUtils.CONTENT_TYPE_EXCEL, + CacheStrategy.RESPECT_SYSTEM_SETTING, + "enrollments.xls", + true); + Grid grid = analyticsService.getEnrollments(params); + GridUtils.toXls(grid, response.getOutputStream()); + } + + @SneakyThrows + @GetMapping("/aggregate/{program}.csv") + public void getAggregateCsv( + @PathVariable String program, + EnrollmentAnalyticsQueryCriteria criteria, + DhisApiVersion apiVersion, + HttpServletResponse response) { + EventQueryParams params = getEventQueryParams(program, criteria, apiVersion, false, AGGREGATE); + + contextUtils.configureResponse( + response, + ContextUtils.CONTENT_TYPE_CSV, + CacheStrategy.RESPECT_SYSTEM_SETTING, + "enrollments.csv", + true); + Grid grid = analyticsService.getEnrollments(params); + GridUtils.toCsv(grid, response.getWriter()); + } + + @SneakyThrows + @GetMapping("/aggregate/{program}.html") + public void getAggregateHtml( + @PathVariable String program, + EnrollmentAnalyticsQueryCriteria criteria, + DhisApiVersion apiVersion, + HttpServletResponse response) { + EventQueryParams params = getEventQueryParams(program, criteria, apiVersion, false, AGGREGATE); + + contextUtils.configureResponse( + response, + ContextUtils.CONTENT_TYPE_HTML, + CacheStrategy.RESPECT_SYSTEM_SETTING, + "enrollments.html", + false); + Grid grid = analyticsService.getEnrollments(params); + GridUtils.toHtml(grid, response.getWriter()); + } + + @SneakyThrows + @GetMapping("/aggregate/{program}.html+css") + public void getAggregateHtmlCss( + @PathVariable String program, + EnrollmentAnalyticsQueryCriteria criteria, + DhisApiVersion apiVersion, + HttpServletResponse response) { + EventQueryParams params = getEventQueryParams(program, criteria, apiVersion, false, AGGREGATE); + + contextUtils.configureResponse( + response, + ContextUtils.CONTENT_TYPE_HTML, + CacheStrategy.RESPECT_SYSTEM_SETTING, + "enrollments.html", + false); + Grid grid = analyticsService.getEnrollments(params); + GridUtils.toHtmlCss(grid, response.getWriter()); + } + @PreAuthorize("hasRole('ALL') or hasRole('F_PERFORM_ANALYTICS_EXPLAIN')") @GetMapping( value = "/query/{program}/explain",