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 fbcdfd79dccb..8362fdf61e17 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 @@ -40,9 +40,11 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import org.hisp.dhis.analytics.AggregationType; import org.hisp.dhis.analytics.AnalyticsMetaDataKey; import org.hisp.dhis.analytics.EventOutputType; @@ -52,8 +54,9 @@ import org.hisp.dhis.event.EventStatus; import org.hisp.dhis.program.EnrollmentStatus; -@Builder @Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor public class EventDataQueryRequest { private String program; diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventDataQueryService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventDataQueryService.java index 5ae07b09e450..7fad3e7fc5c6 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventDataQueryService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/EventDataQueryService.java @@ -67,20 +67,10 @@ public interface EventDataQueryService { * Returns the coordinate column field to use for the given coordinate field. Coordinate field * must match EVENT, a data element identifier or an attribute identifier. * - * @param program the program instance. - * @param coordinateField the coordinate field. - * @param fallbackCoordinateField the fallback coordinate field applied if coordinate field in - * result set is null. - * @param defaultCoordinateFallback flag for cascade fallback, first not null geometry (coalesce) - * will be applied. - * @return the coordinate column list. + * @param request the {@link EventDataQueryRequest}. * @throws IllegalQueryException if one of the given coordinates is not valid. */ - List getCoordinateFields( - String program, - String coordinateField, - String fallbackCoordinateField, - boolean defaultCoordinateFallback); + List getCoordinateFields(EventDataQueryRequest request); /** * Returns a {@link QueryItem}. 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 55a58883bd63..47952b35a9d4 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 @@ -1384,11 +1384,13 @@ protected String getCoalesce(List fields, String defaultColumnName) { String args = fields.stream() - .filter(f -> f != null && !f.isBlank()) + .filter(StringUtils::isNotBlank) .map(f -> sqlBuilder.quoteAx(f)) .collect(Collectors.joining(",")); - return args.isEmpty() ? defaultColumnName : "coalesce(" + args + ")"; + String sql = String.format("coalesce(%s)", args); + + return args.isEmpty() ? defaultColumnName : sql; } /** diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventCoordinateService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventCoordinateService.java index 10c66873f3e9..39740ebe917a 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventCoordinateService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventCoordinateService.java @@ -31,7 +31,6 @@ import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; import lombok.RequiredArgsConstructor; import org.hisp.dhis.common.IllegalQueryException; import org.hisp.dhis.common.ValueType; @@ -69,11 +68,11 @@ public class DefaultEventCoordinateService implements EventCoordinateService { public static final List COL_NAME_PROGRAM_NO_REGISTRATION_GEOMETRY_LIST = List.of(COL_NAME_EVENT_GEOMETRY, COL_NAME_ENROLLMENT_GEOMETRY, COL_NAME_OU_GEOMETRY); - @Nonnull private final ProgramService programService; + private final ProgramService programService; - @Nonnull private final DataElementService dataElementService; + private final DataElementService dataElementService; - @Nonnull private final TrackedEntityAttributeService attributeService; + private final TrackedEntityAttributeService attributeService; @Override public boolean isFallbackCoordinateFieldValid(boolean isRegistration, String coordinateField) { @@ -115,23 +114,21 @@ public List getFallbackCoordinateFields( } fallbackCoordinateFields.add(fallbackCoordinateField); - } else { - if (defaultCoordinateFallback) { - List items = - new ArrayList<>( - pr.isRegistration() - ? COL_NAME_GEOMETRY_LIST - : COL_NAME_PROGRAM_NO_REGISTRATION_GEOMETRY_LIST); - - fallbackCoordinateFields.addAll(items); - } + } else if (defaultCoordinateFallback) { + List items = + new ArrayList<>( + pr.isRegistration() + ? COL_NAME_GEOMETRY_LIST + : COL_NAME_PROGRAM_NO_REGISTRATION_GEOMETRY_LIST); + + fallbackCoordinateFields.addAll(items); } return fallbackCoordinateFields; } @Override - public String getCoordinateField(ValueType valueType, String field, ErrorCode errorCode) { + public String validateCoordinateField(ValueType valueType, String field, ErrorCode errorCode) { if (ValueType.COORDINATE != valueType && ValueType.ORGANISATION_UNIT != valueType) { throwIllegalQueryEx(errorCode, field); } @@ -140,13 +137,13 @@ public String getCoordinateField(ValueType valueType, String field, ErrorCode er } @Override - public String getCoordinateField(String program, String coordinateField, ErrorCode errorCode) { + public String validateCoordinateField(String program, String field, ErrorCode errorCode) { Program pr = programService.getProgram(program); - if (COL_NAME_TRACKED_ENTITY_GEOMETRY.equals(coordinateField) && !pr.isRegistration()) { - throwIllegalQueryEx(errorCode, coordinateField); + if (COL_NAME_TRACKED_ENTITY_GEOMETRY.equals(field) && !pr.isRegistration()) { + throwIllegalQueryEx(errorCode, field); } - return coordinateField; + return field; } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventDataQueryService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventDataQueryService.java index f32795547028..533bd5c3229e 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventDataQueryService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/DefaultEventDataQueryService.java @@ -149,12 +149,7 @@ public EventQueryParams getFromRequest(EventDataQueryRequest request, boolean an throwIllegalQueryEx(ErrorCode.E7130, request.getStage()); } - List coordinateFields = - getCoordinateFields( - request.getProgram(), - request.getCoordinateField(), - request.getFallbackCoordinateField(), - request.isDefaultCoordinateFallback()); + List coordinateFields = getCoordinateFields(request); addDimensionsToParams(params, request, userOrgUnits, pr, idScheme); @@ -420,33 +415,32 @@ public EventQueryParams getFromAnalyticalObject(EventAnalyticalObject object) { * @return the coordinate column list. */ @Override - public List getCoordinateFields( - String program, - String coordinateField, - String fallbackCoordinateField, - boolean defaultCoordinateFallback) { - List coordinateFields = new ArrayList<>(); + public List getCoordinateFields(EventDataQueryRequest request) { + final String program = request.getProgram(); + // TODO Remove when all web apps stop using old names of coordinate fields + final String coordinateField = mapCoordinateField(request.getCoordinateField()); + final boolean defaultCoordinateFallback = request.isDefaultCoordinateFallback(); + final String fallbackCoordinateField = mapCoordinateField(request.getFallbackCoordinateField()); - // TODO!!! remove when all fe apps stop using old names of coordinate fields - coordinateField = mapCoordinateField(coordinateField); - fallbackCoordinateField = mapCoordinateField(fallbackCoordinateField); + List coordinateFields = new ArrayList<>(); if (coordinateField == null) { coordinateFields.add(StringUtils.EMPTY); } else if (COL_NAME_GEOMETRY_LIST.contains(coordinateField)) { coordinateFields.add( - eventCoordinateService.getCoordinateField(program, coordinateField, ErrorCode.E7221)); + eventCoordinateService.validateCoordinateField( + program, coordinateField, ErrorCode.E7221)); } else if (EventQueryParams.EVENT_COORDINATE_FIELD.equals(coordinateField)) { coordinateFields.add( - eventCoordinateService.getCoordinateField( + eventCoordinateService.validateCoordinateField( program, COL_NAME_EVENT_GEOMETRY, ErrorCode.E7221)); } else if (EventQueryParams.ENROLLMENT_COORDINATE_FIELD.equals(coordinateField)) { coordinateFields.add( - eventCoordinateService.getCoordinateField( + eventCoordinateService.validateCoordinateField( program, COL_NAME_ENROLLMENT_GEOMETRY, ErrorCode.E7221)); } else if (EventQueryParams.TRACKER_COORDINATE_FIELD.equals(coordinateField)) { coordinateFields.add( - eventCoordinateService.getCoordinateField( + eventCoordinateService.validateCoordinateField( program, COL_NAME_TRACKED_ENTITY_GEOMETRY, ErrorCode.E7221)); } @@ -454,7 +448,7 @@ public List getCoordinateFields( if (dataElement != null) { coordinateFields.add( - eventCoordinateService.getCoordinateField( + eventCoordinateService.validateCoordinateField( dataElement.getValueType(), coordinateField, ErrorCode.E7219)); } @@ -462,7 +456,7 @@ public List getCoordinateFields( if (attribute != null) { coordinateFields.add( - eventCoordinateService.getCoordinateField( + eventCoordinateService.validateCoordinateField( attribute.getValueType(), coordinateField, ErrorCode.E7220)); } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EnrollmentQueryService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EnrollmentQueryService.java index d165518b1236..0f9f1ba769af 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EnrollmentQueryService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EnrollmentQueryService.java @@ -68,6 +68,7 @@ import org.hisp.dhis.common.DimensionItemKeywords.Keyword; import org.hisp.dhis.common.Grid; import org.hisp.dhis.common.GridHeader; +import org.hisp.dhis.db.sql.SqlBuilder; import org.hisp.dhis.system.grid.ListGrid; import org.hisp.dhis.util.Timer; import org.springframework.stereotype.Service; @@ -89,6 +90,8 @@ public class EnrollmentQueryService { private final SchemeIdHandler schemeIdHandler; + private final SqlBuilder sqlBuilder; + /** * Returns a list of enrollments matching the given query. * @@ -143,8 +146,9 @@ public Grid getEnrollments(EventQueryParams params) { * @return the {@link Grid} with headers. */ private Grid createGridWithHeaders(EventQueryParams params) { - return new ListGrid() - .addHeader( + Grid grid = new ListGrid(); + + grid.addHeader( new GridHeader( ENROLLMENT.getItem(), getEnrollmentLabel(params.getProgram(), ENROLLMENT.getName()), @@ -183,11 +187,15 @@ private Grid createGridWithHeaders(EventQueryParams params) { false, true)) .addHeader( - new GridHeader(LAST_UPDATED.getItem(), LAST_UPDATED.getName(), DATETIME, false, true)) - .addHeader(new GridHeader(GEOMETRY.getItem(), GEOMETRY.getName(), TEXT, false, true)) - .addHeader(new GridHeader(LONGITUDE.getItem(), LONGITUDE.getName(), NUMBER, false, true)) - .addHeader(new GridHeader(LATITUDE.getItem(), LATITUDE.getName(), NUMBER, false, true)) - .addHeader( + new GridHeader(LAST_UPDATED.getItem(), LAST_UPDATED.getName(), DATETIME, false, true)); + + if (sqlBuilder.supportsGeospatialData()) { + grid.addHeader(new GridHeader(GEOMETRY.getItem(), GEOMETRY.getName(), TEXT, false, true)) + .addHeader(new GridHeader(LONGITUDE.getItem(), LONGITUDE.getName(), NUMBER, false, true)) + .addHeader(new GridHeader(LATITUDE.getItem(), LATITUDE.getName(), NUMBER, false, true)); + } + + grid.addHeader( new GridHeader( ORG_UNIT_NAME.getItem(), getOrgUnitLabel(params.getProgram(), ORG_UNIT_NAME.getName()), @@ -205,6 +213,8 @@ private Grid createGridWithHeaders(EventQueryParams params) { new GridHeader(ORG_UNIT_CODE.getItem(), ORG_UNIT_CODE.getName(), TEXT, false, true)) .addHeader( new GridHeader(PROGRAM_STATUS.getItem(), PROGRAM_STATUS.getName(), TEXT, false, true)); + + return grid; } /** diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EventCoordinateService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EventCoordinateService.java index 4f89b6319ab1..f765760483f1 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EventCoordinateService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EventCoordinateService.java @@ -39,10 +39,10 @@ public interface EventCoordinateService { /** * Verifies the validity of fallback coordinate field. * - * @param isRegistration true when underlying program is registration - * @param coordinateField the name of coordinate field (uid or name) - * @return returns true if valid. - * @throws IllegalQueryException + * @param isRegistration true when program is registration. + * @param coordinateField the name of coordinate field (identifier or name). + * @return true if valid. + * @throws IllegalQueryException if validation failed. */ boolean isFallbackCoordinateFieldValid(boolean isRegistration, String coordinateField) throws IllegalQueryException; @@ -50,36 +50,36 @@ boolean isFallbackCoordinateFieldValid(boolean isRegistration, String coordinate /** * Provides list of coordinate fields. * - * @param program underlying program - * @param fallbackCoordinateField fallback - * @param defaultCoordinateFallback fallback cascade should be applied when true - * @return list of coordinate fields - * @throws IllegalQueryException + * @param program the program identifier. + * @param fallbackCoordinateField the fallback coordinate field. + * @param defaultCoordinateFallback fallback cascade should be applied when true. + * @return a list of coordinate fields. + * @throws IllegalQueryException if validation failed. */ List getFallbackCoordinateFields( String program, String fallbackCoordinateField, boolean defaultCoordinateFallback); /** - * Provides verified geometry. + * Validates the given coordinate field. * - * @param valueType value type - * @param field geometry + * @param valueType the {@link ValueType}. + * @param field the coordinate field. * @param errorCode code for standard error message * @return the coordinate field. - * @throws IllegalQueryException + * @throws IllegalQueryException if validation failed. */ - String getCoordinateField(ValueType valueType, String field, ErrorCode errorCode) + String validateCoordinateField(ValueType valueType, String field, ErrorCode errorCode) throws IllegalQueryException; /** - * Provides verified geometry. + * Validates the given coordinate field. * - * @param program underlying program - * @param coordinateField geometry - * @param errorCode code for standard error message + * @param program the program identifier. + * @param field the coordinate field. + * @param errorCode the {@link ErrorCode}. * @return the coordinate field. - * @throws IllegalQueryException + * @throws IllegalQueryException if validation failed. */ - public String getCoordinateField(String program, String coordinateField, ErrorCode errorCode) + public String validateCoordinateField(String program, String field, ErrorCode errorCode) throws IllegalQueryException; } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EventQueryService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EventQueryService.java index 2bac9553ab43..09fb1b9c0578 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EventQueryService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/EventQueryService.java @@ -70,7 +70,6 @@ import static org.hisp.dhis.feedback.ErrorCode.E7218; import java.util.List; -import javax.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import org.hisp.dhis.analytics.AnalyticsSecurityManager; import org.hisp.dhis.analytics.Rectangle; @@ -83,7 +82,7 @@ import org.hisp.dhis.common.DimensionItemKeywords.Keyword; import org.hisp.dhis.common.Grid; import org.hisp.dhis.common.GridHeader; -import org.hisp.dhis.system.database.DatabaseInfoProvider; +import org.hisp.dhis.db.sql.SqlBuilder; import org.hisp.dhis.system.grid.ListGrid; import org.hisp.dhis.util.Timer; import org.springframework.stereotype.Service; @@ -101,18 +100,11 @@ public class EventQueryService { private final EventQueryPlanner queryPlanner; - private final DatabaseInfoProvider databaseInfoProvider; - private final MetadataItemsHandler metadataHandler; private final SchemeIdHandler schemeIdHandler; - private boolean spatialSupport; - - @PostConstruct - void init() { - this.spatialSupport = databaseInfoProvider.getDatabaseInfo().isSpatialSupport(); - } + private final SqlBuilder sqlBuilder; /** * Returns a list of events matching the given query. @@ -168,7 +160,7 @@ public Grid getEvents(EventQueryParams params) { * @return event clusters as a {@link Grid} object. */ public Grid getEventClusters(EventQueryParams params) { - if (!spatialSupport) { + if (!isGeospatialSupport()) { throwIllegalQueryEx(E7218); } @@ -182,11 +174,12 @@ public Grid getEventClusters(EventQueryParams params) { queryValidator.validate(params); - Grid grid = new ListGrid(); - grid.addHeader(new GridHeader(COUNT.getItem(), COUNT.getName(), NUMBER, false, false)) - .addHeader(new GridHeader(CENTER.getItem(), CENTER.getName(), TEXT, false, false)) - .addHeader(new GridHeader(EXTENT.getItem(), EXTENT.getName(), TEXT, false, false)) - .addHeader(new GridHeader(POINTS.getItem(), POINTS.getName(), TEXT, false, false)); + Grid grid = + new ListGrid() + .addHeader(new GridHeader(COUNT.getItem(), COUNT.getName(), NUMBER, false, false)) + .addHeader(new GridHeader(CENTER.getItem(), CENTER.getName(), TEXT, false, false)) + .addHeader(new GridHeader(EXTENT.getItem(), EXTENT.getName(), TEXT, false, false)) + .addHeader(new GridHeader(POINTS.getItem(), POINTS.getName(), TEXT, false, false)); params = queryPlanner.planEventQuery(params); @@ -203,7 +196,7 @@ public Grid getEventClusters(EventQueryParams params) { * @return event clusters as a {@link Grid} object. */ public Rectangle getRectangle(EventQueryParams params) { - if (!spatialSupport) { + if (!isGeospatialSupport()) { throwIllegalQueryEx(E7218); } @@ -298,10 +291,13 @@ private Grid createGridWithHeaders(EventQueryParams params) { PROGRAM_INSTANCE.getItem(), PROGRAM_INSTANCE.getName(), TEXT, false, true)); } - grid.addHeader(new GridHeader(GEOMETRY.getItem(), GEOMETRY.getName(), TEXT, false, true)) - .addHeader(new GridHeader(LONGITUDE.getItem(), LONGITUDE.getName(), NUMBER, false, true)) - .addHeader(new GridHeader(LATITUDE.getItem(), LATITUDE.getName(), NUMBER, false, true)) - .addHeader( + if (isGeospatialSupport()) { + grid.addHeader(new GridHeader(GEOMETRY.getItem(), GEOMETRY.getName(), TEXT, false, true)) + .addHeader(new GridHeader(LONGITUDE.getItem(), LONGITUDE.getName(), NUMBER, false, true)) + .addHeader(new GridHeader(LATITUDE.getItem(), LATITUDE.getName(), NUMBER, false, true)); + } + + grid.addHeader( new GridHeader( ORG_UNIT_NAME.getItem(), getOrgUnitLabel(params.getProgram(), ORG_UNIT_NAME.getName()), @@ -354,4 +350,13 @@ private long addData(Grid grid, EventQueryParams params) { return count; } + + /** + * Indicates whether the DBMS supports geospatial data types and functions. + * + * @return true if the DBMS supports geospatial data types and functions. + */ + private boolean isGeospatialSupport() { + return sqlBuilder.supportsGeospatialData(); + } } 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 914007f42909..da2129f70c2f 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 @@ -75,6 +75,7 @@ import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.program.AnalyticsType; import org.hisp.dhis.program.ProgramIndicatorService; +import org.hisp.dhis.system.util.ListBuilder; import org.locationtech.jts.util.Assert; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.InvalidResultSetAccessException; @@ -102,23 +103,9 @@ public class JdbcEnrollmentAnalyticsManager extends AbstractJdbcEventAnalyticsMa private static final String IS_NOT_NULL = " is not null "; - private static final List COLUMNS = - List.of( - EnrollmentAnalyticsColumnName.ENROLLMENT_COLUMN_NAME, - EnrollmentAnalyticsColumnName.TRACKED_ENTITY_COLUMN_NAME, - EnrollmentAnalyticsColumnName.ENROLLMENT_DATE_COLUMN_NAME, - EnrollmentAnalyticsColumnName.OCCURRED_DATE_COLUMN_NAME, - EnrollmentAnalyticsColumnName.STORED_BY_COLUMN_NAME, - EnrollmentAnalyticsColumnName.CREATED_BY_DISPLAY_NAME_COLUMN_NAME, - EnrollmentAnalyticsColumnName.LAST_UPDATED_BY_DISPLAY_NAME_COLUMN_NAME, - EnrollmentAnalyticsColumnName.LAST_UPDATED_COLUMN_NAME, - "ST_AsGeoJSON(" + EnrollmentAnalyticsColumnName.ENROLLMENT_GEOMETRY_COLUMN_NAME + ")", - EnrollmentAnalyticsColumnName.LONGITUDE_COLUMN_NAME, - EnrollmentAnalyticsColumnName.LATITUDE_COLUMN_NAME, - EnrollmentAnalyticsColumnName.OU_NAME_COLUMN_NAME, - AbstractJdbcTableManager.OU_NAME_HIERARCHY_COLUMN_NAME, - EnrollmentAnalyticsColumnName.OU_CODE_COLUMN_NAME, - EnrollmentAnalyticsColumnName.ENROLLMENT_STATUS_COLUMN_NAME); + private static final String COLUMN_ENROLLMENT_GEOMETRY_GEOJSON = + String.format( + "ST_AsGeoJSON(%s)", EnrollmentAnalyticsColumnName.ENROLLMENT_GEOMETRY_COLUMN_NAME); public JdbcEnrollmentAnalyticsManager( @Qualifier("analyticsJdbcTemplate") JdbcTemplate jdbcTemplate, @@ -197,11 +184,11 @@ private void getEnrollments( } /** - * The method retrieves the amount of the supportive columns in database result set + * Retrieves the amount of the supportive columns in database result set. * * @param rowSet {@link SqlRowSet}. * @param columnName The name of the investigated column. - * @return If the investigated column has some supportive columns lie .exists or .status, the + * @return if the investigated column has some supportive columns like .exists or .status, the * count of the columns is returned. */ private long getRowSetOriginItems(SqlRowSet rowSet, String columnName) { @@ -214,7 +201,7 @@ private long getRowSetOriginItems(SqlRowSet rowSet, String columnName) { } /** - * Add value meta info into the grid. Value meta info is information about origin of the + * Adds value meta info into the grid. Value meta info is information about origin of the * repeatable stage value. * * @param grid the {@link Grid}. @@ -493,7 +480,7 @@ protected String getWhereClause(EventQueryParams params) { protected String getSelectClause(EventQueryParams params) { List selectCols = ListUtils.distinctUnion( - params.isAggregatedEnrollments() ? List.of("enrollment") : COLUMNS, + params.isAggregatedEnrollments() ? List.of("enrollment") : getStandardColumns(), getSelectColumns(params, false)); return "select " + StringUtils.join(selectCols, ",") + " "; @@ -661,6 +648,40 @@ protected String getColumn(QueryItem item, String suffix) { } } + /** + * Returns a list of names of standard columns. + * + * @return a list of names of standard columns. + */ + private List getStandardColumns() { + ListBuilder columns = new ListBuilder<>(); + + columns.add( + EnrollmentAnalyticsColumnName.ENROLLMENT_COLUMN_NAME, + EnrollmentAnalyticsColumnName.TRACKED_ENTITY_COLUMN_NAME, + EnrollmentAnalyticsColumnName.ENROLLMENT_DATE_COLUMN_NAME, + EnrollmentAnalyticsColumnName.OCCURRED_DATE_COLUMN_NAME, + EnrollmentAnalyticsColumnName.STORED_BY_COLUMN_NAME, + EnrollmentAnalyticsColumnName.CREATED_BY_DISPLAY_NAME_COLUMN_NAME, + EnrollmentAnalyticsColumnName.LAST_UPDATED_BY_DISPLAY_NAME_COLUMN_NAME, + EnrollmentAnalyticsColumnName.LAST_UPDATED_COLUMN_NAME); + + if (sqlBuilder.supportsGeospatialData()) { + columns.add( + COLUMN_ENROLLMENT_GEOMETRY_GEOJSON, + EnrollmentAnalyticsColumnName.LONGITUDE_COLUMN_NAME, + EnrollmentAnalyticsColumnName.LATITUDE_COLUMN_NAME); + } + + columns.add( + EnrollmentAnalyticsColumnName.OU_NAME_COLUMN_NAME, + AbstractJdbcTableManager.OU_NAME_HIERARCHY_COLUMN_NAME, + EnrollmentAnalyticsColumnName.OU_CODE_COLUMN_NAME, + EnrollmentAnalyticsColumnName.ENROLLMENT_STATUS_COLUMN_NAME); + + return columns.build(); + } + /** * Returns true if the item is a program attribute and the value type is an organizational unit. * 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 667a139a2209..23da08181fd1 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 @@ -37,7 +37,7 @@ import static org.hisp.dhis.analytics.common.ColumnHeader.LATITUDE; import static org.hisp.dhis.analytics.common.ColumnHeader.LONGITUDE; import static org.hisp.dhis.analytics.event.data.OrgUnitTableJoiner.joinOrgUnitTables; -import static org.hisp.dhis.analytics.table.JdbcEventAnalyticsTableManager.OU_GEOMETRY_COL_SUFFIX; +import static org.hisp.dhis.analytics.table.AbstractEventJdbcTableManager.OU_GEOMETRY_COL_SUFFIX; import static org.hisp.dhis.analytics.util.AnalyticsUtils.withExceptionHandling; import static org.hisp.dhis.common.DimensionalObject.ORGUNIT_DIM_ID; import static org.hisp.dhis.common.IdentifiableObjectUtils.getUids; @@ -47,7 +47,6 @@ import static org.hisp.dhis.util.DateUtils.toMediumDate; import static org.postgresql.util.PSQLState.DIVISION_BY_ZERO; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Date; @@ -86,6 +85,7 @@ import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.program.AnalyticsType; import org.hisp.dhis.program.ProgramIndicatorService; +import org.hisp.dhis.system.util.ListBuilder; import org.postgresql.util.PSQLException; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.DataAccessResourceFailureException; @@ -263,12 +263,15 @@ public long getEventCount(EventQueryParams params) { @Override public Rectangle getRectangle(EventQueryParams params) { + String coalesceClause = + getCoalesce( + params.getCoordinateFields(), FallbackCoordinateFieldType.EVENT_GEOMETRY.getValue()); + String sql = "select count(event) as " + COL_COUNT + ", ST_Extent(" - + getCoalesce( - params.getCoordinateFields(), FallbackCoordinateFieldType.EVENT_GEOMETRY.getValue()) + + coalesceClause + ") as " + COL_EXTENT + " "; @@ -306,6 +309,11 @@ private SqlRowSet queryForRows(String sql) { } } + @Override + protected AnalyticsType getAnalyticsType() { + return AnalyticsType.EVENT; + } + // ------------------------------------------------------------------------- // Supportive methods // ------------------------------------------------------------------------- @@ -317,44 +325,70 @@ private SqlRowSet queryForRows(String sql) { */ @Override protected String getSelectClause(EventQueryParams params) { - ImmutableList.Builder cols = - new ImmutableList.Builder() - .add( - EventAnalyticsColumnName.EVENT_COLUMN_NAME, - EventAnalyticsColumnName.PS_COLUMN_NAME, - EventAnalyticsColumnName.OCCURRED_DATE_COLUMN_NAME, - EventAnalyticsColumnName.STORED_BY_COLUMN_NAME, - EventAnalyticsColumnName.CREATED_BY_DISPLAYNAME_COLUMN_NAME, - EventAnalyticsColumnName.LAST_UPDATED_BY_DISPLAYNAME_COLUMN_NAME, - EventAnalyticsColumnName.LAST_UPDATED_COLUMN_NAME, - EventAnalyticsColumnName.SCHEDULED_DATE_COLUMN_NAME); + List standardColumns = getStandardColumns(params); + + List selectCols = + ListUtils.distinctUnion(standardColumns, getSelectColumns(params, false)); + + return "select " + StringUtils.join(selectCols, ",") + " "; + } + + /** + * Returns a list of names of standard columns. + * + * @param params the {@link EventQueryParams}. + * @return a list of names of standard columns. + */ + private List getStandardColumns(EventQueryParams params) { + ListBuilder columns = new ListBuilder<>(); + + columns.add( + EventAnalyticsColumnName.EVENT_COLUMN_NAME, + EventAnalyticsColumnName.PS_COLUMN_NAME, + EventAnalyticsColumnName.OCCURRED_DATE_COLUMN_NAME, + EventAnalyticsColumnName.STORED_BY_COLUMN_NAME, + EventAnalyticsColumnName.CREATED_BY_DISPLAYNAME_COLUMN_NAME, + EventAnalyticsColumnName.LAST_UPDATED_BY_DISPLAYNAME_COLUMN_NAME, + EventAnalyticsColumnName.LAST_UPDATED_COLUMN_NAME, + EventAnalyticsColumnName.SCHEDULED_DATE_COLUMN_NAME); if (params.getProgram().isRegistration()) { - cols.add( + columns.add( EventAnalyticsColumnName.ENROLLMENT_DATE_COLUMN_NAME, EventAnalyticsColumnName.ENROLLMENT_OCCURRED_DATE_COLUMN_NAME, EventAnalyticsColumnName.TRACKED_ENTITY_COLUMN_NAME, EventAnalyticsColumnName.ENROLLMENT_COLUMN_NAME); } - String coordinatesFieldsSnippet = - getCoalesce( - params.getCoordinateFields(), FallbackCoordinateFieldType.EVENT_GEOMETRY.getValue()); + if (sqlBuilder.supportsGeospatialData()) { + columns.add( + getCoordinateSelectExpression(params), + EventAnalyticsColumnName.LONGITUDE_COLUMN_NAME, + EventAnalyticsColumnName.LATITUDE_COLUMN_NAME); + } - cols.add( - "ST_AsGeoJSON(" + coordinatesFieldsSnippet + ", 6) as geometry", - EventAnalyticsColumnName.LONGITUDE_COLUMN_NAME, - EventAnalyticsColumnName.LATITUDE_COLUMN_NAME, + columns.add( EventAnalyticsColumnName.OU_NAME_COLUMN_NAME, AbstractJdbcTableManager.OU_NAME_HIERARCHY_COLUMN_NAME, EventAnalyticsColumnName.OU_CODE_COLUMN_NAME, EventAnalyticsColumnName.ENROLLMENT_STATUS_COLUMN_NAME, EventAnalyticsColumnName.EVENT_STATUS_COLUMN_NAME); - List selectCols = - ListUtils.distinctUnion(cols.build(), getSelectColumns(params, false)); + return columns.build(); + } - return "select " + StringUtils.join(selectCols, ",") + " "; + /** + * Returns a coordinate coalesce select expression. + * + * @param params the {@link EventQueryParams}. + * @return a coordinate coalesce select expression. + */ + private String getCoordinateSelectExpression(EventQueryParams params) { + String field = + getCoalesce( + params.getCoordinateFields(), FallbackCoordinateFieldType.EVENT_GEOMETRY.getValue()); + + return String.format("ST_AsGeoJSON(%s, 6) as geometry", field); } /** @@ -797,11 +831,6 @@ private String getProgramIndicatorSql(EventQueryParams params) { params.getLatestEndDate()); } - @Override - protected AnalyticsType getAnalyticsType() { - return AnalyticsType.EVENT; - } - /** * If the coordinateField points to an Item of type ORG UNIT, add the "_geom" suffix to the field * name. diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractEventJdbcTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractEventJdbcTableManager.java index f805967cbae5..092b97c7b657 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractEventJdbcTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractEventJdbcTableManager.java @@ -124,7 +124,7 @@ protected String getColumnExpression(ValueType valueType, String columnExpressio columnExpression + " = 'true'", "1", columnExpression + " = 'false'", "0", "null"); } else if (valueType.isDate()) { return getCastExpression(columnExpression, DATE_REGEXP, sqlBuilder.dataTypeTimestamp()); - } else if (valueType.isGeo() && isSpatialSupport()) { + } else if (valueType.isGeo() && isGeospatialSupport()) { return String.format( """ ST_GeomFromGeoJSON('{"type":"Point", "coordinates":' || (%s) || \ @@ -185,7 +185,7 @@ protected List getColumnForAttribute(TrackedEntityAttribut List columns = new ArrayList<>(); String valueColumn = getValueColumn(attribute); - DataType dataType = getColumnType(attribute.getValueType(), isSpatialSupport()); + DataType dataType = getColumnType(attribute.getValueType(), isGeospatialSupport()); String selectExpression = getColumnExpression(attribute.getValueType(), valueColumn); Skip skipIndex = skipIndex(attribute.getValueType(), attribute.hasOptionSet()); @@ -220,7 +220,7 @@ private List getColumnForOrgUnitAttribute( Validate.isTrue(attribute.getValueType().isOrganisationUnit()); List columns = new ArrayList<>(); - if (isSpatialSupport()) { + if (isGeospatialSupport()) { columns.add( AnalyticsTableColumn.builder() .name((attribute.getUid() + OU_GEOMETRY_COL_SUFFIX)) diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java index 732c93737af6..0507ab88dd38 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java @@ -349,15 +349,19 @@ private boolean tableExists(String name) { // ------------------------------------------------------------------------- /** - * Indicates whether spatial support is available. + * Indicates whether the DBMS supports geospatial data types and functions. * - * @return true if spatial support is available. + * @return true if the DBMS supports geospatial data types and functions. */ - protected boolean isSpatialSupport() { - return analyticsTableSettings.isSpatialSupport() && sqlBuilder.supportsGeospatialData(); + protected boolean isGeospatialSupport() { + return sqlBuilder.supportsGeospatialData(); } - /** Returns the analytics table name. */ + /** + * Returns the analytics table name. + * + * @return the analytics table name. + */ protected String getTableName() { return getAnalyticsTableType().getTableName(); } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManager.java index 2a07e0b69b89..6fa5118509ca 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManager.java @@ -140,7 +140,7 @@ public List getAnalyticsTables(AnalyticsTableUpdateParams params log.info( "Get tables using earliest: {}, spatial support: {}", params.getFromDate(), - isSpatialSupport()); + isGeospatialSupport()); List availableDataYears = periodDataProvider.getAvailableYears(analyticsTableSettings.getPeriodSource()); @@ -487,7 +487,7 @@ private List getColumnForDataElement( DataElement dataElement, boolean withLegendSet) { List columns = new ArrayList<>(); - DataType dataType = getColumnType(dataElement.getValueType(), isSpatialSupport()); + DataType dataType = getColumnType(dataElement.getValueType(), isGeospatialSupport()); String jsonExpression = sqlBuilder.jsonExtract("eventdatavalues", dataElement.getUid(), "value"); String columnExpression = getColumnExpression(dataElement.getValueType(), jsonExpression); @@ -540,7 +540,7 @@ private List getColumnForOrgUnitDataElement(DataElement da List columns = new ArrayList<>(); - if (isSpatialSupport()) { + if (isGeospatialSupport()) { columns.add( AnalyticsTableColumn.builder() .name((dataElement.getUid() + OU_GEOMETRY_COL_SUFFIX)) diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityAnalyticsTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityAnalyticsTableManager.java index ceecc38220db..778a38f997e4 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityAnalyticsTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcTrackedEntityAnalyticsTableManager.java @@ -213,7 +213,7 @@ private List getColumns( tea -> AnalyticsTableColumn.builder() .name(tea.getUid()) - .dataType(getColumnType(tea.getValueType(), isSpatialSupport())) + .dataType(getColumnType(tea.getValueType(), isGeospatialSupport())) .selectExpression( getColumnExpression(tea.getValueType(), quote(tea.getUid()) + ".value")) .build()) diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/setting/AnalyticsTableSettings.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/setting/AnalyticsTableSettings.java index e77f44a4ca82..6cdb9e72a369 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/setting/AnalyticsTableSettings.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/setting/AnalyticsTableSettings.java @@ -53,7 +53,6 @@ import org.hisp.dhis.period.PeriodDataProvider.PeriodSource; import org.hisp.dhis.setting.SystemSettings; import org.hisp.dhis.setting.SystemSettingsProvider; -import org.hisp.dhis.system.database.DatabaseInfo; import org.hisp.dhis.system.database.DatabaseInfoProvider; import org.springframework.stereotype.Component; @@ -134,16 +133,6 @@ public Set getSkipColumnDimensions() { return toSet(config.getProperty(ANALYTICS_TABLE_SKIP_COLUMN)); } - /** - * Indicates whether spatial database support is available. - * - * @return true if spatial database support is available. - */ - public boolean isSpatialSupport() { - DatabaseInfo info = databaseInfoProvider.getDatabaseInfo(); - return info != null && info.isSpatialSupport(); - } - /** * Returns the {@link Database} matching the given value. * diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/DefaultEventCoordinateServiceTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/DefaultEventCoordinateServiceTest.java index d7bc5b74197e..2d836bbbbc66 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/DefaultEventCoordinateServiceTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/DefaultEventCoordinateServiceTest.java @@ -69,7 +69,7 @@ class DefaultEventCoordinateServiceTest { void testGetCoordinateFieldOrFail(String geometry) { when(programService.getProgram(any(String.class))).thenReturn(createProgram('A')); - assertEquals(geometry, service.getCoordinateField("A", geometry, ErrorCode.E7232)); + assertEquals(geometry, service.validateCoordinateField("A", geometry, ErrorCode.E7232)); } @Test diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/EventQueryServiceTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/EventQueryServiceTest.java index 4d5b916e4475..3865f5f258c1 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/EventQueryServiceTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/event/data/EventQueryServiceTest.java @@ -53,9 +53,9 @@ import org.hisp.dhis.analytics.tracker.MetadataItemsHandler; import org.hisp.dhis.analytics.tracker.SchemeIdHandler; import org.hisp.dhis.common.IdScheme; +import org.hisp.dhis.db.sql.SqlBuilder; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.program.Program; -import org.hisp.dhis.system.database.DatabaseInfoProvider; import org.hisp.dhis.user.SystemUser; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -85,7 +85,7 @@ class EventQueryServiceTest { @Mock private EventQueryPlanner queryPlanner; - @Mock private DatabaseInfoProvider databaseInfoProvider; + @Mock private SqlBuilder sqlBuilder; @Mock private SchemeIdResponseMapper schemeIdResponseMapper; diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/AbstractEventJdbcTableManagerTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/AbstractEventJdbcTableManagerTest.java index b26200ffb396..a3be6e2be37d 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/AbstractEventJdbcTableManagerTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/AbstractEventJdbcTableManagerTest.java @@ -142,7 +142,7 @@ void testGetSelectExpressionText() { @Test void testGetSelectExpressionGeometry() { - when(manager.isSpatialSupport()).thenReturn(true); + when(manager.isGeospatialSupport()).thenReturn(true); String expected = """ diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/JdbcEnrollmentAnalyticsTableManagerTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/JdbcEnrollmentAnalyticsTableManagerTest.java index 4d7463b8fb6e..a85f5cac3e71 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/JdbcEnrollmentAnalyticsTableManagerTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/JdbcEnrollmentAnalyticsTableManagerTest.java @@ -110,7 +110,6 @@ class JdbcEnrollmentAnalyticsTableManagerTest { @BeforeEach public void setUp() { - when(analyticsTableSettings.isSpatialSupport()).thenReturn(true); lenient().when(settingsProvider.getCurrentSettings()).thenReturn(SystemSettings.of(Map.of())); } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManagerTest.java b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManagerTest.java index 7bc0ca4c0208..84289b558a3a 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManagerTest.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/JdbcEventAnalyticsTableManagerTest.java @@ -357,7 +357,6 @@ private AnalyticsTableColumn getColumn(String column, AnalyticsTable analyticsTa @Test void verifyGetTableWithDataElements() { - when(analyticsTableSettings.isSpatialSupport()).thenReturn(true); Program program = createProgram('A'); DataElement d1 = createDataElement('Z', ValueType.TEXT, AggregationType.SUM); @@ -478,7 +477,6 @@ void verifyGetTableWithDataElements() { @Test void verifyGetTableWithTrackedEntityAttribute() { - when(analyticsTableSettings.isSpatialSupport()).thenReturn(true); Program program = createProgram('A'); TrackedEntityAttribute tea1 = rnd.nextObject(TrackedEntityAttribute.class); @@ -552,7 +550,6 @@ void verifyGetTableWithTrackedEntityAttribute() { @Test void verifyDataElementTypeOrgUnitFetchesOuNameWhenPopulatingEventAnalyticsTable() { ArgumentCaptor sql = ArgumentCaptor.forClass(String.class); - when(analyticsTableSettings.isSpatialSupport()).thenReturn(true); Program programA = createProgram('A'); DataElement d5 = createDataElement('G', ValueType.ORGANISATION_UNIT, AggregationType.NONE); @@ -605,7 +602,6 @@ void verifyDataElementTypeOrgUnitFetchesOuNameWhenPopulatingEventAnalyticsTable( @Test void verifyTeiTypeOrgUnitFetchesOuNameWhenPopulatingEventAnalyticsTable() { ArgumentCaptor sql = ArgumentCaptor.forClass(String.class); - when(analyticsTableSettings.isSpatialSupport()).thenReturn(true); Program programA = createProgram('A'); TrackedEntityAttribute tea = createTrackedEntityAttribute('a', ValueType.ORGANISATION_UNIT); @@ -661,7 +657,6 @@ void verifyTeiTypeOrgUnitFetchesOuNameWhenPopulatingEventAnalyticsTable() { @Test void verifyOrgUnitOwnershipJoinsWhenPopulatingEventAnalyticsTable() { ArgumentCaptor sql = ArgumentCaptor.forClass(String.class); - when(analyticsTableSettings.isSpatialSupport()).thenReturn(true); Program programA = createProgram('A'); TrackedEntityAttribute tea = createTrackedEntityAttribute('a', ValueType.ORGANISATION_UNIT); @@ -878,7 +873,6 @@ private void match(AnalyticsTableColumn col) { @Test void verifyTeaTypeOrgUnitFetchesOuNameWhenPopulatingEventAnalyticsTable() { ArgumentCaptor sql = ArgumentCaptor.forClass(String.class); - when(analyticsTableSettings.isSpatialSupport()).thenReturn(true); Program programA = createProgram('A'); TrackedEntityAttribute tea = createTrackedEntityAttribute('a', ValueType.ORGANISATION_UNIT); diff --git a/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/ClickHouseSqlBuilder.java b/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/ClickHouseSqlBuilder.java index b4677fd81538..4cb4e0bec09e 100644 --- a/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/ClickHouseSqlBuilder.java +++ b/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/ClickHouseSqlBuilder.java @@ -34,6 +34,7 @@ import org.apache.commons.lang3.Validate; import org.hisp.dhis.analytics.DataType; import org.hisp.dhis.db.model.Column; +import org.hisp.dhis.db.model.Database; import org.hisp.dhis.db.model.Index; import org.hisp.dhis.db.model.Table; import org.hisp.dhis.db.model.constraint.Nullable; @@ -48,6 +49,13 @@ public class ClickHouseSqlBuilder extends AbstractSqlBuilder { private static final String QUOTE = "\""; + // Database + + @Override + public Database getDatabase() { + return Database.CLICKHOUSE; + } + // Data types @Override diff --git a/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/DorisSqlBuilder.java b/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/DorisSqlBuilder.java index 1b01c4d1a5e9..fac63f30672e 100644 --- a/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/DorisSqlBuilder.java +++ b/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/DorisSqlBuilder.java @@ -34,6 +34,7 @@ import org.apache.commons.lang3.Validate; import org.hisp.dhis.analytics.DataType; import org.hisp.dhis.db.model.Column; +import org.hisp.dhis.db.model.Database; import org.hisp.dhis.db.model.Index; import org.hisp.dhis.db.model.Table; import org.hisp.dhis.db.model.TablePartition; @@ -50,6 +51,13 @@ public class DorisSqlBuilder extends AbstractSqlBuilder { private static final String QUOTE = "`"; + // Database + + @Override + public Database getDatabase() { + return Database.DORIS; + } + // Data types @Override diff --git a/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/PostgreSqlBuilder.java b/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/PostgreSqlBuilder.java index a6898fb73e19..9c18e3c45fe9 100644 --- a/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/PostgreSqlBuilder.java +++ b/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/PostgreSqlBuilder.java @@ -32,6 +32,7 @@ import org.hisp.dhis.analytics.DataType; import org.hisp.dhis.db.model.Collation; import org.hisp.dhis.db.model.Column; +import org.hisp.dhis.db.model.Database; import org.hisp.dhis.db.model.Index; import org.hisp.dhis.db.model.Table; import org.hisp.dhis.db.model.constraint.Nullable; @@ -48,6 +49,13 @@ public class PostgreSqlBuilder extends AbstractSqlBuilder { private static final String QUOTE = "\""; + // Database + + @Override + public Database getDatabase() { + return Database.POSTGRESQL; + } + // Data types @Override diff --git a/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/SqlBuilder.java b/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/SqlBuilder.java index 5ac7c3c723bb..b0e22acac09b 100644 --- a/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/SqlBuilder.java +++ b/dhis-2/dhis-support/dhis-support-sql/src/main/java/org/hisp/dhis/db/sql/SqlBuilder.java @@ -29,6 +29,7 @@ import java.util.Collection; import org.hisp.dhis.analytics.DataType; +import org.hisp.dhis.db.model.Database; import org.hisp.dhis.db.model.Index; import org.hisp.dhis.db.model.Table; @@ -39,6 +40,13 @@ */ public interface SqlBuilder { + // Database + + /** + * @return the {@link Database}. + */ + Database getDatabase(); + // Data types /** diff --git a/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/database/HibernateDatabaseInfoProvider.java b/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/database/HibernateDatabaseInfoProvider.java index 94e8f9a913bb..df01cc261243 100644 --- a/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/database/HibernateDatabaseInfoProvider.java +++ b/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/database/HibernateDatabaseInfoProvider.java @@ -136,7 +136,7 @@ public void init() { .name(internalInfo.getDatabase()) .user(StringUtils.defaultIfEmpty(internalInfo.getUser(), user)) .url(url) - .spatialSupport(spatialSupport) + .spatialSupport(true) // Always true, for backwards compatibility .databaseVersion(internalInfo.getVersion()) .build(); } diff --git a/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ListBuilder.java b/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ListBuilder.java new file mode 100644 index 000000000000..fccce9c5c4bd --- /dev/null +++ b/dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/ListBuilder.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2004-2024, 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. + */ +package org.hisp.dhis.system.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** Builder of immutable lists. */ +public class ListBuilder { + private final List list; + + public ListBuilder() { + list = new ArrayList<>(); + } + + public ListBuilder(List initial) { + list = new ArrayList<>(initial); + } + + public final ListBuilder addAll(List items) { + this.list.addAll(items); + return this; + } + + @SafeVarargs + public final ListBuilder add(T... items) { + this.list.addAll(Arrays.asList(items)); + return this; + } + + public final ListBuilder add(T item) { + this.list.add(item); + return this; + } + + public List build() { + return Collections.unmodifiableList(list); + } +} diff --git a/dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/ListBuilderTest.java b/dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/ListBuilderTest.java new file mode 100644 index 000000000000..6e5ae1c44747 --- /dev/null +++ b/dhis-2/dhis-support/dhis-support-system/src/test/java/org/hisp/dhis/system/util/ListBuilderTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2024, 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. + */ +package org.hisp.dhis.system.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class ListBuilderTest { + @Test + void testAdd() { + List actual = + new ListBuilder().add("one").addAll(List.of("two", "three")).build(); + + List expected = List.of("one", "two", "three"); + + assertEquals(expected, actual); + } + + @Test + void testAddWithInitial() { + List actual = + new ListBuilder(List.of("one")).addAll(List.of("two", "three")).build(); + + List expected = List.of("one", "two", "three"); + + assertEquals(expected, actual); + } +} diff --git a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/analytics/event/data/EventDataQueryServiceTest.java b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/analytics/event/data/EventDataQueryServiceTest.java index 9bd97b9ec042..97484aad9035 100644 --- a/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/analytics/event/data/EventDataQueryServiceTest.java +++ b/dhis-2/dhis-test-integration/src/test/java/org/hisp/dhis/analytics/event/data/EventDataQueryServiceTest.java @@ -503,17 +503,18 @@ void testGetCoordinateField() { assertEquals( List.of("eventgeometry"), dataQueryService.getCoordinateFields( - prA.getUid(), EventQueryParams.EVENT_COORDINATE_FIELD, null, false)); + toRequest(prA.getUid(), EventQueryParams.EVENT_COORDINATE_FIELD, null, false))); assertEquals( List.of("enrollmentgeometry"), dataQueryService.getCoordinateFields( - prA.getUid(), EventQueryParams.ENROLLMENT_COORDINATE_FIELD, null, false)); + toRequest(prA.getUid(), EventQueryParams.ENROLLMENT_COORDINATE_FIELD, null, false))); assertEquals( List.of("eventgeometry"), - dataQueryService.getCoordinateFields(prA.getUid(), null, "eventgeometry", false)); + dataQueryService.getCoordinateFields( + toRequest(prA.getUid(), null, "eventgeometry", false))); assertEquals( List.of(deC.getUid()), - dataQueryService.getCoordinateFields(prA.getUid(), deC.getUid(), null, false)); + dataQueryService.getCoordinateFields(toRequest(prA.getUid(), deC.getUid(), null, false))); } @Test @@ -525,30 +526,30 @@ void testGetBackwardCompatibleCoordinateField() { assertEquals( List.of(COL_NAME_EVENT_GEOMETRY), dataQueryService.getCoordinateFields( - prA.getUid(), OLD_COL_NAME_EVENT_GEOMETRY, null, false)); + toRequest(prA.getUid(), OLD_COL_NAME_EVENT_GEOMETRY, null, false))); assertEquals( List.of(COL_NAME_ENROLLMENT_GEOMETRY), dataQueryService.getCoordinateFields( - prA.getUid(), OLD_COL_NAME_ENROLLMENT_GEOMETRY, null, false)); + toRequest(prA.getUid(), OLD_COL_NAME_ENROLLMENT_GEOMETRY, null, false))); assertEquals( List.of(COL_NAME_TRACKED_ENTITY_GEOMETRY), dataQueryService.getCoordinateFields( - prA.getUid(), OLD_COL_NAME_TRACKED_ENTITY_GEOMETRY, null, false)); + toRequest(prA.getUid(), OLD_COL_NAME_TRACKED_ENTITY_GEOMETRY, null, false))); assertEquals( List.of(COL_NAME_EVENT_GEOMETRY), dataQueryService.getCoordinateFields( - prA.getUid(), null, OLD_COL_NAME_EVENT_GEOMETRY, false)); + toRequest(prA.getUid(), null, OLD_COL_NAME_EVENT_GEOMETRY, false))); assertEquals( List.of(COL_NAME_ENROLLMENT_GEOMETRY), dataQueryService.getCoordinateFields( - prA.getUid(), null, OLD_COL_NAME_ENROLLMENT_GEOMETRY, false)); + toRequest(prA.getUid(), null, OLD_COL_NAME_ENROLLMENT_GEOMETRY, false))); assertEquals( List.of(COL_NAME_TRACKED_ENTITY_GEOMETRY), dataQueryService.getCoordinateFields( - prA.getUid(), null, OLD_COL_NAME_TRACKED_ENTITY_GEOMETRY, false)); + toRequest(prA.getUid(), null, OLD_COL_NAME_TRACKED_ENTITY_GEOMETRY, false))); assertEquals( List.of(deC.getUid()), - dataQueryService.getCoordinateFields(prA.getUid(), deC.getUid(), null, false)); + dataQueryService.getCoordinateFields(toRequest(prA.getUid(), deC.getUid(), null, false))); } @Test @@ -560,7 +561,7 @@ void testGetInvalidCoordinateFieldException() { // Then assertThrows( IllegalQueryException.class, - () -> dataQueryService.getCoordinateFields(programUid, "badfield", null, false)); + () -> dataQueryService.getCoordinateFields(toRequest(programUid, "badfield", null, false))); } @Test @@ -572,6 +573,21 @@ void testGetNonCoordinateValueTypeCoordinateFieldException() { // Then assertThrows( IllegalQueryException.class, - () -> dataQueryService.getCoordinateFields(programUid, "tegeometry", "badfallback", false)); + () -> + dataQueryService.getCoordinateFields( + toRequest(programUid, "tegeometry", "badfallback", false))); + } + + private EventDataQueryRequest toRequest( + String program, + String coordinateField, + String fallbackCoordinateField, + boolean isDefaultCoordinateFallback) { + return EventDataQueryRequest.builder() + .program(program) + .coordinateField(coordinateField) + .fallbackCoordinateField(fallbackCoordinateField) + .defaultCoordinateFallback(isDefaultCoordinateFallback) + .build(); } }