From af94d50f389a646f70df7220714d78c890a7ea48 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 17 Apr 2023 17:45:23 +1000 Subject: [PATCH 01/23] [ANDROSDK-1571] Implement d2:MaxValue and d2:MinValue functions --- .../internal/ProgramIndicatorParserUtils.kt | 2 + .../internal/function/D2MaxValue.kt | 38 +++++++++ .../internal/function/D2MinValue.kt | 38 +++++++++ .../function/ProgramMinMaxFunction.kt | 84 +++++++++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2MaxValue.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2MinValue.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramMinMaxFunction.kt diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorParserUtils.kt index ef4dd55248..cdad71f9a0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorParserUtils.kt @@ -74,7 +74,9 @@ internal object ProgramIndicatorParserUtils { ExpressionParser.D2_COUNT_IF_VALUE to D2CountIfValue(), ExpressionParser.D2_DAYS_BETWEEN to D2DaysBetween(), ExpressionParser.D2_HAS_VALUE to D2HasValue(), + ExpressionParser.D2_MAX_VALUE to D2MaxValue(), ExpressionParser.D2_MINUTES_BETWEEN to D2MinutesBetween(), + ExpressionParser.D2_MIN_VALUE to D2MinValue(), ExpressionParser.D2_MONTHS_BETWEEN to D2MonthsBetween(), ExpressionParser.D2_OIZP to D2Oizp(), ExpressionParser.D2_RELATIONSHIP_COUNT to D2RelationshipCount(), diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2MaxValue.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2MaxValue.kt new file mode 100644 index 0000000000..4c6b7f5028 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2MaxValue.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2022, 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.android.core.program.programindicatorengine.internal.function + +internal class D2MaxValue : ProgramMinMaxFunction() { + override fun evaluateFn(values: List): Any? { + return values.maxOrNull() + } + + override fun getSqlAggregator(): String { + return "MAX" + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2MinValue.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2MinValue.kt new file mode 100644 index 0000000000..ed19c16e92 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2MinValue.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2022, 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.android.core.program.programindicatorengine.internal.function + +internal class D2MinValue : ProgramMinMaxFunction() { + override fun evaluateFn(values: List): Any? { + return values.minOrNull() + } + + override fun getSqlAggregator(): String { + return "MIN" + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramMinMaxFunction.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramMinMaxFunction.kt new file mode 100644 index 0000000000..0de9fa2b68 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramMinMaxFunction.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004-2022, 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.android.core.program.programindicatorengine.internal.function + +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.event.EventTableInfo +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLUtils.getDataValueEventWhereClause +import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueTableInfo +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +internal abstract class ProgramMinMaxFunction : ExpressionItem { + + abstract fun evaluateFn(values: List): Any? + + abstract fun getSqlAggregator(): String + + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + val programStageUid = ctx.uid0.text + val mappedValues: List = (visitor.programIndicatorContext!!.events[programStageUid] ?: emptyList()) + .mapNotNull { event -> + when (ctx.uid1) { + null -> { + // It has the form PS_EVENTDATE:programStageUid + event.eventDate()?.let { DateUtils.DATE_FORMAT.format(it) } + } + else -> { + event.trackedEntityDataValues()?.find { it.dataElement() == ctx.uid1.text }?.value() + } + } + } + + return evaluateFn(mappedValues) + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val isDataElement = ctx.uid1.text != null + val programStageId = ctx.uid0.text + + return if (isDataElement) { + val dataElementId = ctx.uid1.text + "(SELECT ${getSqlAggregator()}(${TrackedEntityDataValueTableInfo.Columns.VALUE}) " + + "FROM ${TrackedEntityDataValueTableInfo.TABLE_INFO.name()} " + + "INNER JOIN ${EventTableInfo.TABLE_INFO.name()} " + + "ON ${TrackedEntityDataValueTableInfo.Columns.EVENT} = ${EventTableInfo.Columns.UID} " + + "WHERE ${TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT} = '$dataElementId' " + + "AND ${EventTableInfo.Columns.PROGRAM_STAGE} = '$programStageId' " + + "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext!!.programIndicator)} " + + ")" + } else { + "(SELECT ${getSqlAggregator()}(${EventTableInfo.Columns.EVENT_DATE}) " + + "FROM ${EventTableInfo.TABLE_INFO.name()} " + + "WHERE ${EventTableInfo.Columns.PROGRAM_STAGE} = '$programStageId' " + + "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext!!.programIndicator)} " + + ")" + } + } +} From 5e030368307228fea76ec5d4c9aad8ec176b9c14 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 18 Apr 2023 20:19:20 +1000 Subject: [PATCH 02/23] [ANDROSDK-1571] Add tests --- ...amIndicatorSQLExecutorIntegrationShould.kt | 67 ++++++++++++++++++- .../function/ProgramMinMaxFunction.kt | 24 +++---- .../ProgramIndicatorExecutorShould.kt | 20 ++++++ 3 files changed, 98 insertions(+), 13 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt index 68992472b6..c4d83dfa81 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt @@ -49,6 +49,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEv import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.trackedEntity1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.trackedEntity2 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.trackedEntityType +import org.hisp.dhis.android.core.arch.helpers.DateUtils import org.hisp.dhis.android.core.common.AggregationType import org.hisp.dhis.android.core.common.AnalyticsType import org.hisp.dhis.android.core.enrollment.EnrollmentStatus @@ -432,6 +433,70 @@ internal class ProgramIndicatorSQLExecutorIntegrationShould : BaseProgramIndicat ).isEqualTo("2") } + @Test + fun should_evaluate_max_min_functions() { + helper.createTrackedEntity(trackedEntity1.uid(), orgunitChild1.uid(), trackedEntityType.uid()) + val enrollment1 = generator.generate() + helper.createEnrollment(trackedEntity1.uid(), enrollment1, program.uid(), orgunitChild1.uid()) + + val event1 = generator.generate() + val eventDate1 = DateUtils.SIMPLE_DATE_FORMAT.parse("2023-02-03") + helper.createTrackerEvent( + event1, enrollment1, program.uid(), programStage1.uid(), orgunitChild1.uid(), + eventDate = eventDate1 + ) + + val event2 = generator.generate() + val eventDate2 = DateUtils.SIMPLE_DATE_FORMAT.parse("2023-02-06") + helper.createTrackerEvent( + event2, enrollment1, program.uid(), programStage1.uid(), orgunitChild1.uid(), + eventDate = eventDate2 + ) + + helper.insertTrackedEntityDataValue(event1, dataElement1.uid(), "1") + helper.insertTrackedEntityDataValue(event1, dataElement2.uid(), "50") + helper.insertTrackedEntityDataValue(event2, dataElement1.uid(), "5") + helper.insertTrackedEntityDataValue(event2, dataElement2.uid(), "10") + helper.insertTrackedEntityDataValue(event2, dataElement3.uid(), "500") + + listOf( + Triple("d2:maxValue(${de(programStage1.uid(), dataElement1.uid())})", AnalyticsType.EVENT, "6"), + Triple("d2:maxValue(${de(programStage1.uid(), dataElement2.uid())})", AnalyticsType.EVENT, "60"), + Triple("d2:maxValue(${de(programStage1.uid(), dataElement3.uid())})", AnalyticsType.EVENT, "500"), + Triple("d2:maxValue(${de(programStage1.uid(), dataElement1.uid())})", AnalyticsType.ENROLLMENT, "5"), + Triple("d2:maxValue(${de(programStage1.uid(), dataElement2.uid())})", AnalyticsType.ENROLLMENT, "50"), + Triple("d2:maxValue(${de(programStage1.uid(), dataElement3.uid())})", AnalyticsType.ENROLLMENT, "500"), + + Triple("d2:minValue(${de(programStage1.uid(), dataElement1.uid())})", AnalyticsType.EVENT, "6"), + Triple("d2:minValue(${de(programStage1.uid(), dataElement2.uid())})", AnalyticsType.EVENT, "60"), + Triple("d2:minValue(${de(programStage1.uid(), dataElement3.uid())})", AnalyticsType.EVENT, "500"), + Triple("d2:minValue(${de(programStage1.uid(), dataElement1.uid())})", AnalyticsType.ENROLLMENT, "1"), + Triple("d2:minValue(${de(programStage1.uid(), dataElement2.uid())})", AnalyticsType.ENROLLMENT, "10"), + Triple("d2:minValue(${de(programStage1.uid(), dataElement3.uid())})", AnalyticsType.ENROLLMENT, "500"), + ).forEach { + assertThat( + evaluateProgramIndicator( + expression = it.first, + analyticsType = it.second + ) + ).isEqualTo(it.third) + } + + assertThat( + evaluateProgramIndicator( + expression = "d2:daysBetween('2023-02-01', d2:maxValue(PS_EVENTDATE:${programStage1.uid()}))", + analyticsType = AnalyticsType.ENROLLMENT + ) + ).isEqualTo("5") + + assertThat( + evaluateProgramIndicator( + expression = "d2:daysBetween('2023-02-01', d2:minValue(PS_EVENTDATE:${programStage1.uid()}))", + analyticsType = AnalyticsType.ENROLLMENT + ) + ).isEqualTo("2") + } + @Test fun should_evaluate_null_functions() { helper.createTrackedEntity(trackedEntity1.uid(), orgunitChild1.uid(), trackedEntityType.uid()) @@ -658,7 +723,7 @@ internal class ProgramIndicatorSQLExecutorIntegrationShould : BaseProgramIndicat } @Test - fun should_evaluate_max_min_functions() { + fun should_evaluate_greatest_least_functions() { helper.createTrackedEntity(trackedEntity1.uid(), orgunitChild1.uid(), trackedEntityType.uid()) val enrollment1 = generator.generate() helper.createEnrollment(trackedEntity1.uid(), enrollment1, program.uid(), orgunitChild1.uid()) diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramMinMaxFunction.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramMinMaxFunction.kt index 0de9fa2b68..412bb79276 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramMinMaxFunction.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramMinMaxFunction.kt @@ -60,25 +60,25 @@ internal abstract class ProgramMinMaxFunction : ExpressionItem { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val isDataElement = ctx.uid1.text != null + val isDataElement = ctx.uid1 != null val programStageId = ctx.uid0.text return if (isDataElement) { val dataElementId = ctx.uid1.text "(SELECT ${getSqlAggregator()}(${TrackedEntityDataValueTableInfo.Columns.VALUE}) " + - "FROM ${TrackedEntityDataValueTableInfo.TABLE_INFO.name()} " + - "INNER JOIN ${EventTableInfo.TABLE_INFO.name()} " + - "ON ${TrackedEntityDataValueTableInfo.Columns.EVENT} = ${EventTableInfo.Columns.UID} " + - "WHERE ${TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT} = '$dataElementId' " + - "AND ${EventTableInfo.Columns.PROGRAM_STAGE} = '$programStageId' " + - "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext!!.programIndicator)} " + - ")" + "FROM ${TrackedEntityDataValueTableInfo.TABLE_INFO.name()} " + + "INNER JOIN ${EventTableInfo.TABLE_INFO.name()} " + + "ON ${TrackedEntityDataValueTableInfo.Columns.EVENT} = ${EventTableInfo.Columns.UID} " + + "WHERE ${TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT} = '$dataElementId' " + + "AND ${EventTableInfo.Columns.PROGRAM_STAGE} = '$programStageId' " + + "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext!!.programIndicator)} " + + ")" } else { "(SELECT ${getSqlAggregator()}(${EventTableInfo.Columns.EVENT_DATE}) " + - "FROM ${EventTableInfo.TABLE_INFO.name()} " + - "WHERE ${EventTableInfo.Columns.PROGRAM_STAGE} = '$programStageId' " + - "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext!!.programIndicator)} " + - ")" + "FROM ${EventTableInfo.TABLE_INFO.name()} " + + "WHERE ${EventTableInfo.Columns.PROGRAM_STAGE} = '$programStageId' " + + "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext!!.programIndicator)} " + + ")" } } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutorShould.kt index 9fbd55eb63..c159c2e182 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutorShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutorShould.kt @@ -299,6 +299,26 @@ class ProgramIndicatorExecutorShould { assertThat(resultFalse).isEqualTo("50") } + @Test + fun evaluate_max_min_functions() { + whenever(dataValue2_1.value()) doReturn "4" + whenever(dataValue2_2.value()) doReturn "8" + whenever(event2_1.eventDate()) doReturn DateUtils.DATE_FORMAT.parse("2020-01-05T00:00:00.000") + whenever(event2_2.eventDate()) doReturn DateUtils.DATE_FORMAT.parse("2020-01-10T00:00:00.000") + + setExpression("d2:maxValue(${de(programStage2, dataElementUid2)})") + assertThat(programIndicatorExecutor.getProgramIndicatorValue(programIndicator)).isEqualTo("8") + + setExpression("d2:minValue(${de(programStage2, dataElementUid2)})") + assertThat(programIndicatorExecutor.getProgramIndicatorValue(programIndicator)).isEqualTo("4") + + setExpression("d2:daysBetween('2020-01-01', d2:maxValue(PS_EVENTDATE:$programStage2))") + assertThat(programIndicatorExecutor.getProgramIndicatorValue(programIndicator)).isEqualTo("9") + + setExpression("d2:daysBetween('2020-01-01', d2:minValue(PS_EVENTDATE:$programStage2))") + assertThat(programIndicatorExecutor.getProgramIndicatorValue(programIndicator)).isEqualTo("4") + } + @Test fun evaluate_boolean_elements() { setExpression("${de(programStage1, dataElementUid1)} + ${att(attributeUid1)}") From cee73bb0c28a1bb8b22e5ad7012f969f793e0980 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 18 Apr 2023 20:21:34 +1000 Subject: [PATCH 03/23] [ANDROSDK-1571] Update docs --- docs/content/developer/analytics.md | 204 +++++++++--------- .../developer/program-indicator-engine.md | 182 ++++++++-------- 2 files changed, 193 insertions(+), 193 deletions(-) diff --git a/docs/content/developer/analytics.md b/docs/content/developer/analytics.md index e09beb0c5a..ef2a58d636 100644 --- a/docs/content/developer/analytics.md +++ b/docs/content/developer/analytics.md @@ -378,108 +378,108 @@ This table shows the functionality supported by the Indicator dimension item com This table shows the functionality supported by the ProgramIndicator dimension item compared to the backend analytics. -| Type | Element | Backend | Android SDK | -|----------------------------|----------------------|-----------|---------------| -| **Mathematical:** | Parenthesis | Yes | Yes | -| | Plus (+) | Yes | Yes | -| | Minus (-) | Yes | Yes | -| | Power (^) | Yes | No | -| | Multiply (*) | Yes | Yes | -| | Divide (/) | Yes | Yes | -| | Modulus (%) | Yes | Yes | -| **Logical:** | NOT | Yes | Yes | -| | ! | Yes | Yes | -| | AND | Yes | Yes | -| | && | Yes | Yes | -| | OR | Yes | Yes | -| | || | Yes | Yes | -| **Comparison:** | Equal (==) | Yes | Yes | -| | NotEqual (!=) | Yes | Yes | -| | GT (>) | Yes | Yes | -| | LT (<) | Yes | Yes | -| | GE (>=) | Yes | Yes | -| | LE (<=) | Yes | Yes | -| **Literals:** | null | Yes | Yes | -| **Functions:** | FirstNonNull | Yes | Yes | -| | Greatest | Yes | Yes | -| | If | Yes | Yes | -| | IsNotNull | Yes | Yes | -| | IsNull | Yes | Yes | -| | Least | Yes | Yes | -| | Log | Yes | No | -| | Log10 | Yes | No | -| | PeriodOffset | Yes | No | -| **D2 functions:** | D2AddDays | No | No | -| | D2Ceil | No | No | -| | D2Concatenate | No | No | -| | D2Condition | Yes | Yes | -| | D2Count | Yes | Yes | -| | D2CountIfCondition | Yes | Yes | -| | D2CountIfValue | Yes | Yes | -| | D2DaysBetween | Yes | Yes | -| | D2Floor | No | No | -| | D2HasValue | Yes | Yes | -| | D2Left | No | No | -| | D2Length | No | No | -| | D2MaxValue | Yes | No | -| | D2MinutesBetween | Yes | Yes | -| | D2MinValue | Yes | No | -| | D2Modulus | No | No | -| | D2MonthsBetween | Yes | Yes | -| | D2Oizp | Yes | Yes | -| | D2RelationshipCount | Yes | Yes | -| | D2Right | No | No | -| | D2Round | No | No | -| | D2Split | No | No | -| | D2Substring | No | No | -| | D2ValidatePattern | No | No | -| | D2WeeksBetween | Yes | Yes | -| | D2YearsBetween | Yes | Yes | -| | D2Zing | Yes | Yes | -| | D2Zpvc | Yes | Yes | -| | D2LastEventDate | No | No | -| | D2AddControlDigits | No | No | -| | D2CheckControlDigits | No | No | -| | D2ZScoreWFA | No | No | -| | D2ZScoreWFH | No | No | -| | D2ZScoreHFA | No | No | -| | D2InOrgUnitGroup | No | No | -| | D2HasUserRole | No | No | -| **Aggregation functions:** | avg | Yes | No | -| | count | Yes | No | -| | max | Yes | No | -| | min | Yes | No | -| | percentileCont | Yes | No | -| | stddev | Yes | No | -| | stddevPop | Yes | No | -| | stddevSamp | Yes | No | -| | sum | Yes | No | -| | variance | Yes | No | -| **Variables:** | AnalyticsPeriodEnd | Yes | Yes | -| | AnalyticsPeriodStart | Yes | Yes | -| | CreationDate | Yes | Yes | -| | CurrentDate | Yes | Yes | -| | CompletedDate | Yes | Yes | -| | DueDate | Yes | Yes | -| | EnrollmentCount | Yes | Yes | -| | EnrollmentDate | Yes | Yes | -| | EnrollmentStatus | Yes | Yes | -| | EventStatus | Yes | Yes | -| | EventCount | Yes | Yes | -| | ExecutionDate | Yes | Yes | -| | EventDate | Yes | Yes | -| | IncidentDate | Yes | Yes | -| | OrgunitCount | Yes | Yes | -| | ProgramStageId | Yes | Yes | -| | ProgramStageName | Yes | Yes | -| | SyncDate | Yes | Yes | -| | TeiCount | Yes | Yes | -| | ValueCount | Yes | Yes | -| | ZeroPosValueCount | Yes | Yes | -| **Other:** | Constant | Yes | Yes | -| | ProgramStageElement | Yes | Yes | -| | ProgramAttribute | Yes | Yes | -| | PS_EVENTDATE | Yes | Yes | +| Type | Element | Backend | Android SDK | +|----------------------------|----------------------|-----------|-------------| +| **Mathematical:** | Parenthesis | Yes | Yes | +| | Plus (+) | Yes | Yes | +| | Minus (-) | Yes | Yes | +| | Power (^) | Yes | No | +| | Multiply (*) | Yes | Yes | +| | Divide (/) | Yes | Yes | +| | Modulus (%) | Yes | Yes | +| **Logical:** | NOT | Yes | Yes | +| | ! | Yes | Yes | +| | AND | Yes | Yes | +| | && | Yes | Yes | +| | OR | Yes | Yes | +| | || | Yes | Yes | +| **Comparison:** | Equal (==) | Yes | Yes | +| | NotEqual (!=) | Yes | Yes | +| | GT (>) | Yes | Yes | +| | LT (<) | Yes | Yes | +| | GE (>=) | Yes | Yes | +| | LE (<=) | Yes | Yes | +| **Literals:** | null | Yes | Yes | +| **Functions:** | FirstNonNull | Yes | Yes | +| | Greatest | Yes | Yes | +| | If | Yes | Yes | +| | IsNotNull | Yes | Yes | +| | IsNull | Yes | Yes | +| | Least | Yes | Yes | +| | Log | Yes | No | +| | Log10 | Yes | No | +| | PeriodOffset | Yes | No | +| **D2 functions:** | D2AddDays | No | No | +| | D2Ceil | No | No | +| | D2Concatenate | No | No | +| | D2Condition | Yes | Yes | +| | D2Count | Yes | Yes | +| | D2CountIfCondition | Yes | Yes | +| | D2CountIfValue | Yes | Yes | +| | D2DaysBetween | Yes | Yes | +| | D2Floor | No | No | +| | D2HasValue | Yes | Yes | +| | D2Left | No | No | +| | D2Length | No | No | +| | D2MaxValue | Yes | Yes | +| | D2MinutesBetween | Yes | Yes | +| | D2MinValue | Yes | Yes | +| | D2Modulus | No | No | +| | D2MonthsBetween | Yes | Yes | +| | D2Oizp | Yes | Yes | +| | D2RelationshipCount | Yes | Yes | +| | D2Right | No | No | +| | D2Round | No | No | +| | D2Split | No | No | +| | D2Substring | No | No | +| | D2ValidatePattern | No | No | +| | D2WeeksBetween | Yes | Yes | +| | D2YearsBetween | Yes | Yes | +| | D2Zing | Yes | Yes | +| | D2Zpvc | Yes | Yes | +| | D2LastEventDate | No | No | +| | D2AddControlDigits | No | No | +| | D2CheckControlDigits | No | No | +| | D2ZScoreWFA | No | No | +| | D2ZScoreWFH | No | No | +| | D2ZScoreHFA | No | No | +| | D2InOrgUnitGroup | No | No | +| | D2HasUserRole | No | No | +| **Aggregation functions:** | avg | Yes | No | +| | count | Yes | No | +| | max | Yes | No | +| | min | Yes | No | +| | percentileCont | Yes | No | +| | stddev | Yes | No | +| | stddevPop | Yes | No | +| | stddevSamp | Yes | No | +| | sum | Yes | No | +| | variance | Yes | No | +| **Variables:** | AnalyticsPeriodEnd | Yes | Yes | +| | AnalyticsPeriodStart | Yes | Yes | +| | CreationDate | Yes | Yes | +| | CurrentDate | Yes | Yes | +| | CompletedDate | Yes | Yes | +| | DueDate | Yes | Yes | +| | EnrollmentCount | Yes | Yes | +| | EnrollmentDate | Yes | Yes | +| | EnrollmentStatus | Yes | Yes | +| | EventStatus | Yes | Yes | +| | EventCount | Yes | Yes | +| | ExecutionDate | Yes | Yes | +| | EventDate | Yes | Yes | +| | IncidentDate | Yes | Yes | +| | OrgunitCount | Yes | Yes | +| | ProgramStageId | Yes | Yes | +| | ProgramStageName | Yes | Yes | +| | SyncDate | Yes | Yes | +| | TeiCount | Yes | Yes | +| | ValueCount | Yes | Yes | +| | ZeroPosValueCount | Yes | Yes | +| **Other:** | Constant | Yes | Yes | +| | ProgramStageElement | Yes | Yes | +| | ProgramAttribute | Yes | Yes | +| | PS_EVENTDATE | Yes | Yes | ### Aggregation type support { #android_sdk_analytics_aggregation_type_support } diff --git a/docs/content/developer/program-indicator-engine.md b/docs/content/developer/program-indicator-engine.md index 8b3a15cd77..0e203f3204 100644 --- a/docs/content/developer/program-indicator-engine.md +++ b/docs/content/developer/program-indicator-engine.md @@ -23,94 +23,94 @@ If the evaluation of the "filter" component returns false, the result is null. Table: Compatibility -| Type | Element | Web | Android SDK | -|---------------------------|-----------------------|-----------|---------------| -|**Mathematical:** |Parenthesis | Yes | Yes | -| |Plus (+) | Yes | Yes | -| |Minus (-) | Yes | Yes | -| |Power (^) | Yes | Yes | -| |Multiply (*) | Yes | Yes | -| |Divide (/) | Yes | Yes | -| |Modulus (%) | Yes | Yes | -|**Logical:** |NOT | Yes | Yes | -| |! | Yes | Yes | -| |AND | Yes | Yes | -| |&& | Yes | Yes | -| |OR | Yes | Yes | -| ||| | Yes | Yes | -|**Comparison:** |Equal (==) | Yes | Yes | -| |NotEqual (!=) | Yes | Yes | -| |GT (>) | Yes | Yes | -| |LT (<) | Yes | Yes | -| |GE (>=) | Yes | Yes | -| |LE (<=) | Yes | Yes | -|**Functions:** |FirstNonNull | Yes | Yes | -| |Greatest | Yes | Yes | -| |If | Yes | Yes | -| |IsNotNull | Yes | Yes | -| |IsNull | Yes | Yes | -| |Least | Yes | Yes | -| |Log | Yes | Yes | -| |Log10 | Yes | Yes | -| |PeriodOffset | - | No | -|**D2 functions:** |D2AddDays | Yes | Yes | -| |D2Ceil | Yes | Yes | -| |D2Concatenate | Yes | Yes | -| |D2Condition | Yes | Yes | -| |D2Count | Yes | Yes | -| |D2CountIfCondition | Yes | Yes | -| |D2CountIfValue | Yes | Yes | -| |D2DaysBetween | Yes | Yes | -| |D2Floor | Yes | Yes | -| |D2HasValue | Yes | Yes | -| |D2Left | Yes | Yes | -| |D2Length | Yes | Yes | -| |D2MaxValue | No | No | -| |D2MinutesBetween | Yes | Yes | -| |D2MinValue | No | No | -| |D2Modulus | Yes | Yes | -| |D2MonthsBetween | Yes | Yes | -| |D2Oizp | Yes | Yes | -| |D2RelationshipCount | Yes | Yes | -| |D2Right | Yes | Yes | -| |D2Round | Yes | Yes | -| |D2Split | Yes | Yes | -| |D2Substring | Yes | Yes | -| |D2ValidatePattern | Yes | Yes | -| |D2WeeksBetween | Yes | Yes | -| |D2YearsBetween | Yes | Yes | -| |D2Zing | Yes | Yes | -| |D2Zpvc | Yes | Yes | -| |D2LastEventDate | Yes | No | -| |D2AddControlDigits | Yes | No | -| |D2CheckControlDigits | Yes | No | -| |D2ZScoreWFA | Yes | No | -| |D2ZScoreWFH | Yes | No | -| |D2ZScoreHFA | Yes | No | -| |D2InOrgUnitGroup | Yes | No | -| |D2HasUserRole | Yes | No | -|**Variables:** |AnalyticsPeriodEnd | No | No | -| |AnalyticsPeriodStart | No | No | -| |CreationDate | No | Yes | -| |CurrentDate | Yes | Yes | -| |CompletedDate | No | Yes | -| |DueDate | Yes | Yes | -| |EnrollmentCount | Yes | Yes | -| |EnrollmentDate | Yes | Yes | -| |EnrollmentStatus | No | Yes | -| |EventStatus | Yes | Yes | -| |EventCount | Yes | Yes | -| |ExecutionDate | Yes | Yes | -| |EventDate | Yes | Yes | -| |IncidentDate | Yes | Yes | -| |OrgunitCount | No | No | -| |ProgramStageId | Yes | Yes | -| |ProgramStageName | Yes | Yes | -| |SyncDate | No | No | -| |TeiCount | Yes | Yes | -| |ValueCount | Yes | Yes | -| |ZeroPosValueCount | Yes | Yes | -|**Other:** |Constant | Yes | Yes | -| |ProgramStageElement | Yes | Yes | -| |ProgramAttribute | Yes | Yes | -| |PS_EVENTDATE | Yes | Yes | +| Type | Element | Web | Android SDK | +|---------------------------|-----------------------|-----------|-------------| +|**Mathematical:** |Parenthesis | Yes | Yes | +| |Plus (+) | Yes | Yes | +| |Minus (-) | Yes | Yes | +| |Power (^) | Yes | Yes | +| |Multiply (*) | Yes | Yes | +| |Divide (/) | Yes | Yes | +| |Modulus (%) | Yes | Yes | +|**Logical:** |NOT | Yes | Yes | +| |! | Yes | Yes | +| |AND | Yes | Yes | +| |&& | Yes | Yes | +| |OR | Yes | Yes | +| ||| | Yes | Yes | +|**Comparison:** |Equal (==) | Yes | Yes | +| |NotEqual (!=) | Yes | Yes | +| |GT (>) | Yes | Yes | +| |LT (<) | Yes | Yes | +| |GE (>=) | Yes | Yes | +| |LE (<=) | Yes | Yes | +|**Functions:** |FirstNonNull | Yes | Yes | +| |Greatest | Yes | Yes | +| |If | Yes | Yes | +| |IsNotNull | Yes | Yes | +| |IsNull | Yes | Yes | +| |Least | Yes | Yes | +| |Log | Yes | Yes | +| |Log10 | Yes | Yes | +| |PeriodOffset | - | No | +|**D2 functions:** |D2AddDays | Yes | Yes | +| |D2Ceil | Yes | Yes | +| |D2Concatenate | Yes | Yes | +| |D2Condition | Yes | Yes | +| |D2Count | Yes | Yes | +| |D2CountIfCondition | Yes | Yes | +| |D2CountIfValue | Yes | Yes | +| |D2DaysBetween | Yes | Yes | +| |D2Floor | Yes | Yes | +| |D2HasValue | Yes | Yes | +| |D2Left | Yes | Yes | +| |D2Length | Yes | Yes | +| |D2MaxValue | No | Yes | +| |D2MinutesBetween | Yes | Yes | +| |D2MinValue | No | Yes | +| |D2Modulus | Yes | Yes | +| |D2MonthsBetween | Yes | Yes | +| |D2Oizp | Yes | Yes | +| |D2RelationshipCount | Yes | Yes | +| |D2Right | Yes | Yes | +| |D2Round | Yes | Yes | +| |D2Split | Yes | Yes | +| |D2Substring | Yes | Yes | +| |D2ValidatePattern | Yes | Yes | +| |D2WeeksBetween | Yes | Yes | +| |D2YearsBetween | Yes | Yes | +| |D2Zing | Yes | Yes | +| |D2Zpvc | Yes | Yes | +| |D2LastEventDate | Yes | No | +| |D2AddControlDigits | Yes | No | +| |D2CheckControlDigits | Yes | No | +| |D2ZScoreWFA | Yes | No | +| |D2ZScoreWFH | Yes | No | +| |D2ZScoreHFA | Yes | No | +| |D2InOrgUnitGroup | Yes | No | +| |D2HasUserRole | Yes | No | +|**Variables:** |AnalyticsPeriodEnd | No | No | +| |AnalyticsPeriodStart | No | No | +| |CreationDate | No | Yes | +| |CurrentDate | Yes | Yes | +| |CompletedDate | No | Yes | +| |DueDate | Yes | Yes | +| |EnrollmentCount | Yes | Yes | +| |EnrollmentDate | Yes | Yes | +| |EnrollmentStatus | No | Yes | +| |EventStatus | Yes | Yes | +| |EventCount | Yes | Yes | +| |ExecutionDate | Yes | Yes | +| |EventDate | Yes | Yes | +| |IncidentDate | Yes | Yes | +| |OrgunitCount | No | No | +| |ProgramStageId | Yes | Yes | +| |ProgramStageName | Yes | Yes | +| |SyncDate | No | No | +| |TeiCount | Yes | Yes | +| |ValueCount | Yes | Yes | +| |ZeroPosValueCount | Yes | Yes | +|**Other:** |Constant | Yes | Yes | +| |ProgramStageElement | Yes | Yes | +| |ProgramAttribute | Yes | Yes | +| |PS_EVENTDATE | Yes | Yes | From f03904d783585c0b456c975a3d2958e685ebac44 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 11 May 2023 16:23:38 +1000 Subject: [PATCH 04/23] [1.8.1-RC] Bump to version 1.8.1-SNAPSHOT --- core/gradle.properties | 4 ++-- docs/content/developer/getting-started.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/gradle.properties b/core/gradle.properties index 1ec3049866..1c9437d1c8 100644 --- a/core/gradle.properties +++ b/core/gradle.properties @@ -29,8 +29,8 @@ # Properties which are consumed by plugins/gradle-mvn-push.gradle plugin. # They are used for publishing artifact to snapshot repository. -VERSION_NAME=1.8.0-SNAPSHOT -VERSION_CODE=280 +VERSION_NAME=1.8.1-SNAPSHOT +VERSION_CODE=281 GROUP=org.hisp.dhis diff --git a/docs/content/developer/getting-started.md b/docs/content/developer/getting-started.md index 8253c4e200..bb591a29a9 100644 --- a/docs/content/developer/getting-started.md +++ b/docs/content/developer/getting-started.md @@ -6,7 +6,7 @@ Include dependency in build.gradle. ```gradle dependencies { - implementation "org.hisp.dhis:android-core:1.7.0" + implementation "org.hisp.dhis:android-core:1.8.1" ... } ``` From 0d2e6beadc8538c02075ddd8fe2fe4bc9ea65346 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Fri, 12 May 2023 20:22:47 +1000 Subject: [PATCH 05/23] [ANDROSDK-1686] Implement 'is' function (evaluate) --- .../parser/internal/expression/ParserUtils.kt | 1 + .../expression/function/FunctionIf.kt | 2 +- .../expression/function/FunctionIs.kt | 49 +++++++++++++++++++ .../expression/ExpressionServiceShould.kt | 2 + 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIs.kt diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt index dfaaae5aaa..feee620c06 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt @@ -85,6 +85,7 @@ internal object ParserUtils { // Functions ExpressionParser.FIRST_NON_NULL to FunctionFirstNonNull(), ExpressionParser.GREATEST to FunctionGreatest(), + ExpressionParser.IS to FunctionIs(), ExpressionParser.IF to FunctionIf(), ExpressionParser.IS_NOT_NULL to FunctionIsNotNull(), ExpressionParser.IS_NULL to FunctionIsNull(), diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIf.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIf.kt index f0cce2a69b..69a34c9a8f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIf.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIf.kt @@ -55,7 +55,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext * * @author Jim Grace */ -internal class FunctionIf : ExpressionItem { +internal class FunctionIf : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { val arg0 = visitor.castBooleanVisit(ctx.expr(0)) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIs.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIs.kt new file mode 100644 index 0000000000..4e09279a0a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIs.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.parser.internal.expression.function + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.antlr.AntlrParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +internal class FunctionIs : ExpressionItem { + + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val values = ctx.expr().map(visitor::visit) + + val targetValue = values[0] + val expectedValues = values.drop(1) + + return expectedValues.any { AntlrParserUtils.compare(targetValue, it) == 0 } + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + return TODO() + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt index b3209309b0..b6f6bc4bef 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt @@ -268,6 +268,8 @@ class ExpressionServiceShould { assertThat(service.getExpressionValue("log(100)") as Double).isAtLeast(4.6) assertThat(service.getExpressionValue("log(100)") as Double).isAtMost(4.7) assertThat(service.getExpressionValue("log10(100)")).isEqualTo(2.0) + assertThat(service.getExpressionValue("is('one' in 'one', 'two')")).isEqualTo(true) + assertThat(service.getExpressionValue("is('three' in 'one', 'two')")).isEqualTo(false) } @Test From 843f8f72054fa23454558d1033111539d21e5336 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 15 May 2023 17:10:15 +1000 Subject: [PATCH 06/23] [ANDROSDK-1686] Implement 'is' function (sql) --- ...amIndicatorSQLExecutorIntegrationShould.kt | 38 +++++++++++++++++++ .../expression/function/FunctionIf.kt | 2 +- .../expression/function/FunctionIs.kt | 7 +++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt index dccd532da2..14cf20daa3 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt @@ -535,6 +535,44 @@ internal class ProgramIndicatorSQLExecutorIntegrationShould : BaseProgramIndicat ).isEqualTo("1000") } + @Test + fun should_evaluate_is_function() { + helper.createTrackedEntity(trackedEntity1.uid(), orgunitChild1.uid(), trackedEntityType.uid()) + val enrollment1 = generator.generate() + helper.createEnrollment(trackedEntity1.uid(), enrollment1, program.uid(), orgunitChild1.uid()) + val event1 = generator.generate() + helper.createTrackerEvent(event1, enrollment1, program.uid(), programStage1.uid(), orgunitChild1.uid()) + + helper.insertTrackedEntityDataValue(event1, dataElement3.uid(), "ONE") + + val textDe = de(programStage1.uid(), dataElement3.uid()) + val nullD2 = de(programStage1.uid(), dataElement2.uid()) + + assertThat( + evaluateProgramIndicator( + expression = "if(is($textDe in 'ONE', 'TWO', 'THREE'), 1, 2)", + analyticsType = AnalyticsType.EVENT, + aggregationType = AggregationType.SUM + ) + ).isEqualTo("1") + + assertThat( + evaluateProgramIndicator( + expression = "if(is($textDe in 'TWO', 'THREE'), 1, 2)", + analyticsType = AnalyticsType.EVENT, + aggregationType = AggregationType.SUM + ) + ).isEqualTo("2") + + assertThat( + evaluateProgramIndicator( + expression = "if(is($nullD2 in 'ONE', 'TWO', 'THREE'), 1, 2)", + analyticsType = AnalyticsType.EVENT, + aggregationType = AggregationType.SUM + ) + ).isEqualTo("2") + } + @Test fun should_filter_program_stage_data_values() { helper.createTrackedEntity(trackedEntity1.uid(), orgunitChild1.uid(), trackedEntityType.uid()) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIf.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIf.kt index 69a34c9a8f..f0cce2a69b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIf.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIf.kt @@ -55,7 +55,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext * * @author Jim Grace */ -internal class FunctionIf : ExpressionItem { +internal class FunctionIf : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { val arg0 = visitor.castBooleanVisit(ctx.expr(0)) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIs.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIs.kt index 4e09279a0a..c45d4f374a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIs.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionIs.kt @@ -44,6 +44,11 @@ internal class FunctionIs : ExpressionItem { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return TODO() + val values = ctx.expr().map(visitor::castStringVisit) + + val targetValue = values[0] + val expectedValues = values.drop(1) + + return "$targetValue IN (${expectedValues.joinToString(", ")})" } } From 852b6a83c2c0a2d7697e57ae33d16616a8112131 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 16 May 2023 12:58:13 +1000 Subject: [PATCH 07/23] [ANDROSDK-1687] Add model and base classes to ExpressionDimensionItem --- ...sionDimensionItemStoreIntegrationShould.kt | 54 ++++++++++++++ core/src/main/assets/migrations/146.sql | 3 + core/src/main/assets/snapshots/snapshot.sql | 1 + .../internal/BaseDatabaseOpenHelper.java | 2 +- .../ExpressionDimensionItem.java | 70 +++++++++++++++++++ .../ExpressionDimensionItemTableInfo.kt | 57 +++++++++++++++ .../internal/ExpressionDimensionItemCall.kt | 61 ++++++++++++++++ .../ExpressionDimensionItemEntityDIModule.kt | 59 ++++++++++++++++ .../internal/ExpressionDimensionItemFields.kt | 44 ++++++++++++ .../ExpressionDimensionItemHandler.kt | 39 +++++++++++ .../ExpressionDimensionItemService.kt | 47 +++++++++++++ .../internal/ExpressionDimensionItemStore.kt | 57 +++++++++++++++ .../ExpressionDimensionItemSamples.kt | 44 ++++++++++++ .../expression_dimension_item.json | 10 +++ .../ExpressionDimensionItemShould.kt | 53 ++++++++++++++ 15 files changed, 600 insertions(+), 1 deletion(-) create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemStoreIntegrationShould.kt create mode 100644 core/src/main/assets/migrations/146.sql create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItem.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemTableInfo.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemCall.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemEntityDIModule.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemFields.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemHandler.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemService.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemStore.kt create mode 100644 core/src/sharedTest/java/org/hisp/dhis/android/core/data/expressiondimensionitem/ExpressionDimensionItemSamples.kt create mode 100644 core/src/sharedTest/resources/expressiondimensionitem/expression_dimension_item.json create mode 100644 core/src/test/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemShould.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemStoreIntegrationShould.kt new file mode 100644 index 0000000000..fdfb891697 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemStoreIntegrationShould.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem + +import org.hisp.dhis.android.core.data.database.IdentifiableObjectStoreAbstractIntegrationShould +import org.hisp.dhis.android.core.data.expressiondimensionitem.ExpressionDimensionItemSamples +import org.hisp.dhis.android.core.expressiondimensionitem.internal.ExpressionDimensionItemStore +import org.hisp.dhis.android.core.utils.integration.mock.TestDatabaseAdapterFactory +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class ExpressionDimensionItemStoreIntegrationShould : + IdentifiableObjectStoreAbstractIntegrationShould( + ExpressionDimensionItemStore.create(TestDatabaseAdapterFactory.get()), + ExpressionDimensionItemTableInfo.TABLE_INFO, + TestDatabaseAdapterFactory.get() + ) { + + override fun buildObject(): ExpressionDimensionItem { + return ExpressionDimensionItemSamples.getExpressionDimensionItem() + } + + override fun buildObjectToUpdate(): ExpressionDimensionItem { + return ExpressionDimensionItemSamples.getExpressionDimensionItem().toBuilder() + .expression("#{fbfJHSPpUQD}") + .build() + } +} diff --git a/core/src/main/assets/migrations/146.sql b/core/src/main/assets/migrations/146.sql new file mode 100644 index 0000000000..f00a37e46e --- /dev/null +++ b/core/src/main/assets/migrations/146.sql @@ -0,0 +1,3 @@ +# Add Calculation support (ANDROSDK-1687) + +CREATE TABLE ExpressionDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, expression TEXT); \ No newline at end of file diff --git a/core/src/main/assets/snapshots/snapshot.sql b/core/src/main/assets/snapshots/snapshot.sql index b2508addab..b6933444ea 100644 --- a/core/src/main/assets/snapshots/snapshot.sql +++ b/core/src/main/assets/snapshots/snapshot.sql @@ -130,3 +130,4 @@ CREATE TABLE MapLayerImageryProvider (_id INTEGER PRIMARY KEY AUTOINCREMENT, map CREATE TABLE DataStore (_id INTEGER PRIMARY KEY AUTOINCREMENT, namespace TEXT NOT NULL, key TEXT NOT NULL, value TEXT, syncState TEXT, deleted INTEGER, UNIQUE(namespace, key)); CREATE TABLE ProgramStageWorkingList (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, program TEXT NOT NULL, programStage TEXT NOT NULL, eventStatus TEXT, eventCreatedAt TEXT, eventOccurredAt TEXT, eventScheduledAt TEXT, enrollmentStatus TEXT, enrolledAt TEXT, enrollmentOccurredAt TEXT, orderProperty TEXT, displayColumnOrder TEXT, orgUnit TEXT, ouMode TEXT, assignedUserMode TEXT, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (programStage) REFERENCES ProgramStage (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (orgUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE LatestAppVersion (_id INTEGER PRIMARY KEY AUTOINCREMENT, downloadURL TEXT, version TEXT); +CREATE TABLE ExpressionDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, expression TEXT); diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/BaseDatabaseOpenHelper.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/BaseDatabaseOpenHelper.java index 4685a1a36f..7d77e6d5e3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/BaseDatabaseOpenHelper.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/BaseDatabaseOpenHelper.java @@ -35,7 +35,7 @@ class BaseDatabaseOpenHelper { - static final int VERSION = 145; + static final int VERSION = 146; private final AssetManager assetManager; private final int targetVersion; diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItem.java b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItem.java new file mode 100644 index 0000000000..125b432bf6 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItem.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem; + +import android.database.Cursor; + +import androidx.annotation.Nullable; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.google.auto.value.AutoValue; + +import org.hisp.dhis.android.core.common.BaseIdentifiableObject; +import org.hisp.dhis.android.core.common.CoreObject; + +@AutoValue +@JsonDeserialize(builder = $$AutoValue_ExpressionDimensionItem.Builder.class) +public abstract class ExpressionDimensionItem extends BaseIdentifiableObject implements CoreObject { + + @Nullable + @JsonProperty() + public abstract String expression(); + + public static Builder builder() { + return new $$AutoValue_ExpressionDimensionItem.Builder(); + } + + public static ExpressionDimensionItem create(Cursor cursor) { + return $AutoValue_ExpressionDimensionItem.createFromCursor(cursor); + } + + public abstract Builder toBuilder(); + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public static abstract class Builder extends BaseIdentifiableObject.Builder { + public abstract Builder id(Long id); + + public abstract Builder expression(String expression); + + public abstract ExpressionDimensionItem build(); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemTableInfo.kt new file mode 100644 index 0000000000..1b0ba49d49 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemTableInfo.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem + +import org.hisp.dhis.android.core.arch.db.tableinfos.TableInfo +import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper +import org.hisp.dhis.android.core.common.CoreColumns +import org.hisp.dhis.android.core.common.IdentifiableColumns + +object ExpressionDimensionItemTableInfo { + val TABLE_INFO: TableInfo = object : TableInfo() { + override fun name(): String { + return "ExpressionDimensionItem" + } + + override fun columns(): CoreColumns { + return Columns() + } + } + + class Columns : IdentifiableColumns() { + override fun all(): Array { + return CollectionsHelper.appendInNewArray(super.all(), + EXPRESSION + ) + } + + companion object { + const val EXPRESSION = "expression" + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemCall.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemCall.kt new file mode 100644 index 0000000000..df0147e613 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemCall.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem.internal + +import dagger.Reusable +import io.reactivex.Single +import org.hisp.dhis.android.core.arch.api.executors.internal.APIDownloader +import org.hisp.dhis.android.core.arch.call.factories.internal.UidsCall +import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import javax.inject.Inject + +@Reusable +internal class ExpressionDimensionItemCall @Inject constructor( + private val service: ExpressionDimensionItemService, + private val handler: Handler, + private val apiDownloader: APIDownloader +) : UidsCall { + override fun download(uids: Set): Single> { + return apiDownloader.downloadPartitioned( + uids, + MAX_UID_LIST_SIZE, + handler + ) { partitionUids: Set -> + service.getExpressionDimensionItems( + ExpressionDimensionItemFields.uid.`in`(partitionUids), + ExpressionDimensionItemFields.allFields, + false + ) + } + } + + companion object { + private const val MAX_UID_LIST_SIZE = 50 + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemEntityDIModule.kt new file mode 100644 index 0000000000..a214e94ed2 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemEntityDIModule.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem.internal + +import dagger.Module +import dagger.Provides +import dagger.Reusable +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import retrofit2.Retrofit + +@Module +internal class ExpressionDimensionItemEntityDIModule { + + @Provides + @Reusable + fun store(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + return ExpressionDimensionItemStore.create(databaseAdapter) + } + + @Provides + @Reusable + fun handler(handler: ExpressionDimensionItemHandler): Handler { + return handler + } + + @Reusable + @Provides + fun service(retrofit: Retrofit): ExpressionDimensionItemService { + return retrofit.create(ExpressionDimensionItemService::class.java) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemFields.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemFields.kt new file mode 100644 index 0000000000..d370f98222 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemFields.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem.internal + +import org.hisp.dhis.android.core.arch.api.fields.internal.Fields +import org.hisp.dhis.android.core.arch.fields.internal.FieldsHelper +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemTableInfo.Columns + +internal object ExpressionDimensionItemFields { + private val fh = FieldsHelper() + + val uid = fh.uid() + + val allFields: Fields = Fields.builder() + .fields(fh.getIdentifiableFields()) + .fields(fh.field(Columns.EXPRESSION)) + .build() +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemHandler.kt new file mode 100644 index 0000000000..4b1d025eac --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemHandler.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem.internal + +import dagger.Reusable +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import javax.inject.Inject + +@Reusable +internal class ExpressionDimensionItemHandler @Inject constructor( + store: IdentifiableObjectStore +) : IdentifiableHandlerImpl(store) diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemService.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemService.kt new file mode 100644 index 0000000000..8598291c0c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemService.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem.internal + +import io.reactivex.Single +import org.hisp.dhis.android.core.arch.api.fields.internal.Fields +import org.hisp.dhis.android.core.arch.api.filters.internal.Filter +import org.hisp.dhis.android.core.arch.api.filters.internal.Where +import org.hisp.dhis.android.core.arch.api.filters.internal.Which +import org.hisp.dhis.android.core.arch.api.payload.internal.Payload +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import retrofit2.http.* + +internal interface ExpressionDimensionItemService { + + @GET("expressionDimensionItems") + fun getExpressionDimensionItems( + @Query("filter") @Where uids: Filter, + @Query("fields") @Which fields: Fields, + @Query("paging") paging: Boolean + ): Single> +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemStore.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemStore.kt new file mode 100644 index 0000000000..ae8550ab29 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemStore.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem.internal + +import android.database.Cursor +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.db.stores.binders.internal.IdentifiableStatementBinder +import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinder +import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapper +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory.objectWithUidStore +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemTableInfo + +@Suppress("MagicNumber") +internal object ExpressionDimensionItemStore { + private val BINDER: StatementBinder = + object : IdentifiableStatementBinder() { + override fun bindToStatement(o: ExpressionDimensionItem, w: StatementWrapper) { + super.bindToStatement(o, w) + w.bind(7, o.expression()) + } + } + + fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + return objectWithUidStore( + databaseAdapter, + ExpressionDimensionItemTableInfo.TABLE_INFO, + BINDER + ) { cursor: Cursor -> ExpressionDimensionItem.create(cursor) } + } +} diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/expressiondimensionitem/ExpressionDimensionItemSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/expressiondimensionitem/ExpressionDimensionItemSamples.kt new file mode 100644 index 0000000000..eabca08060 --- /dev/null +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/expressiondimensionitem/ExpressionDimensionItemSamples.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.data.expressiondimensionitem + +import org.hisp.dhis.android.core.data.utils.FillPropertiesTestUtils.fillIdentifiableProperties +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem + +object ExpressionDimensionItemSamples { + + fun getExpressionDimensionItem(): ExpressionDimensionItem { + val builder = ExpressionDimensionItem.builder() + fillIdentifiableProperties(builder) + return builder + .id(1L) + .expression("#{fbfJHSPpUQD}+#{cYeuwXTCPkU}") + .build() + } +} diff --git a/core/src/sharedTest/resources/expressiondimensionitem/expression_dimension_item.json b/core/src/sharedTest/resources/expressiondimensionitem/expression_dimension_item.json new file mode 100644 index 0000000000..c5187f55be --- /dev/null +++ b/core/src/sharedTest/resources/expressiondimensionitem/expression_dimension_item.json @@ -0,0 +1,10 @@ +{ + "id": "MUcDTQTYanb", + "name": "ANC 1 + 2", + "displayName": "ANC 1 + 2 display", + "code": "ANC_code", + "created": "2023-05-16T00:42:44.670", + "lastUpdated": "2023-05-16T00:42:44.670", + "expression": "#{fbfJHSPpUQD}+#{cYeuwXTCPkU}", + "attributeValues": [] +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemShould.kt b/core/src/test/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemShould.kt new file mode 100644 index 0000000000..6a1f3f1490 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemShould.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2004-2022, 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.android.core.expressiondimensionitem + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.common.BaseObjectShould +import org.hisp.dhis.android.core.common.ObjectShould +import org.junit.Test + +class ExpressionDimensionItemShould : + BaseObjectShould("expressiondimensionitem/expression_dimension_item.json"), + ObjectShould { + + @Test + override fun map_from_json_string() { + val item = objectMapper.readValue(jsonStream, ExpressionDimensionItem::class.java) + + assertThat(item.uid()).isEqualTo("MUcDTQTYanb") + assertThat(item.code()).isEqualTo("ANC_code") + assertThat(item.name()).isEqualTo("ANC 1 + 2") + assertThat(item.displayName()).isEqualTo("ANC 1 + 2 display") + assertThat(item.created()).isEqualTo(DateUtils.DATE_FORMAT.parse("2023-05-16T00:42:44.670")) + assertThat(item.lastUpdated()).isEqualTo(DateUtils.DATE_FORMAT.parse("2023-05-16T00:42:44.670")) + assertThat(item.expression()).isEqualTo("#{fbfJHSPpUQD}+#{cYeuwXTCPkU}") + assertThat(item.deleted()).isNull() + } +} From 86cd8f0c11478f0c3a7296f83b2eb826ff6f8cf4 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Fri, 19 May 2023 17:19:01 +1000 Subject: [PATCH 08/23] [ANDROSDK-1687] Refactor visualization model --- ...rogramDataElementStoreIntegrationShould.kt | 58 --- ...DataDimensionItemStoreIntegrationShould.kt | 58 --- ...goryDimensionLinkStoreIntegrationShould.kt | 58 --- ...ionDimensionItemStoreIntegrationShould.kt} | 22 +- ...ectionRepositoryMockIntegrationShould.java | 189 --------- ...llectionRepositoryMockIntegrationShould.kt | 187 +++++++++ core/src/main/assets/migrations/146.sql | 17 +- core/src/main/assets/snapshots/snapshot.sql | 5 +- .../analytics/aggregated/AnalyticsModel.kt | 2 + .../AnalyticsServiceEvaluatorHelper.kt | 2 + .../AnalyticsServiceMetadataHelper.kt | 4 + .../AnalyticsVisualizationsService.kt | 13 +- ...icsVisualizationsServiceDimensionHelper.kt | 172 ++++---- .../core/arch/d2/internal/D2DIComponent.kt | 2 + .../ObjectWithUidListColumnAdapter.kt | 76 ---- .../internal/RelativePeriodsColumnAdapter.kt | 76 ---- .../internal/LayoutPositionColumnAdapter.kt} | 18 +- ...ualizationDimensionListColumnAdapter.java} | 6 +- .../internal/BaseUidsSeeker.kt} | 25 +- .../internal/CategoryComboUidsSeeker.kt | 17 +- .../core/domain/metadata/MetadataCall.kt | 8 +- ...xpressionDimensionItemModuleDownloader.kt} | 31 +- .../ExpressionDimensionItemModuleWiper.kt | 47 +++ ...ExpressionDimensionItemPackageDIModule.kt} | 21 +- .../ExpressionDimensionItemUidsSeeker.kt} | 35 +- .../indicator/internal/IndicatorUidsSeeker.kt | 34 +- .../legendset/internal/LegendSetUidsSeeker.kt | 27 +- .../core/mockwebserver/Dhis2MockServer.java | 4 + .../internal/ProgramIndicatorUidsSeeker.kt | 30 +- .../core/visualization/DataDimensionItem.java | 178 -------- .../DataDimensionItemProgramDataElement.java | 88 ---- .../DataDimensionItemTableInfo.kt | 94 ----- .../core/visualization/DimensionItemType.kt | 43 ++ .../core/visualization/LayoutPosition.kt | 35 ++ .../core/visualization/Visualization.java | 95 +---- .../core/visualization/VisualizationAPI36.kt | 33 +- .../VisualizationCollectionRepository.java | 41 +- .../visualization/VisualizationDimension.java | 68 ++++ ...e.java => VisualizationDimensionItem.java} | 65 +-- ...=> VisualizationDimensionItemTableInfo.kt} | 25 +- .../visualization/VisualizationTableInfo.kt | 20 - .../internal/DataDimensionItemFields.kt | 57 --- .../internal/DataDimensionItemStore.kt | 62 --- ...zationCategoryDimensionChildrenAppender.kt | 71 ---- ...tionColumnsRowsFiltersChildrenAppender.kt} | 56 ++- ...lds.kt => VisualizationDimensionFields.kt} | 24 +- ...sualizationDimensionItemEntityDIModule.kt} | 12 +- ...kt => VisualizationDimensionItemFields.kt} | 22 +- ....kt => VisualizationDimensionItemStore.kt} | 22 +- .../internal/VisualizationEntityDIModule.kt | 9 +- .../internal/VisualizationFields.kt | 32 +- .../internal/VisualizationHandler.kt | 67 ++- .../internal/VisualizationModuleWiper.kt | 6 +- .../VisualizationPackageDIModule.java | 3 +- .../internal/VisualizationStore.kt | 24 +- .../core/wipe/internal/D2ModuleWipers.kt | 3 + .../visualization/DataDimensionItemSamples.kt | 61 --- .../VisualizationDimensionItemSamples.kt | 44 ++ .../visualization/VisualizationSamples.kt | 93 ++++- .../expression_dimension_items.json | 14 + .../visualization/visualization.json | 384 ++++-------------- .../visualization/visualization_api_36.json | 384 ++++-------------- .../visualization_simplified.json | 109 ----- .../visualization/visualizations.json | 185 ++++----- ...ualizationsServiceDimensionHelperShould.kt | 267 ++++++++---- .../domain/metadata/MetadataCallShould.kt | 18 +- .../core/visualization/VisualizationShould.kt | 38 +- .../VisualizationSimplifiedShould.kt | 44 -- .../internal/VisualizationHandlerShould.kt | 49 +-- .../DataDimensionItemPublicAccessShould.java | 59 --- 70 files changed, 1415 insertions(+), 2833 deletions(-) delete mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramDataElementStoreIntegrationShould.kt delete mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStoreIntegrationShould.kt delete mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStoreIntegrationShould.kt rename core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/{DataDimensionItemProgramAttributeStoreIntegrationShould.kt => VisualizationDimensionItemStoreIntegrationShould.kt} (72%) delete mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.java create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/ObjectWithUidListColumnAdapter.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/RelativePeriodsColumnAdapter.kt rename core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/{identifiable/internal/DataDimensionItemProgramAttributeWithUidColumnAdapter.java => enums/internal/LayoutPositionColumnAdapter.kt} (75%) rename core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/{IgnoreDataDimensionItemListColumnAdapter.java => IgnoreVisualizationDimensionListColumnAdapter.java} (89%) rename core/src/main/java/org/hisp/dhis/android/core/arch/db/{adapters/custom/internal/IntegerListColumnAdapter.kt => uidseeker/internal/BaseUidsSeeker.kt} (72%) rename core/src/main/java/org/hisp/dhis/android/core/{visualization/internal/CategoryDimensionFields.kt => expressiondimensionitem/internal/ExpressionDimensionItemModuleDownloader.kt} (67%) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleWiper.kt rename core/src/main/java/org/hisp/dhis/android/core/{arch/db/adapters/identifiable/internal/DataDimensionItemProgramDataElementWithUidColumnAdapter.java => expressiondimensionitem/internal/ExpressionDimensionItemPackageDIModule.kt} (74%) rename core/src/main/java/org/hisp/dhis/android/core/{visualization/internal/DataDimensionItemEntityDIModule.kt => expressiondimensionitem/internal/ExpressionDimensionItemUidsSeeker.kt} (65%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItem.java delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramDataElement.java delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemTableInfo.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/DimensionItemType.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/LayoutPosition.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimension.java rename core/src/main/java/org/hisp/dhis/android/core/visualization/{DataDimensionItemProgramAttribute.java => VisualizationDimensionItem.java} (59%) rename core/src/main/java/org/hisp/dhis/android/core/visualization/{VisualizationCategoryDimensionLinkTableInfo.kt => VisualizationDimensionItemTableInfo.kt} (79%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemFields.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStore.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionChildrenAppender.kt rename core/src/main/java/org/hisp/dhis/android/core/visualization/internal/{VisualizationDataDimensionItemChildrenAppender.kt => VisualizationColumnsRowsFiltersChildrenAppender.kt} (51%) rename core/src/main/java/org/hisp/dhis/android/core/visualization/internal/{DataDimensionItemProgramDataElementFields.kt => VisualizationDimensionFields.kt} (69%) rename core/src/main/java/org/hisp/dhis/android/core/visualization/internal/{VisualizationCategoryDimensionEntityDIModule.kt => VisualizationDimensionItemEntityDIModule.kt} (83%) rename core/src/main/java/org/hisp/dhis/android/core/visualization/internal/{DataDimensionItemProgramAttributeFields.kt => VisualizationDimensionItemFields.kt} (73%) rename core/src/main/java/org/hisp/dhis/android/core/visualization/internal/{VisualizationCategoryDimensionLinkStore.kt => VisualizationDimensionItemStore.kt} (77%) delete mode 100644 core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/DataDimensionItemSamples.kt create mode 100644 core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationDimensionItemSamples.kt create mode 100644 core/src/sharedTest/resources/expressiondimensionitem/expression_dimension_items.json delete mode 100644 core/src/sharedTest/resources/visualization/visualization_simplified.json delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationSimplifiedShould.kt delete mode 100644 core/src/test/java/org/hisp/dhis/android/testapp/visualization/DataDimensionItemPublicAccessShould.java diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramDataElementStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramDataElementStoreIntegrationShould.kt deleted file mode 100644 index 37544c6bb6..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramDataElementStoreIntegrationShould.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization.internal - -import org.hisp.dhis.android.core.data.database.LinkStoreAbstractIntegrationShould -import org.hisp.dhis.android.core.data.visualization.DataDimensionItemSamples -import org.hisp.dhis.android.core.utils.integration.mock.TestDatabaseAdapterFactory -import org.hisp.dhis.android.core.utils.runner.D2JunitRunner -import org.hisp.dhis.android.core.visualization.DataDimensionItem -import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo -import org.junit.runner.RunWith - -@RunWith(D2JunitRunner::class) -class DataDimensionItemProgramDataElementStoreIntegrationShould : - LinkStoreAbstractIntegrationShould( - DataDimensionItemStore.create(TestDatabaseAdapterFactory.get()), - DataDimensionItemTableInfo.TABLE_INFO, - TestDatabaseAdapterFactory.get() - ) { - override fun addMasterUid(): String { - return "visualization_uid" - } - - override fun buildObject(): DataDimensionItem { - return DataDimensionItemSamples.dataDimensionItemProgramDataElement() - } - - override fun buildObjectWithOtherMasterUid(): DataDimensionItem { - return DataDimensionItemSamples.dataDimensionItemProgramDataElement().toBuilder() - .visualization("visualization_uid_2") - .build() - } -} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStoreIntegrationShould.kt deleted file mode 100644 index 8404552aa3..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStoreIntegrationShould.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization.internal - -import org.hisp.dhis.android.core.data.database.LinkStoreAbstractIntegrationShould -import org.hisp.dhis.android.core.data.visualization.DataDimensionItemSamples -import org.hisp.dhis.android.core.utils.integration.mock.TestDatabaseAdapterFactory -import org.hisp.dhis.android.core.utils.runner.D2JunitRunner -import org.hisp.dhis.android.core.visualization.DataDimensionItem -import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo -import org.junit.runner.RunWith - -@RunWith(D2JunitRunner::class) -class DataDimensionItemStoreIntegrationShould : - LinkStoreAbstractIntegrationShould( - DataDimensionItemStore.create(TestDatabaseAdapterFactory.get()), - DataDimensionItemTableInfo.TABLE_INFO, - TestDatabaseAdapterFactory.get() - ) { - override fun addMasterUid(): String { - return "visualization_uid" - } - - override fun buildObject(): DataDimensionItem { - return DataDimensionItemSamples.dataDimensionItem() - } - - override fun buildObjectWithOtherMasterUid(): DataDimensionItem { - return DataDimensionItemSamples.dataDimensionItem().toBuilder() - .visualization("visualization_uid_2") - .build() - } -} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStoreIntegrationShould.kt deleted file mode 100644 index d07a39ecdb..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStoreIntegrationShould.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization.internal - -import org.hisp.dhis.android.core.data.database.LinkStoreAbstractIntegrationShould -import org.hisp.dhis.android.core.data.visualization.VisualizationCategoryDimensionLinkSamples -import org.hisp.dhis.android.core.utils.integration.mock.TestDatabaseAdapterFactory -import org.hisp.dhis.android.core.utils.runner.D2JunitRunner -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLink -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLinkTableInfo -import org.junit.runner.RunWith - -@RunWith(D2JunitRunner::class) -class VisualizationCategoryDimensionLinkStoreIntegrationShould : - LinkStoreAbstractIntegrationShould( - VisualizationCategoryDimensionLinkStore.create(TestDatabaseAdapterFactory.get()), - VisualizationCategoryDimensionLinkTableInfo.TABLE_INFO, - TestDatabaseAdapterFactory.get() - ) { - override fun addMasterUid(): String { - return VisualizationCategoryDimensionLinkSamples.visualizationCategoryDimensionLinkSamples().visualization() - } - - override fun buildObject(): VisualizationCategoryDimensionLink { - return VisualizationCategoryDimensionLinkSamples.visualizationCategoryDimensionLinkSamples() - } - - override fun buildObjectWithOtherMasterUid(): VisualizationCategoryDimensionLink { - return VisualizationCategoryDimensionLinkSamples.visualizationCategoryDimensionLinkSamples().toBuilder() - .visualization("visualization_uid_2") - .build() - } -} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramAttributeStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStoreIntegrationShould.kt similarity index 72% rename from core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramAttributeStoreIntegrationShould.kt rename to core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStoreIntegrationShould.kt index ece84a746f..7bbddea0f1 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramAttributeStoreIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStoreIntegrationShould.kt @@ -28,30 +28,30 @@ package org.hisp.dhis.android.core.visualization.internal import org.hisp.dhis.android.core.data.database.LinkStoreAbstractIntegrationShould -import org.hisp.dhis.android.core.data.visualization.DataDimensionItemSamples +import org.hisp.dhis.android.core.data.visualization.VisualizationDimensionItemSamples import org.hisp.dhis.android.core.utils.integration.mock.TestDatabaseAdapterFactory import org.hisp.dhis.android.core.utils.runner.D2JunitRunner -import org.hisp.dhis.android.core.visualization.DataDimensionItem -import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo import org.junit.runner.RunWith @RunWith(D2JunitRunner::class) -class DataDimensionItemProgramAttributeStoreIntegrationShould : - LinkStoreAbstractIntegrationShould( - DataDimensionItemStore.create(TestDatabaseAdapterFactory.get()), - DataDimensionItemTableInfo.TABLE_INFO, +class VisualizationDimensionItemStoreIntegrationShould : + LinkStoreAbstractIntegrationShould( + VisualizationDimensionItemStore.create(TestDatabaseAdapterFactory.get()), + VisualizationDimensionItemTableInfo.TABLE_INFO, TestDatabaseAdapterFactory.get() ) { override fun addMasterUid(): String { return "visualization_uid" } - override fun buildObject(): DataDimensionItem { - return DataDimensionItemSamples.dataDimensionItemAttribute() + override fun buildObject(): VisualizationDimensionItem { + return VisualizationDimensionItemSamples.visualizationDimensionItem() } - override fun buildObjectWithOtherMasterUid(): DataDimensionItem { - return DataDimensionItemSamples.dataDimensionItemAttribute().toBuilder() + override fun buildObjectWithOtherMasterUid(): VisualizationDimensionItem { + return VisualizationDimensionItemSamples.visualizationDimensionItem().toBuilder() .visualization("visualization_uid_2") .build() } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.java deleted file mode 100644 index 0c8473bb11..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2004-2022, 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.android.testapp.visualization; - -import static com.google.common.truth.Truth.assertThat; - -import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; -import org.hisp.dhis.android.core.utils.runner.D2JunitRunner; -import org.hisp.dhis.android.core.visualization.DataDimensionItemType; -import org.hisp.dhis.android.core.visualization.HideEmptyItemStrategy; -import org.hisp.dhis.android.core.visualization.Visualization; -import org.hisp.dhis.android.core.visualization.VisualizationType; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.List; - -@RunWith(D2JunitRunner.class) -public class VisualizationCollectionRepositoryMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { - - @Test - public void find_all() { - List visualizations = d2.visualizationModule().visualizations().blockingGet(); - assertThat(visualizations.size()).isEqualTo(2); - } - - @Test - public void find_uids() { - List visualizationUids = d2.visualizationModule().visualizations() - .blockingGetUids(); - assertThat(visualizationUids.size()).isEqualTo(2); - assertThat(visualizationUids.contains("PYBH8ZaAQnC")).isTrue(); - assertThat(visualizationUids.contains("FAFa11yFeFe")).isTrue(); - } - - @Test - public void find_by_description() { - List visualizations = d2.visualizationModule().visualizations() - .byDescription().eq("Sample visualization for the Android SDK") - .blockingGet(); - assertThat(visualizations.size()).isEqualTo(1); - assertThat(visualizations.get(0).uid()).isEqualTo("PYBH8ZaAQnC"); - } - - @Test - public void find_by_display_description() { - List visualizations = d2.visualizationModule().visualizations() - .byDisplayDescription().eq("Sample visualization for the Android SDK") - .blockingGet(); - assertThat(visualizations.size()).isEqualTo(1); - assertThat(visualizations.get(0).uid()).isEqualTo("PYBH8ZaAQnC"); - } - - @Test - public void find_by_display_form_name() { - List visualizations = d2.visualizationModule().visualizations() - .byDisplayFormName().eq("Android SDK Visualization sample") - .blockingGet(); - assertThat(visualizations.size()).isEqualTo(1); - assertThat(visualizations.get(0).uid()).isEqualTo("PYBH8ZaAQnC"); - } - - @Test - public void find_by_display_title() { - List visualizations = d2.visualizationModule().visualizations() - .byDisplayTitle().eq("Sample title") - .blockingGet(); - assertThat(visualizations.size()).isEqualTo(1); - assertThat(visualizations.get(0).uid()).isEqualTo("PYBH8ZaAQnC"); - } - - @Test - public void filter_by_legend_id() { - List visualizations = d2.visualizationModule().visualizations() - .byLegendUid() - .eq("Yf6UHoPkd57") - .blockingGet(); - - assertThat(visualizations.size()).isEqualTo(1); - } - - @Test - public void filter_by_legend_style() { - List visualizations = d2.visualizationModule().visualizations() - .byLegendStyle() - .eq("FILL") - .blockingGet(); - - assertThat(visualizations.size()).isEqualTo(1); - } - - @Test - public void filter_by_legend_show_key() { - List visualizations = d2.visualizationModule().visualizations() - .byLegendShowKey() - .isFalse() - .blockingGet(); - - assertThat(visualizations.size()).isEqualTo(2); - } - - @Test - public void filter_by_legend_strategy() { - List indicators = d2.visualizationModule().visualizations() - .byLegendStrategy() - .eq("FIXED") - .blockingGet(); - - assertThat(indicators.size()).isEqualTo(2); - } - - @Test - public void find_by_display_subtitle() { - List visualizations = d2.visualizationModule().visualizations() - .byDisplaySubtitle().eq("Sample subtitle") - .blockingGet(); - assertThat(visualizations.size()).isEqualTo(1); - assertThat(visualizations.get(0).uid()).isEqualTo("PYBH8ZaAQnC"); - } - - @Test - public void find_by_visualization_type() { - List visualizations = d2.visualizationModule().visualizations() - .byType().eq(VisualizationType.COLUMN) - .blockingGet(); - assertThat(visualizations.size()).isEqualTo(1); - assertThat(visualizations.get(0).uid()).isEqualTo("FAFa11yFeFe"); - } - - @Test - public void find_by_hide_title() { - List visualizations = d2.visualizationModule().visualizations() - .byHideTitle().isFalse() - .blockingGet(); - assertThat(visualizations.size()).isEqualTo(1); - } - - @Test - public void find_by_hide_empty_row_items() { - List visualizations = d2.visualizationModule().visualizations() - .byHideEmptyRowItems().eq(HideEmptyItemStrategy.AFTER_LAST) - .blockingGet(); - assertThat(visualizations.size()).isEqualTo(1); - } - - @Test - public void include_category_dimensions_as_children() { - Visualization visualization = d2.visualizationModule().visualizations() - .withCategoryDimensions().one().blockingGet(); - assertThat(visualization.categoryDimensions().get(0).category().uid()).isEqualTo("KfdsGBcoiCa"); - assertThat(visualization.categoryDimensions().get(0).categoryOptions().get(0).uid()).isEqualTo("TNYQzTHdoxL"); - assertThat(visualization.categoryDimensions().get(0).categoryOptions().get(1).uid()).isEqualTo("TXGfLxZlInA"); - assertThat(visualization.categoryDimensions().get(0).categoryOptions().get(2).uid()).isEqualTo("uZUnebiT5DI"); - } - - @Test - public void include_data_dimension_item_as_children() { - Visualization visualization = d2.visualizationModule().visualizations() - .withDataDimensionItems().one().blockingGet(); - assertThat(visualization.dataDimensionItems().get(0).dataDimensionItemType()).isEqualTo(DataDimensionItemType.INDICATOR); - assertThat(visualization.dataDimensionItems().get(0).dataDimensionItem()).isEqualTo("Uvn6LCg7dVU"); - } -} \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt new file mode 100644 index 0000000000..ec6a83ae38 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2004-2022, 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.android.testapp.visualization + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.hisp.dhis.android.core.visualization.HideEmptyItemStrategy +import org.hisp.dhis.android.core.visualization.VisualizationType +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class VisualizationCollectionRepositoryMockIntegrationShould : BaseMockIntegrationTestFullDispatcher() { + @Test + fun find_all() { + val visualizations = d2.visualizationModule().visualizations() + .blockingGet() + + assertThat(visualizations.size).isEqualTo(2) + } + + @Test + fun find_uids() { + val visualizationUids = d2.visualizationModule().visualizations() + .blockingGetUids() + + assertThat(visualizationUids.size).isEqualTo(2) + assertThat(visualizationUids.contains("PYBH8ZaAQnC")).isTrue() + assertThat(visualizationUids.contains("FAFa11yFeFe")).isTrue() + } + + @Test + fun find_by_description() { + val visualizations = d2.visualizationModule().visualizations() + .byDescription().eq("Sample visualization for the Android SDK") + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + assertThat(visualizations[0].uid()).isEqualTo("PYBH8ZaAQnC") + } + + @Test + fun find_by_display_description() { + val visualizations = d2.visualizationModule().visualizations() + .byDisplayDescription().eq("Sample visualization for the Android SDK") + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + assertThat(visualizations[0].uid()).isEqualTo("PYBH8ZaAQnC") + } + + @Test + fun find_by_display_form_name() { + val visualizations = d2.visualizationModule().visualizations() + .byDisplayFormName().eq("Android SDK Visualization sample") + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + assertThat(visualizations[0].uid()).isEqualTo("PYBH8ZaAQnC") + } + + @Test + fun find_by_display_title() { + val visualizations = d2.visualizationModule().visualizations() + .byDisplayTitle().eq("Sample title") + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + assertThat(visualizations[0].uid()).isEqualTo("PYBH8ZaAQnC") + } + + @Test + fun filter_by_legend_id() { + val visualizations = d2.visualizationModule().visualizations() + .byLegendUid() + .eq("Yf6UHoPkd57") + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + } + + @Test + fun filter_by_legend_style() { + val visualizations = d2.visualizationModule().visualizations() + .byLegendStyle().eq("FILL") + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + } + + @Test + fun filter_by_legend_show_key() { + val visualizations = d2.visualizationModule().visualizations() + .byLegendShowKey().isFalse + .blockingGet() + + assertThat(visualizations.size).isEqualTo(2) + } + + @Test + fun filter_by_legend_strategy() { + val indicators = d2.visualizationModule().visualizations() + .byLegendStrategy().eq("FIXED") + .blockingGet() + + assertThat(indicators.size).isEqualTo(2) + } + + @Test + fun find_by_display_subtitle() { + val visualizations = d2.visualizationModule().visualizations() + .byDisplaySubtitle().eq("Sample subtitle") + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + assertThat(visualizations[0].uid()).isEqualTo("PYBH8ZaAQnC") + } + + @Test + fun find_by_visualization_type() { + val visualizations = d2.visualizationModule().visualizations() + .byType().eq(VisualizationType.COLUMN) + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + assertThat(visualizations[0].uid()).isEqualTo("FAFa11yFeFe") + } + + @Test + fun find_by_hide_title() { + val visualizations = d2.visualizationModule().visualizations() + .byHideTitle().isFalse + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + } + + @Test + fun find_by_hide_empty_row_items() { + val visualizations = d2.visualizationModule().visualizations() + .byHideEmptyRowItems().eq(HideEmptyItemStrategy.AFTER_LAST) + .blockingGet() + + assertThat(visualizations.size).isEqualTo(1) + } + + @Test + fun include_columns_rows_and_filters_as_children() { + val visualization = d2.visualizationModule().visualizations() + .withColumnsRowsAndFilters() + .one() + .blockingGet() + + assertThat(visualization.columns()!![0].id()).isEqualTo("dx") + assertThat(visualization.columns()!![0].items()!!.size).isEqualTo(3) + assertThat(visualization.rows()!![0].id()).isEqualTo("pe") + assertThat(visualization.rows()!![0].items()!!.size).isEqualTo(4) + assertThat(visualization.filters()!![0].id()).isEqualTo("ou") + assertThat(visualization.filters()!![0].items()!!.size).isEqualTo(3) + } +} diff --git a/core/src/main/assets/migrations/146.sql b/core/src/main/assets/migrations/146.sql index f00a37e46e..5a9a502e33 100644 --- a/core/src/main/assets/migrations/146.sql +++ b/core/src/main/assets/migrations/146.sql @@ -1,3 +1,18 @@ # Add Calculation support (ANDROSDK-1687) -CREATE TABLE ExpressionDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, expression TEXT); \ No newline at end of file +CREATE TABLE ExpressionDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, expression TEXT); + +CREATE TABLE VisualizationDimensionItem(_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, position TEXT NOT NULL, dimension TEXT NOT NULL, dimensionItem TEXT NOT NULL, dimensionItemType TEXT, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); + +ALTER TABLE Visualization RENAME TO Visualization_old; +CREATE TABLE Visualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, displayDescription TEXT, displayFormName TEXT, title TEXT, displayTitle TEXT, subtitle TEXT, displaySubtitle TEXT, type TEXT, hideTitle INTEGER, hideSubtitle INTEGER, hideEmptyColumns INTEGER, hideEmptyRows INTEGER, hideEmptyRowItems TEXT, hideLegend INTEGER, showHierarchy INTEGER, rowTotals INTEGER, rowSubTotals INTEGER, colTotals INTEGER, colSubTotals INTEGER, showDimensionLabels INTEGER, percentStackedValues INTEGER, noSpaceBetweenColumns INTEGER, skipRounding INTEGER, displayDensity TEXT, digitGroupSeparator TEXT, legendShowKey TEXT, legendStyle TEXT, legendSetId TEXT, legendStrategy TEXT, aggregationType TEXT); +INSERT INTO Visualization (_id, uid, code, name, displayName, created, lastUpdated, description, displayDescription, displayFormName, title, displayTitle, subtitle, displaySubtitle, type, hideTitle, hideSubtitle, hideEmptyColumns, hideEmptyRows, hideEmptyRowItems, hideLegend, showHierarchy, rowTotals, rowSubTotals, colTotals, colSubTotals, showDimensionLabels, percentStackedValues, noSpaceBetweenColumns, skipRounding, displayDensity, digitGroupSeparator, legendShowKey, legendStyle, legendSetId, legendStrategy, aggregationType) SELECT _id, uid, code, name, displayName, created, lastUpdated, description, displayDescription, displayFormName, title, displayTitle, subtitle, displaySubtitle, type, hideTitle, hideSubtitle, hideEmptyColumns, hideEmptyRows, hideEmptyRowItems, hideLegend, showHierarchy, rowTotals, rowSubTotals, colTotals, colSubTotals, showDimensionLabels, percentStackedValues, noSpaceBetweenColumns, skipRounding, displayDensity, digitGroupSeparator, legendShowKey, legendStyle, legendSetId, legendStrategy, aggregationType FROM Visualization_old; + +DROP TABLE IF EXISTS Visualization_old; +DROP TABLE IF EXISTS VisualizationCategoryDimensionLink; +DROP TABLE IF EXISTS DataDimensionItem; + +# TO DELETE +#CREATE TABLE Visualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, displayDescription TEXT, displayFormName TEXT, type TEXT, hideTitle INTEGER, hideSubtitle INTEGER, hideEmptyColumns INTEGER, hideEmptyRows INTEGER, hideEmptyRowItems TEXT, hideLegend INTEGER, showHierarchy INTEGER, rowTotals INTEGER, rowSubTotals INTEGER, colTotals INTEGER, colSubTotals INTEGER, showDimensionLabels INTEGER, percentStackedValues INTEGER, noSpaceBetweenColumns INTEGER, skipRounding INTEGER, displayDensity TEXT, digitGroupSeparator TEXT, relativePeriods TEXT, filterDimensions TEXT, rowDimensions TEXT, columnDimensions TEXT, organisationUnitLevels TEXT, userOrganisationUnit INTEGER, userOrganisationUnitChildren INTEGER, userOrganisationUnitGrandChildren INTEGER, organisationUnits TEXT, periods TEXT, title TEXT, displayTitle TEXT, subtitle TEXT, displaySubtitle TEXT, legendShowKey TEXT, legendStyle TEXT, legendSetId TEXT, legendStrategy TEXT, aggregationType TEXT); +#CREATE TABLE VisualizationCategoryDimensionLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, category TEXT NOT NULL, categoryOption TEXT, FOREIGN KEY (category) REFERENCES Category (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryOption) REFERENCES CategoryOption (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +#CREATE TABLE DataDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, dataDimensionItemType TEXT, indicator TEXT, dataElement TEXT, dataElementOperand TEXT, reportingRate TEXT, programIndicator TEXT, programDataElement TEXT, programAttribute TEXT, validationRule TEXT, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); diff --git a/core/src/main/assets/snapshots/snapshot.sql b/core/src/main/assets/snapshots/snapshot.sql index b6933444ea..46d3593e9b 100644 --- a/core/src/main/assets/snapshots/snapshot.sql +++ b/core/src/main/assets/snapshots/snapshot.sql @@ -110,9 +110,8 @@ CREATE TABLE ProgramAttributeValueLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, p CREATE TABLE TrackerJobObject (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackerType TEXT NOT NULL, objectUid TEXT NOT NULL, jobUid TEXT NOT NULL, lastUpdated TEXT NOT NULL, fileResources TEXT); CREATE TABLE DataValueConflict (_id INTEGER PRIMARY KEY AUTOINCREMENT, conflict TEXT, value TEXT, attributeOptionCombo TEXT, categoryOptionCombo TEXT, dataElement TEXT, period TEXT, orgUnit TEXT, errorCode TEXT, status TEXT, created TEXT, displayDescription TEXT); CREATE TABLE AnalyticsDhisVisualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL, scopeUid TEXT, scope TEXT, groupUid TEXT, groupName TEXT, timestamp TEXT, name TEXT, FOREIGN KEY (uid) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE Visualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, displayDescription TEXT, displayFormName TEXT, type TEXT, hideTitle INTEGER, hideSubtitle INTEGER, hideEmptyColumns INTEGER, hideEmptyRows INTEGER, hideEmptyRowItems TEXT, hideLegend INTEGER, showHierarchy INTEGER, rowTotals INTEGER, rowSubTotals INTEGER, colTotals INTEGER, colSubTotals INTEGER, showDimensionLabels INTEGER, percentStackedValues INTEGER, noSpaceBetweenColumns INTEGER, skipRounding INTEGER, displayDensity TEXT, digitGroupSeparator TEXT, relativePeriods TEXT, filterDimensions TEXT, rowDimensions TEXT, columnDimensions TEXT, organisationUnitLevels TEXT, userOrganisationUnit INTEGER, userOrganisationUnitChildren INTEGER, userOrganisationUnitGrandChildren INTEGER, organisationUnits TEXT, periods TEXT, title TEXT, displayTitle TEXT, subtitle TEXT, displaySubtitle TEXT, legendShowKey TEXT, legendStyle TEXT, legendSetId TEXT, legendStrategy TEXT, aggregationType TEXT); -CREATE TABLE VisualizationCategoryDimensionLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, category TEXT NOT NULL, categoryOption TEXT, FOREIGN KEY (category) REFERENCES Category (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryOption) REFERENCES CategoryOption (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE DataDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, dataDimensionItemType TEXT, indicator TEXT, dataElement TEXT, dataElementOperand TEXT, reportingRate TEXT, programIndicator TEXT, programDataElement TEXT, programAttribute TEXT, validationRule TEXT, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE Visualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, displayDescription TEXT, displayFormName TEXT, title TEXT, displayTitle TEXT, subtitle TEXT, displaySubtitle TEXT, type TEXT, hideTitle INTEGER, hideSubtitle INTEGER, hideEmptyColumns INTEGER, hideEmptyRows INTEGER, hideEmptyRowItems TEXT, hideLegend INTEGER, showHierarchy INTEGER, rowTotals INTEGER, rowSubTotals INTEGER, colTotals INTEGER, colSubTotals INTEGER, showDimensionLabels INTEGER, percentStackedValues INTEGER, noSpaceBetweenColumns INTEGER, skipRounding INTEGER, displayDensity TEXT, digitGroupSeparator TEXT, legendShowKey TEXT, legendStyle TEXT, legendSetId TEXT, legendStrategy TEXT, aggregationType TEXT); +CREATE TABLE VisualizationDimensionItem(_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, position TEXT NOT NULL, dimension TEXT NOT NULL, dimensionItem TEXT NOT NULL, dimensionItemType TEXT, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE LocalDataStore (_id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT NOT NULL UNIQUE, value TEXT); CREATE TABLE AnalyticsPeriodBoundary (_id INTEGER PRIMARY KEY AUTOINCREMENT, programIndicator TEXT NOT NULL, boundaryTarget TEXT, analyticsPeriodBoundaryType TEXT, offsetPeriods INTEGER, offsetPeriodType TEXT, FOREIGN KEY (programIndicator) REFERENCES ProgramIndicator (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE IndicatorLegendSetLink(_id INTEGER PRIMARY KEY AUTOINCREMENT, indicator TEXT NOT NULL, legendSet TEXT NOT NULL, sortOrder INTEGER, FOREIGN KEY (indicator) REFERENCES Indicator (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (legendSet) REFERENCES LegendSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (indicator, legendSet)); diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt index 2349708dc5..ab7b9e62f1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt @@ -104,6 +104,8 @@ sealed class DimensionItem(val dimension: Dimension, val id: String) { EventDataItem("$program.$dataElement") data class Attribute(val program: String, val attribute: String) : EventDataItem("$program.$attribute") } + + data class ExpressionDimensionItem(val uid: String) : DataItem(uid) } sealed class PeriodItem(id: String) : DimensionItem(Dimension.Period, id) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt index 62cab3514f..2f5b5852a8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt @@ -108,6 +108,7 @@ internal class AnalyticsServiceEvaluatorHelper @Inject constructor( is DimensionItem.DataItem.ProgramIndicatorItem -> programIndicatorEvaluator is DimensionItem.DataItem.IndicatorItem -> indicatorEvaluator is DimensionItem.DataItem.EventDataItem -> eventDataItemEvaluator + is DimensionItem.DataItem.ExpressionDimensionItem -> TODO() } } @@ -142,6 +143,7 @@ internal class AnalyticsServiceEvaluatorHelper @Inject constructor( dimensionDataItem.attribute, value ) + is DimensionItem.DataItem.ExpressionDimensionItem -> TODO() } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt index c9b791e982..75b05c62d0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt @@ -166,6 +166,10 @@ internal class AnalyticsServiceMetadataHelper @Inject constructor( MetadataItem.EventAttributeItem(attribute, program) } + + is DimensionItem.DataItem.ExpressionDimensionItem -> { + TODO() + } } ) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsService.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsService.kt index 3560c60684..1e2c8f583c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsService.kt @@ -65,8 +65,7 @@ internal class AnalyticsVisualizationsService @Inject constructor( private fun getVisualization(visualizationId: String): Visualization? { return visualizationCollectionRepository - .withCategoryDimensions() - .withDataDimensionItems() + .withColumnsRowsAndFilters() .uid(visualizationId) .blockingGet() } @@ -80,11 +79,13 @@ internal class AnalyticsVisualizationsService @Inject constructor( var queryAnalyticsRepository = analyticsRepository val queryDimensions = - (visualization.rowDimensions() ?: emptyList()) + - (visualization.columnDimensions() ?: emptyList()) + (visualization.rows() ?: emptyList()) + + (visualization.columns() ?: emptyList()) - var queryItems = dimensionHelper.getDimensionItems(visualization, queryDimensions) - var filterItems = dimensionHelper.getDimensionItems(visualization, visualization.filterDimensions()) + val filterDimensions = visualization.filters() + + var queryItems = dimensionHelper.getDimensionItems(queryDimensions) + var filterItems = dimensionHelper.getDimensionItems(filterDimensions) // Overwrite periods if (!params.periods.isNullOrEmpty()) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelper.kt index 713d81bfd0..3c05e8b68f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelper.kt @@ -38,11 +38,15 @@ import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStor import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore import org.hisp.dhis.android.core.category.Category import org.hisp.dhis.android.core.category.CategoryCategoryOptionLink +import org.hisp.dhis.android.core.common.RelativeOrganisationUnit import org.hisp.dhis.android.core.common.RelativeOrganisationUnit.* +import org.hisp.dhis.android.core.common.RelativePeriod import org.hisp.dhis.android.core.organisationunit.OrganisationUnitLevel import org.hisp.dhis.android.core.organisationunit.OrganisationUnitLevelTableInfo import org.hisp.dhis.android.core.visualization.DataDimensionItemType import org.hisp.dhis.android.core.visualization.Visualization +import org.hisp.dhis.android.core.visualization.VisualizationDimension +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem internal class AnalyticsVisualizationsServiceDimensionHelper @Inject constructor( private val categoryStore: IdentifiableObjectStore, @@ -53,134 +57,130 @@ internal class AnalyticsVisualizationsServiceDimensionHelper @Inject constructor private val orgUnitDimension = "ou" private val periodDimension = "pe" private val uidRegex = "^\\w{11}\$".toRegex() - private val dataElementOperandRegex = "^(\\w{11})\\.(\\w{11})\$".toRegex() + private val composedUidOperandRegex = "^(\\w{11})\\.(\\w{11})\$".toRegex() + private val orgunitLevelRegex = "LEVEL-(\\d+)".toRegex() - fun getDimensionItems(visualization: Visualization, dimensions: List?): List { + fun getDimensionItems(dimensions: List?): List { return dimensions?.map { dimension -> - when (dimension) { - dataDimension -> extractDataDimensionItems(visualization) - orgUnitDimension -> extractOrgunitDimensionItems(visualization) - periodDimension -> extractPeriodDimensionItems(visualization) + when (dimension.id()) { + dataDimension -> extractDataDimensionItems(dimension.items()) + orgUnitDimension -> extractOrgunitDimensionItems(dimension.items()) + periodDimension -> extractPeriodDimensionItems(dimension.items()) else -> { - if (uidRegex.matches(dimension)) { - extractUidDimensionItems(visualization, dimension) - } else { - emptyList() - } + dimension.id()?.let { + if (uidRegex.matches(it)) { + extractUidDimensionItems(dimension.items(), it) + } else { + emptyList() + } + } ?: emptyList() } } }?.flatten() ?: emptyList() } @Suppress("ComplexMethod") - private fun extractDataDimensionItems(visualization: Visualization): List { - return visualization.dataDimensionItems()?.mapNotNull { item -> - when (item.dataDimensionItemType()) { + private fun extractDataDimensionItems(items: List?): List { + return items?.mapNotNull { item -> + val dataType = DataDimensionItemType.values().find { it.name == item.dimensionItemType() } + + when (dataType) { DataDimensionItemType.INDICATOR -> - item.indicator()?.let { DimensionItem.DataItem.IndicatorItem(it.uid()) } + item.dimensionItem()?.let { DimensionItem.DataItem.IndicatorItem(it) } DataDimensionItemType.DATA_ELEMENT -> - item.dataElement()?.let { DimensionItem.DataItem.DataElementItem(it.uid()) } + item.dimensionItem()?.let { DimensionItem.DataItem.DataElementItem(it) } DataDimensionItemType.DATA_ELEMENT_OPERAND -> - item.dataElementOperand()?.let { - val (dataElement, coc) = dataElementOperandRegex.find(it.uid())!!.destructured + item.dimensionItem()?.let { + val (dataElement, coc) = composedUidOperandRegex.find(it)!!.destructured DimensionItem.DataItem.DataElementOperandItem(dataElement, coc) } DataDimensionItemType.PROGRAM_INDICATOR -> - item.programIndicator()?.let { DimensionItem.DataItem.ProgramIndicatorItem(it.uid()) } + item.dimensionItem()?.let { DimensionItem.DataItem.ProgramIndicatorItem(it) } DataDimensionItemType.PROGRAM_DATA_ELEMENT -> - item.programDataElement()?.let { - it.program()?.uid()?.let { program -> - it.dataElement()?.uid()?.let { dataElement -> - DimensionItem.DataItem.EventDataItem.DataElement(program, dataElement) - } - } + item.dimensionItem()?.let { + val (program, dataElement) = composedUidOperandRegex.find(it)!!.destructured + DimensionItem.DataItem.EventDataItem.DataElement(program, dataElement) } DataDimensionItemType.PROGRAM_ATTRIBUTE -> - item.programAttribute()?.let { - it.program()?.uid()?.let { program -> - it.attribute()?.uid()?.let { attribute -> - DimensionItem.DataItem.EventDataItem.Attribute(program, attribute) - } - } + item.dimensionItem()?.let { + val (program, attribute) = composedUidOperandRegex.find(it)!!.destructured + DimensionItem.DataItem.EventDataItem.Attribute(program, attribute) } + DataDimensionItemType.EXPRESSION_DIMENSION_ITEM -> + item.dimensionItem()?.let { DimensionItem.DataItem.ExpressionDimensionItem(it) } else -> null } } ?: emptyList() } - private fun extractOrgunitDimensionItems(visualization: Visualization): List { - val absolute = visualization.organisationUnits()?.map { - DimensionItem.OrganisationUnitItem.Absolute(it.uid()) - } ?: emptyList() - - val levels = visualization.organisationUnitLevels()?.map { - val level = organisationUnitLevelStore.selectOneWhere( - WhereClauseBuilder() - .appendKeyNumberValue(OrganisationUnitLevelTableInfo.Columns.LEVEL, it) - .build() - ) ?: throw AnalyticsException.InvalidOrganisationUnitLevel(it.toString()) - DimensionItem.OrganisationUnitItem.Level(level.uid()) - } ?: emptyList() + private fun extractOrgunitDimensionItems(items: List?): List { + return items?.mapNotNull { it.dimensionItem() }?.map { item -> + val relativeOrgUnit = RelativeOrganisationUnit.values().find { it.name == item } - val relative = listOfNotNull( - if (visualization.userOrganisationUnit() == true) USER_ORGUNIT else null, - if (visualization.userOrganisationUnitChildren() == true) USER_ORGUNIT_CHILDREN else null, - if (visualization.userOrganisationUnitGrandChildren() == true) USER_ORGUNIT_GRANDCHILDREN else null - ).map { item -> - DimensionItem.OrganisationUnitItem.Relative(item) - } + when { + relativeOrgUnit != null -> { + DimensionItem.OrganisationUnitItem.Relative(relativeOrgUnit) + } - return absolute + levels + relative - } + orgunitLevelRegex.matches(item) -> { + val (levelNumber) = orgunitLevelRegex.find(item)!!.destructured + val level = organisationUnitLevelStore.selectOneWhere( + WhereClauseBuilder() + .appendKeyNumberValue(OrganisationUnitLevelTableInfo.Columns.LEVEL, levelNumber.toInt()) + .build() + ) ?: throw AnalyticsException.InvalidOrganisationUnitLevel(levelNumber) + DimensionItem.OrganisationUnitItem.Level(level.uid()) + } - private fun extractPeriodDimensionItems(visualization: Visualization): List { - val absolute = visualization.periods()?.map { - DimensionItem.PeriodItem.Absolute(it.uid()) + else -> { + DimensionItem.OrganisationUnitItem.Absolute(item) + } + } } ?: emptyList() + } - val relative = visualization.relativePeriods() - ?.filter { it.value == true } - ?.map { - DimensionItem.PeriodItem.Relative(it.key) - } ?: emptyList() + private fun extractPeriodDimensionItems(items: List?): List { + return items?.mapNotNull { it.dimensionItem() }?.map { item -> + val relativePeriod = RelativePeriod.values().find { it.name == item } - return absolute + relative + if (relativePeriod != null) { + DimensionItem.PeriodItem.Relative(relativePeriod) + } else { + DimensionItem.PeriodItem.Absolute(item) + } + } ?: emptyList() } - private fun extractUidDimensionItems(visualization: Visualization, uid: String): List { - return categoryStore.selectByUid(uid)?.let { - visualization.categoryDimensions() - ?.find { it.category()?.uid() == uid } - ?.let { categoryDimension -> - val categoryOptions = - if (categoryDimension.categoryOptions().isNullOrEmpty()) { - categoryOptionLinkStore.selectLinksForMasterUid(uid).mapNotNull { it.categoryOption() } - } else { - categoryDimension.categoryOptions()!!.map { it.uid() } - } - - categoryOptions.map { DimensionItem.CategoryItem(uid, it) } + private fun extractUidDimensionItems(items: List?, uid: String): List { + return categoryStore.selectByUid(uid)?.let { category -> + val categoryOptions = + if (items.isNullOrEmpty()) { + categoryOptionLinkStore.selectLinksForMasterUid(category.uid()).mapNotNull { it.categoryOption() } + } else { + items.mapNotNull { it.dimensionItem() } } + + categoryOptions.map { DimensionItem.CategoryItem(category.uid(), it) } } ?: emptyList() } fun getGridDimensions(visualization: Visualization): GridDimension { - val columns = mapDimensions(visualization.columnDimensions()) - val rows = mapDimensions(visualization.rowDimensions()) + val columns = mapDimensions(visualization.columns()) + val rows = mapDimensions(visualization.rows()) return GridDimension(columns, rows) } - private fun mapDimensions(dimensionStrs: List?): List { - return dimensionStrs?.mapNotNull { - when (it) { + private fun mapDimensions(dimensionStrs: List?): List { + return dimensionStrs?.mapNotNull { dimension -> + when (dimension.id()) { dataDimension -> Dimension.Data periodDimension -> Dimension.Period orgUnitDimension -> Dimension.OrganisationUnit else -> { - if (uidRegex.matches(it)) { - extractUidDimension(it) + if (uidRegex.matches(dimension.id()!!)) { + extractUidDimension(dimension) } else { null } @@ -189,11 +189,9 @@ internal class AnalyticsVisualizationsServiceDimensionHelper @Inject constructor } ?: emptyList() } - private fun extractUidDimension(uid: String): Dimension? { - val category = categoryStore.selectByUid(uid) - - return if (category != null) { - Dimension.Category(uid) + private fun extractUidDimension(dimension: VisualizationDimension): Dimension? { + return if (dimension.items()?.all { it.dimensionItemType() == "CATEGORY_OPTION" } == true) { + Dimension.Category(dimension.id()!!) } else { null } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt index d1b0d0fd53..aa72263fdf 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt @@ -63,6 +63,7 @@ import org.hisp.dhis.android.core.domain.aggregated.internal.AggregatedModuleImp import org.hisp.dhis.android.core.domain.metadata.internal.MetadataModuleImpl import org.hisp.dhis.android.core.enrollment.internal.EnrollmentPackageDIModule import org.hisp.dhis.android.core.event.internal.EventPackageDIModule +import org.hisp.dhis.android.core.expressiondimensionitem.internal.ExpressionDimensionItemPackageDIModule import org.hisp.dhis.android.core.fileresource.internal.FileResourcePackageDIModule import org.hisp.dhis.android.core.imports.internal.ImportPackageDIModule import org.hisp.dhis.android.core.indicator.internal.IndicatorPackageDIModule @@ -123,6 +124,7 @@ import retrofit2.Retrofit DataValuePackageDIModule::class, EnrollmentPackageDIModule::class, EventPackageDIModule::class, + ExpressionDimensionItemPackageDIModule::class, FileResourcePackageDIModule::class, ImportPackageDIModule::class, IndicatorPackageDIModule::class, diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/ObjectWithUidListColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/ObjectWithUidListColumnAdapter.kt deleted file mode 100644 index 1ab74f659f..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/ObjectWithUidListColumnAdapter.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.arch.db.adapters.custom.internal - -import android.content.ContentValues -import android.database.Cursor -import com.fasterxml.jackson.core.JsonProcessingException -import com.fasterxml.jackson.databind.JsonMappingException -import com.fasterxml.jackson.module.kotlin.readValue -import com.gabrielittner.auto.value.cursor.ColumnTypeAdapter -import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory -import org.hisp.dhis.android.core.common.BaseIdentifiableObject -import org.hisp.dhis.android.core.common.ObjectWithUid - -internal class ObjectWithUidListColumnAdapter : ColumnTypeAdapter> { - - override fun fromCursor(cursor: Cursor, columnName: String): List { - val columnIndex = cursor.getColumnIndex(columnName) - val str = cursor.getString(columnIndex) - return try { - val list = ObjectMapperFactory.objectMapper().readValue>>(str) - list.map { ObjectWithUid.create(it[BaseIdentifiableObject.UID].toString()) } - } catch (e: JsonProcessingException) { - listOf() - } catch (e: JsonMappingException) { - listOf() - } catch (e: IllegalArgumentException) { - listOf() - } catch (e: IllegalStateException) { - listOf() - } - } - - override fun toContentValues(contentValues: ContentValues, columnName: String, o: List?) { - try { - contentValues.put(columnName, serialize(o)) - } catch (e: JsonProcessingException) { - e.printStackTrace() - } - } - - private fun serialize(o: List?): String? = ObjectWithUidListColumnAdapter.serialize(o) - - companion object { - fun serialize(o: List?): String? { - return o?.let { - ObjectMapperFactory.objectMapper().writeValueAsString(it) - } - } - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/RelativePeriodsColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/RelativePeriodsColumnAdapter.kt deleted file mode 100644 index b418f89654..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/RelativePeriodsColumnAdapter.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.arch.db.adapters.custom.internal - -import android.content.ContentValues -import android.database.Cursor -import com.fasterxml.jackson.core.JsonProcessingException -import com.fasterxml.jackson.databind.JsonMappingException -import com.gabrielittner.auto.value.cursor.ColumnTypeAdapter -import java.util.* -import kotlin.collections.HashMap -import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory -import org.hisp.dhis.android.core.common.RelativePeriod - -internal class RelativePeriodsColumnAdapter : ColumnTypeAdapter> { - - override fun fromCursor(cursor: Cursor, columnName: String): Map { - val columnIndex = cursor.getColumnIndex(columnName) - val str = cursor.getString(columnIndex) - return try { - val map = ObjectMapperFactory.objectMapper().readValue(str, (HashMap())::class.java) - map.mapKeys { RelativePeriod.valueOf(it.key) } - } catch (e: JsonProcessingException) { - EnumMap(RelativePeriod::class.java) - } catch (e: JsonMappingException) { - EnumMap(RelativePeriod::class.java) - } catch (e: IllegalArgumentException) { - EnumMap(RelativePeriod::class.java) - } catch (e: IllegalStateException) { - EnumMap(RelativePeriod::class.java) - } - } - - override fun toContentValues(contentValues: ContentValues, columnName: String, o: Map?) { - try { - contentValues.put(columnName, serialize(o)) - } catch (e: JsonProcessingException) { - e.printStackTrace() - } - } - - private fun serialize(o: Map?): String? = RelativePeriodsColumnAdapter.serialize(o) - - companion object { - fun serialize(o: Map?): String? { - return o?.let { - ObjectMapperFactory.objectMapper().writeValueAsString(it.filter { it.value }) - } - } - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/DataDimensionItemProgramAttributeWithUidColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/LayoutPositionColumnAdapter.kt similarity index 75% rename from core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/DataDimensionItemProgramAttributeWithUidColumnAdapter.java rename to core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/LayoutPositionColumnAdapter.kt index cd45935913..e97b282dac 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/DataDimensionItemProgramAttributeWithUidColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/LayoutPositionColumnAdapter.kt @@ -25,18 +25,12 @@ * (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.android.core.arch.db.adapters.enums.internal -package org.hisp.dhis.android.core.arch.db.adapters.identifiable.internal; +import org.hisp.dhis.android.core.visualization.LayoutPosition -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.visualization.DataDimensionItemProgramAttribute; - -public class DataDimensionItemProgramAttributeWithUidColumnAdapter - extends IdentifiableObjectColumnAdapter { - - @Override - protected DataDimensionItemProgramAttribute build(@NonNull String uid) { - return DataDimensionItemProgramAttribute.builder().uid(uid).build(); +class LayoutPositionColumnAdapter : EnumColumnAdapter() { + override fun getEnumClass(): Class { + return LayoutPosition::class.java } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreDataDimensionItemListColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreVisualizationDimensionListColumnAdapter.java similarity index 89% rename from core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreDataDimensionItemListColumnAdapter.java rename to core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreVisualizationDimensionListColumnAdapter.java index 94b13cece0..eb06293f92 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreDataDimensionItemListColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreVisualizationDimensionListColumnAdapter.java @@ -28,10 +28,10 @@ package org.hisp.dhis.android.core.arch.db.adapters.ignore.internal; -import org.hisp.dhis.android.core.visualization.DataDimensionItem; +import org.hisp.dhis.android.core.visualization.VisualizationDimension; import java.util.List; -public final class IgnoreDataDimensionItemListColumnAdapter - extends IgnoreColumnAdapter> { +public final class IgnoreVisualizationDimensionListColumnAdapter + extends IgnoreColumnAdapter> { } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/IntegerListColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/uidseeker/internal/BaseUidsSeeker.kt similarity index 72% rename from core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/IntegerListColumnAdapter.kt rename to core/src/main/java/org/hisp/dhis/android/core/arch/db/uidseeker/internal/BaseUidsSeeker.kt index 4db9e81243..da243f6167 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/IntegerListColumnAdapter.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/uidseeker/internal/BaseUidsSeeker.kt @@ -25,22 +25,25 @@ * (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.android.core.arch.db.adapters.custom.internal -import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory +package org.hisp.dhis.android.core.arch.db.uidseeker.internal -internal class IntegerListColumnAdapter : JSONObjectListColumnAdapter() { - override fun getObjectClass(): Class> { - return ArrayList().javaClass - } +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter + +internal open class BaseUidsSeeker constructor(private val databaseAdapter: DatabaseAdapter) { - override fun serialize(o: List?): String? = IntegerListColumnAdapter.serialize(o) + fun readSingleColumnResults(query: String): Set { + val cursor = databaseAdapter.rawQuery(query) - companion object { - fun serialize(o: List?): String? { - return o?.let { - ObjectMapperFactory.objectMapper().writeValueAsString(it) + val results: MutableSet = HashSet() + cursor.use { c -> + if (c.count > 0) { + c.moveToFirst() + do { + results.add(c.getString(0)) + } while (c.moveToNext()) } } + return results } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboUidsSeeker.kt index cc9642f268..877e350066 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboUidsSeeker.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboUidsSeeker.kt @@ -31,6 +31,7 @@ import dagger.Reusable import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.querybuilders.internal.MultipleTableQueryBuilder +import org.hisp.dhis.android.core.arch.db.uidseeker.internal.BaseUidsSeeker import org.hisp.dhis.android.core.dataelement.DataElementTableInfo import org.hisp.dhis.android.core.dataset.DataSetElementLinkTableInfo import org.hisp.dhis.android.core.dataset.DataSetTableInfo @@ -38,8 +39,8 @@ import org.hisp.dhis.android.core.program.ProgramTableInfo @Reusable internal class CategoryComboUidsSeeker @Inject constructor( - private val databaseAdapter: DatabaseAdapter -) { + databaseAdapter: DatabaseAdapter +) : BaseUidsSeeker(databaseAdapter) { fun seekUids(): Set { val tableNames = listOf( ProgramTableInfo.TABLE_INFO.name(), @@ -50,16 +51,6 @@ internal class CategoryComboUidsSeeker @Inject constructor( val query = MultipleTableQueryBuilder() .generateQuery(DataSetTableInfo.Columns.CATEGORY_COMBO, tableNames).build() - val categoryCombos: MutableSet = HashSet() - - databaseAdapter.rawQuery(query).use { cursor -> - if (cursor.count > 0) { - cursor.moveToFirst() - do { - categoryCombos.add(cursor.getString(0)) - } while (cursor.moveToNext()) - } - } - return categoryCombos + return readSingleColumnResults(query) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/domain/metadata/MetadataCall.kt b/core/src/main/java/org/hisp/dhis/android/core/domain/metadata/MetadataCall.kt index f890e1d06a..d0d0710210 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/domain/metadata/MetadataCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/domain/metadata/MetadataCall.kt @@ -44,6 +44,8 @@ import org.hisp.dhis.android.core.constant.Constant import org.hisp.dhis.android.core.constant.internal.ConstantModuleDownloader import org.hisp.dhis.android.core.dataset.DataSet import org.hisp.dhis.android.core.dataset.internal.DataSetModuleDownloader +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import org.hisp.dhis.android.core.expressiondimensionitem.internal.ExpressionDimensionItemModuleDownloader import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.internal.IndicatorModuleDownloader import org.hisp.dhis.android.core.legendset.LegendSet @@ -90,10 +92,11 @@ internal class MetadataCall @Inject constructor( private val multiUserDatabaseManager: MultiUserDatabaseManager, private val credentialsSecureStore: CredentialsSecureStore, private val legendSetModuleDownloader: LegendSetModuleDownloader, + private val expressionDimensionItemModuleDownloader: ExpressionDimensionItemModuleDownloader, ) { companion object { - const val CALLS_COUNT = 11 + const val CALLS_COUNT = 12 } fun download(): Observable { @@ -161,6 +164,9 @@ internal class MetadataCall @Inject constructor( }, legendSetModuleDownloader.downloadMetadata().toSingle { progressManager.increaseProgress(LegendSet::class.java, false) + }, + expressionDimensionItemModuleDownloader.downloadMetadata().toSingle { + progressManager.increaseProgress(ExpressionDimensionItem::class.java, false) } ).toObservable() ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/CategoryDimensionFields.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleDownloader.kt similarity index 67% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/internal/CategoryDimensionFields.kt rename to core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleDownloader.kt index e1242b9339..b511a417e1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/CategoryDimensionFields.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleDownloader.kt @@ -25,22 +25,23 @@ * (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.android.core.visualization.internal -import org.hisp.dhis.android.core.arch.api.fields.internal.Fields -import org.hisp.dhis.android.core.arch.fields.internal.FieldsHelper -import org.hisp.dhis.android.core.visualization.CategoryDimension +package org.hisp.dhis.android.core.expressiondimensionitem.internal -internal object CategoryDimensionFields { - internal const val CATEGORY = "category" - internal const val CATEGORY_OPTIONS = "categoryOptions" - private val fh = FieldsHelper() +import dagger.Reusable +import io.reactivex.Completable +import io.reactivex.Single +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader - val allFields: Fields = - Fields.builder() - .fields( - fh.nestedFieldWithUid(CATEGORY), - fh.nestedFieldWithUid(CATEGORY_OPTIONS) - ) - .build() +@Reusable +internal class ExpressionDimensionItemModuleDownloader @Inject internal constructor( + private val expressionDimensionItemUidsSeeker: ExpressionDimensionItemUidsSeeker, + private val expressionDimensionItemCall: ExpressionDimensionItemCall +) : UntypedModuleDownloader { + override fun downloadMetadata(): Completable { + return Single.fromCallable { expressionDimensionItemUidsSeeker.seekUids() } + .flatMap { expressionDimensionItemCall.download(it) } + .ignoreElement() + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleWiper.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleWiper.kt new file mode 100644 index 0000000000..5a1a8f7d0c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleWiper.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem.internal + +import dagger.Reusable +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemTableInfo +import org.hisp.dhis.android.core.wipe.internal.ModuleWiper +import org.hisp.dhis.android.core.wipe.internal.TableWiper +import javax.inject.Inject + +@Reusable +internal class ExpressionDimensionItemModuleWiper @Inject constructor( + private val tableWiper: TableWiper +) : ModuleWiper { + override fun wipeMetadata() { + tableWiper.wipeTable(ExpressionDimensionItemTableInfo.TABLE_INFO) + } + + override fun wipeData() { + // No data to wipe + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/DataDimensionItemProgramDataElementWithUidColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemPackageDIModule.kt similarity index 74% rename from core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/DataDimensionItemProgramDataElementWithUidColumnAdapter.java rename to core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemPackageDIModule.kt index 30e61b722e..004d8cceaa 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/identifiable/internal/DataDimensionItemProgramDataElementWithUidColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemPackageDIModule.kt @@ -25,18 +25,13 @@ * (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.android.core.expressiondimensionitem.internal -package org.hisp.dhis.android.core.arch.db.adapters.identifiable.internal; +import dagger.Module -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.visualization.DataDimensionItemProgramDataElement; - -public class DataDimensionItemProgramDataElementWithUidColumnAdapter - extends IdentifiableObjectColumnAdapter { - - @Override - protected DataDimensionItemProgramDataElement build(@NonNull String uid) { - return DataDimensionItemProgramDataElement.builder().uid(uid).build(); - } -} \ No newline at end of file +@Module( + includes = [ + ExpressionDimensionItemEntityDIModule::class + ] +) +internal class ExpressionDimensionItemPackageDIModule \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemUidsSeeker.kt similarity index 65% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemEntityDIModule.kt rename to core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemUidsSeeker.kt index 761c164547..386137da58 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemUidsSeeker.kt @@ -25,30 +25,27 @@ * (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.android.core.visualization.internal -import dagger.Module -import dagger.Provides +package org.hisp.dhis.android.core.expressiondimensionitem.internal + import dagger.Reusable import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter -import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore -import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandler -import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandlerImpl -import org.hisp.dhis.android.core.visualization.DataDimensionItem +import org.hisp.dhis.android.core.arch.db.uidseeker.internal.BaseUidsSeeker +import org.hisp.dhis.android.core.visualization.DimensionItemType +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo +import javax.inject.Inject -@Module -internal class DataDimensionItemEntityDIModule { +@Reusable +internal class ExpressionDimensionItemUidsSeeker @Inject constructor( + databaseAdapter: DatabaseAdapter +) : BaseUidsSeeker(databaseAdapter) { - @Provides - @Reusable - fun store(databaseAdapter: DatabaseAdapter): LinkStore { - return DataDimensionItemStore.create(databaseAdapter) - } + fun seekUids(): Set { + val query = "SELECT ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM} " + + "FROM ${VisualizationDimensionItemTableInfo.TABLE_INFO.name()} " + + "WHERE ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM_TYPE} = " + + "'${DimensionItemType.EXPRESSION_DIMENSION_ITEM.name}'" - @Provides - @Reusable - fun handler(store: LinkStore): - LinkHandler { - return LinkHandlerImpl(store) + return readSingleColumnResults(query) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt index dbc7da9cfa..7bac1c8ade 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt @@ -28,38 +28,34 @@ package org.hisp.dhis.android.core.indicator.internal import dagger.Reusable -import java.util.* -import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.querybuilders.internal.MultipleTableQueryBuilder +import org.hisp.dhis.android.core.arch.db.uidseeker.internal.BaseUidsSeeker import org.hisp.dhis.android.core.dataset.SectionIndicatorLinkTableInfo import org.hisp.dhis.android.core.indicator.DataSetIndicatorLinkTableInfo -import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo +import org.hisp.dhis.android.core.visualization.DimensionItemType +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo +import javax.inject.Inject @Reusable -internal class IndicatorUidsSeeker @Inject constructor(private val databaseAdapter: DatabaseAdapter) { +internal class IndicatorUidsSeeker @Inject constructor( + databaseAdapter: DatabaseAdapter +) : BaseUidsSeeker(databaseAdapter) { fun seekUids(): Set { - val tableNames = Arrays.asList( + val tableNames = listOf( DataSetIndicatorLinkTableInfo.TABLE_INFO.name(), - SectionIndicatorLinkTableInfo.TABLE_INFO.name(), - DataDimensionItemTableInfo.TABLE_INFO.name() + SectionIndicatorLinkTableInfo.TABLE_INFO.name() ) - val query = MultipleTableQueryBuilder() + val tablesQuery = MultipleTableQueryBuilder() .generateQuery(DataSetIndicatorLinkTableInfo.Columns.INDICATOR, tableNames).build() - val cursor = databaseAdapter.rawQuery(query) + val visualizationQuery = "SELECT ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM} " + + "FROM ${VisualizationDimensionItemTableInfo.TABLE_INFO.name()} " + + "WHERE ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM_TYPE} = " + + "'${DimensionItemType.PROGRAM_INDICATOR.name}'" - val indicators: MutableSet = HashSet() - cursor.use { c -> - if (c.count > 0) { - c.moveToFirst() - do { - indicators.add(c.getString(0)) - } while (c.moveToNext()) - } - } - return indicators + return readSingleColumnResults(tablesQuery) + readSingleColumnResults(visualizationQuery) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/legendset/internal/LegendSetUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/legendset/internal/LegendSetUidsSeeker.kt index d3c675e33c..28e3114e44 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/legendset/internal/LegendSetUidsSeeker.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/legendset/internal/LegendSetUidsSeeker.kt @@ -32,6 +32,7 @@ import dagger.Reusable import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.querybuilders.internal.MultipleTableQueryBuilder +import org.hisp.dhis.android.core.arch.db.uidseeker.internal.BaseUidsSeeker import org.hisp.dhis.android.core.indicator.IndicatorLegendSetLinkTableInfo import org.hisp.dhis.android.core.legendset.DataElementLegendSetLinkTableInfo import org.hisp.dhis.android.core.legendset.ProgramIndicatorLegendSetLinkTableInfo @@ -39,7 +40,9 @@ import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeLegendSetL import org.hisp.dhis.android.core.visualization.VisualizationTableInfo @Reusable -internal class LegendSetUidsSeeker @Inject constructor(private val databaseAdapter: DatabaseAdapter) { +internal class LegendSetUidsSeeker @Inject constructor( + databaseAdapter: DatabaseAdapter +) : BaseUidsSeeker(databaseAdapter) { fun seekUids(): Set { val tableNames = listOf( @@ -57,26 +60,6 @@ internal class LegendSetUidsSeeker @Inject constructor(private val databaseAdapt .generateQuery(legendSetIdColumnName, listOf(VisualizationTableInfo.TABLE_INFO.name())) .build() - val cursor = databaseAdapter.rawQuery(query) - val legendSets = hashSetOf() - cursor.use { mCursor -> - if (mCursor.count > 0) { - mCursor.moveToFirst() - do { - legendSets.add(mCursor.getString(0)) - } while (mCursor.moveToNext()) - } - } - - val visualisationCursor = databaseAdapter.rawQuery(visualisationLegendSetQuery) - visualisationCursor.use { mCursor -> - if (mCursor.count > 0) { - mCursor.moveToFirst() - do { - legendSets.add(mCursor.getString(0)) - } while (mCursor.moveToNext()) - } - } - return legendSets + return readSingleColumnResults(query) + readSingleColumnResults(visualisationLegendSetQuery) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java b/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java index f4b3f8d81a..01edb89444 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java +++ b/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java @@ -99,6 +99,7 @@ public class Dhis2MockServer { private static final String EVENTS_JSON = "event/events.json"; private static final String NEW_EVENTS_JSON = "event/new_tracker_importer_events.json"; private static final String LEGEND_SETS_JSON = "legendset/legend_sets.json"; + private static final String EXPRESSION_DIMENSION_ITEMS = "expressiondimensionitem/expression_dimension_items.json"; private static final String TRACKED_ENTITY_INSTANCES_JSON = "trackedentity/tracked_entity_instances.json"; private static final String NEW_TRACKED_ENTITY_INSTANCES_JSON = "trackedentity/new_tracker_importer_tracked_entities.json"; @@ -283,6 +284,8 @@ public MockResponse dispatch(RecordedRequest request) { return createMockResponse(DATA_APPROVALS_MULTIPLE_JSON); } else if (path.startsWith("/api/legendSets?")) { return createMockResponse(LEGEND_SETS_JSON); + } else if (path.startsWith("/api/expressionDimensionItems?")) { + return createMockResponse(EXPRESSION_DIMENSION_ITEMS); } else if (path.startsWith("/api/trackedEntityAttributes/aejWyOfXge6/generateAndReserve")) { return createMockResponse(RESERVE_VALUES_JSON); } else if (path.startsWith("/api/metadata")) { @@ -362,6 +365,7 @@ public void enqueueMetadataResponses() { enqueueMockResponse(INDICATORS_JSON); enqueueMockResponse(INDICATOR_TYPES_JSON); enqueueMockResponse(LEGEND_SETS_JSON); + enqueueMockResponse(EXPRESSION_DIMENSION_ITEMS); } private MockResponse createMockResponse(String fileName) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramIndicatorUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramIndicatorUidsSeeker.kt index be56b51a27..4316aaf409 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramIndicatorUidsSeeker.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramIndicatorUidsSeeker.kt @@ -29,32 +29,24 @@ package org.hisp.dhis.android.core.program.internal import dagger.Reusable -import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.querybuilders.internal.MultipleTableQueryBuilder -import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo +import org.hisp.dhis.android.core.arch.db.uidseeker.internal.BaseUidsSeeker +import org.hisp.dhis.android.core.visualization.DimensionItemType +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo +import javax.inject.Inject @Reusable internal class ProgramIndicatorUidsSeeker @Inject constructor( - private val databaseAdapter: DatabaseAdapter -) { + databaseAdapter: DatabaseAdapter +) : BaseUidsSeeker(databaseAdapter) { fun seekUids(): Set { - val tableName = listOf(DataDimensionItemTableInfo.TABLE_INFO.name()) - val query = MultipleTableQueryBuilder() - .generateQuery(DataDimensionItemTableInfo.Columns.PROGRAM_INDICATOR, tableName) - .build() + val query = "SELECT ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM} " + + "FROM ${VisualizationDimensionItemTableInfo.TABLE_INFO.name()} " + + "WHERE ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM_TYPE} = " + + "'${DimensionItemType.PROGRAM_INDICATOR.name}'" - val cursor = databaseAdapter.rawQuery(query) - val programIndicators = hashSetOf() - cursor.use { mCursor -> - if (mCursor.count > 0) { - mCursor.moveToFirst() - do { - programIndicators.add(cursor.getString(0)) - } while (mCursor.moveToNext()) - } - } - return programIndicators + return readSingleColumnResults(query) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItem.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItem.java deleted file mode 100644 index 210dafa1f8..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItem.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization; - -import android.database.Cursor; - -import androidx.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import com.gabrielittner.auto.value.cursor.ColumnAdapter; -import com.google.auto.value.AutoValue; - -import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.DataDimensionItemTypeColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.identifiable.internal.DataDimensionItemProgramAttributeWithUidColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.identifiable.internal.DataDimensionItemProgramDataElementWithUidColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.identifiable.internal.ObjectWithUidColumnAdapter; -import org.hisp.dhis.android.core.common.CoreObject; -import org.hisp.dhis.android.core.common.ObjectWithUid; -import org.hisp.dhis.android.core.common.ObjectWithUidInterface; - -@AutoValue -@JsonDeserialize(builder = $$AutoValue_DataDimensionItem.Builder.class) -public abstract class DataDimensionItem implements CoreObject { - - @Nullable - public abstract String visualization(); - - @Nullable - @JsonProperty() - @ColumnAdapter(DataDimensionItemTypeColumnAdapter.class) - public abstract DataDimensionItemType dataDimensionItemType(); - - @Nullable - @JsonProperty() - @ColumnAdapter(ObjectWithUidColumnAdapter.class) - public abstract ObjectWithUid indicator(); - - @Nullable - @JsonProperty() - @ColumnAdapter(ObjectWithUidColumnAdapter.class) - public abstract ObjectWithUid dataElement(); - - @Nullable - @JsonProperty() - @ColumnAdapter(ObjectWithUidColumnAdapter.class) - public abstract ObjectWithUid dataElementOperand(); - - @Nullable - @JsonProperty() - @ColumnAdapter(ObjectWithUidColumnAdapter.class) - public abstract ObjectWithUid reportingRate(); - - @Nullable - @JsonProperty() - @ColumnAdapter(ObjectWithUidColumnAdapter.class) - public abstract ObjectWithUid programIndicator(); - - @Nullable - @JsonProperty() - @ColumnAdapter(DataDimensionItemProgramDataElementWithUidColumnAdapter.class) - public abstract DataDimensionItemProgramDataElement programDataElement(); - - @Nullable - @JsonProperty() - @ColumnAdapter(DataDimensionItemProgramAttributeWithUidColumnAdapter.class) - public abstract DataDimensionItemProgramAttribute programAttribute(); - - @Nullable - @JsonProperty() - @ColumnAdapter(ObjectWithUidColumnAdapter.class) - public abstract ObjectWithUid validationRule(); - - @Nullable - public String dataDimensionItem() { - ObjectWithUidInterface item = dataDimensionItemObject(); - if (item == null) { - return null; - } else { - return item.uid(); - } - } - - @Nullable - private ObjectWithUidInterface dataDimensionItemObject() { - DataDimensionItemType type = dataDimensionItemType(); - if (type == null) { - return null; - } else { - switch (type) { - case INDICATOR: - return indicator(); - case DATA_ELEMENT: - return dataElement(); - case PROGRAM_ATTRIBUTE: - return programAttribute(); - case PROGRAM_DATA_ELEMENT: - return programDataElement(); - case PROGRAM_INDICATOR: - return programIndicator(); - case REPORTING_RATE: - return reportingRate(); - case DATA_ELEMENT_OPERAND: - return dataElementOperand(); - case VALIDATION_RULE: - return validationRule(); - default: - return null; - } - } - } - - public static Builder builder() { - return new $$AutoValue_DataDimensionItem.Builder(); - } - - public static DataDimensionItem create(Cursor cursor) { - return AutoValue_DataDimensionItem.createFromCursor(cursor); - } - - public abstract Builder toBuilder(); - - @AutoValue.Builder - @JsonPOJOBuilder(withPrefix = "") - public static abstract class Builder { - - public abstract Builder id(Long id); - - public abstract Builder visualization(String visualization); - - public abstract Builder dataDimensionItemType(DataDimensionItemType dataDimensionItemType); - - public abstract Builder indicator(ObjectWithUid indicator); - - public abstract Builder dataElement(ObjectWithUid dataElement); - - public abstract Builder dataElementOperand(ObjectWithUid dataElementOperand); - - public abstract Builder reportingRate(ObjectWithUid reportingRate); - - public abstract Builder programIndicator(ObjectWithUid programIndicator); - - public abstract Builder programDataElement(DataDimensionItemProgramDataElement programDataElement); - - public abstract Builder programAttribute(DataDimensionItemProgramAttribute programAttribute); - - public abstract Builder validationRule(ObjectWithUid validationRule); - - public abstract DataDimensionItem build(); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramDataElement.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramDataElement.java deleted file mode 100644 index e6c2a8cafa..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramDataElement.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization; - -import androidx.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import com.gabrielittner.auto.value.cursor.ColumnAdapter; -import com.google.auto.value.AutoValue; - -import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreObjectWithUidColumnAdapter; -import org.hisp.dhis.android.core.common.ObjectWithUid; -import org.hisp.dhis.android.core.common.ObjectWithUidInterface; -import org.hisp.dhis.android.core.visualization.internal.DataDimensionItemProgramDataElementFields; - -@AutoValue -@JsonDeserialize(builder = AutoValue_DataDimensionItemProgramDataElement.Builder.class) -public abstract class DataDimensionItemProgramDataElement implements ObjectWithUidInterface { - - @Nullable - @JsonProperty(DataDimensionItemProgramDataElementFields.DIMENSION_ITEM) - public abstract String uid(); - - @Nullable - @JsonIgnore() - @ColumnAdapter(IgnoreObjectWithUidColumnAdapter.class) - public ObjectWithUid program() { - return uid() == null ? null : getTokenAt(0); - } - - @Nullable - @JsonIgnore() - @ColumnAdapter(IgnoreObjectWithUidColumnAdapter.class) - public ObjectWithUid dataElement() { - return uid() == null ? null : getTokenAt(1); - } - - public static Builder builder() { - return new AutoValue_DataDimensionItemProgramDataElement.Builder(); - } - - public abstract Builder toBuilder(); - - private ObjectWithUid getTokenAt(int position) { - String[] tokens = uid() == null ? new String[] {} : uid().split("\\."); - String uid = tokens.length > position ? tokens[position] : null; - return uid == null ? null : ObjectWithUid.create(uid); - } - - @AutoValue.Builder - @JsonPOJOBuilder(withPrefix = "") - public static abstract class Builder { - - @JsonProperty(DataDimensionItemProgramDataElementFields.DIMENSION_ITEM) - public abstract Builder uid(String uid); - - public abstract DataDimensionItemProgramDataElement build(); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemTableInfo.kt deleted file mode 100644 index daf2c5716c..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemTableInfo.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization - -import org.hisp.dhis.android.core.arch.db.tableinfos.TableInfo -import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper -import org.hisp.dhis.android.core.common.CoreColumns - -object DataDimensionItemTableInfo { - - @JvmField - val TABLE_INFO: TableInfo = object : TableInfo() { - override fun name(): String { - return "DataDimensionItem" - } - - override fun columns(): CoreColumns { - return Columns() - } - } - - class Columns : CoreColumns() { - override fun all(): Array { - return CollectionsHelper.appendInNewArray( - super.all(), - VISUALIZATION, - DATA_DIMENSION_ITEM_TYPE, - INDICATOR, - DATA_ELEMENT, - DATA_ELEMENT_OPERAND, - REPORTING_RATE, - PROGRAM_INDICATOR, - PROGRAM_DATA_ELEMENT, - PROGRAM_ATTRIBUTE, - VALIDATION_RULE - ) - } - - override fun whereUpdate(): Array { - return CollectionsHelper.appendInNewArray( - super.all(), - VISUALIZATION, - DATA_DIMENSION_ITEM_TYPE, - INDICATOR, - DATA_ELEMENT, - DATA_ELEMENT_OPERAND, - REPORTING_RATE, - PROGRAM_INDICATOR, - PROGRAM_DATA_ELEMENT, - PROGRAM_ATTRIBUTE, - VALIDATION_RULE - ) - } - - companion object { - const val VISUALIZATION = "visualization" - const val DATA_DIMENSION_ITEM_TYPE = "dataDimensionItemType" - const val INDICATOR = "indicator" - const val DATA_ELEMENT = "dataElement" - const val DATA_ELEMENT_OPERAND = "dataElementOperand" - const val REPORTING_RATE = "reportingRate" - const val PROGRAM_INDICATOR = "programIndicator" - const val PROGRAM_DATA_ELEMENT = "programDataElement" - const val PROGRAM_ATTRIBUTE = "programAttribute" - const val VALIDATION_RULE = "validationRule" - } - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DimensionItemType.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/DimensionItemType.kt new file mode 100644 index 0000000000..2df68d6df0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/DimensionItemType.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.visualization + +enum class DimensionItemType { + INDICATOR, + DATA_ELEMENT, + DATA_ELEMENT_OPERAND, + REPORTING_RATE, + PROGRAM_INDICATOR, + PROGRAM_DATA_ELEMENT, + PROGRAM_ATTRIBUTE, + EXPRESSION_DIMENSION_ITEM, + VALIDATION_RULE, + PERIOD, + ORGANISATION_UNIT, + CATEGORY_OPTION +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/LayoutPosition.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/LayoutPosition.kt new file mode 100644 index 0000000000..7b58d17942 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/LayoutPosition.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.visualization + +enum class LayoutPosition { + COLUMN, + ROW, + FILTER +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java index ceff40bf34..7cef61d14b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java @@ -39,29 +39,20 @@ import com.google.auto.value.AutoValue; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbVisualizationLegendColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.IntegerListColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.ObjectWithUidListColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.RelativePeriodsColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.StringListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.AggregationTypeColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.DigitGroupSeparatorColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.DisplayDensityColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.HideEmptyItemStrategyColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.VisualizationTypeColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreCategoryDimensionListColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreDataDimensionItemListColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreVisualizationDimensionListColumnAdapter; import org.hisp.dhis.android.core.common.AggregationType; import org.hisp.dhis.android.core.common.BaseIdentifiableObject; import org.hisp.dhis.android.core.common.CoreObject; -import org.hisp.dhis.android.core.common.ObjectWithUid; -import org.hisp.dhis.android.core.common.RelativePeriod; import java.util.List; -import java.util.Map; @AutoValue @JsonDeserialize(builder = $$AutoValue_Visualization.Builder.class) -@SuppressWarnings({"PMD.ExcessivePublicCount", "PMD.CouplingBetweenObjects"}) public abstract class Visualization extends BaseIdentifiableObject implements CoreObject { @Nullable @@ -175,65 +166,23 @@ public abstract class Visualization extends BaseIdentifiableObject implements Co @Nullable @JsonProperty() - @ColumnAdapter(RelativePeriodsColumnAdapter.class) - public abstract Map relativePeriods(); - - @Nullable - @JsonProperty() - @ColumnAdapter(IgnoreCategoryDimensionListColumnAdapter.class) - public abstract List categoryDimensions(); - - @Nullable - @JsonProperty() - @ColumnAdapter(StringListColumnAdapter.class) - public abstract List filterDimensions(); - - @Nullable - @JsonProperty() - @ColumnAdapter(StringListColumnAdapter.class) - public abstract List rowDimensions(); - - @Nullable - @JsonProperty() - @ColumnAdapter(StringListColumnAdapter.class) - public abstract List columnDimensions(); - - @Nullable - @JsonProperty() - @ColumnAdapter(IgnoreDataDimensionItemListColumnAdapter.class) - public abstract List dataDimensionItems(); - - @Nullable - @JsonProperty() - @ColumnAdapter(IntegerListColumnAdapter.class) - public abstract List organisationUnitLevels(); - - @Nullable - @JsonProperty() - public abstract Boolean userOrganisationUnit(); - - @Nullable - @JsonProperty() - public abstract Boolean userOrganisationUnitChildren(); - - @Nullable - @JsonProperty() - public abstract Boolean userOrganisationUnitGrandChildren(); + @ColumnAdapter(AggregationTypeColumnAdapter.class) + public abstract AggregationType aggregationType(); @Nullable @JsonProperty() - @ColumnAdapter(ObjectWithUidListColumnAdapter.class) - public abstract List organisationUnits(); + @ColumnAdapter(IgnoreVisualizationDimensionListColumnAdapter.class) + public abstract List columns(); @Nullable @JsonProperty() - @ColumnAdapter(ObjectWithUidListColumnAdapter.class) - public abstract List periods(); + @ColumnAdapter(IgnoreVisualizationDimensionListColumnAdapter.class) + public abstract List rows(); @Nullable @JsonProperty() - @ColumnAdapter(AggregationTypeColumnAdapter.class) - public abstract AggregationType aggregationType(); + @ColumnAdapter(IgnoreVisualizationDimensionListColumnAdapter.class) + public abstract List filters(); public static Builder builder() { return new $$AutoValue_Visualization.Builder(); @@ -303,31 +252,13 @@ public static abstract class Builder extends BaseIdentifiableObject.Builder relativePeriods); - - public abstract Builder categoryDimensions(List categoryDimensions); - - public abstract Builder filterDimensions(List filterDimensions); - - public abstract Builder rowDimensions(List rowDimensions); - - public abstract Builder columnDimensions(List columnDimensions); - - public abstract Builder dataDimensionItems(List dataDimensionItems); - - public abstract Builder organisationUnitLevels(List organisationUnitLevels); - - public abstract Builder userOrganisationUnit(Boolean userOrganisationUnit); - - public abstract Builder userOrganisationUnitChildren(Boolean userOrganisationUnitChildren); - - public abstract Builder userOrganisationUnitGrandChildren(Boolean userOrganisationUnitGrandChildren); + public abstract Builder aggregationType(AggregationType aggregationType); - public abstract Builder organisationUnits(List organisationUnits); + public abstract Builder columns(List columns); - public abstract Builder periods(List periods); + public abstract Builder rows(List rows); - public abstract Builder aggregationType(AggregationType aggregationType); + public abstract Builder filters(List filters); public abstract Visualization build(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationAPI36.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationAPI36.kt index 706795952d..f6b50e8701 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationAPI36.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationAPI36.kt @@ -31,7 +31,6 @@ package org.hisp.dhis.android.core.visualization import java.util.* import org.hisp.dhis.android.core.common.AggregationType import org.hisp.dhis.android.core.common.ObjectWithUid -import org.hisp.dhis.android.core.common.RelativePeriod internal data class VisualizationAPI36( val id: String, @@ -66,22 +65,13 @@ internal data class VisualizationAPI36( val skipRounding: Boolean?, val displayDensity: DisplayDensity?, val digitGroupSeparator: DigitGroupSeparator?, - val relativePeriods: Map?, - val categoryDimensions: List?, - val filterDimensions: List?, - val rowDimensions: List?, - val columnDimensions: List?, - val dataDimensionItems: List?, - val organisationUnitLevels: List?, - val userOrganisationUnit: Boolean?, - val userOrganisationUnitChildren: Boolean?, - val userOrganisationUnitGrandChildren: Boolean?, - val organisationUnits: List?, - val periods: List?, val legendSet: ObjectWithUid?, val legendDisplayStyle: LegendStyle?, val legendDisplayStrategy: LegendStrategy?, - val aggregationType: AggregationType? + val aggregationType: AggregationType?, + val columns: List, + val rows: List, + val filters: List, ) { fun toVisualization(): Visualization = Visualization.builder() @@ -117,18 +107,6 @@ internal data class VisualizationAPI36( .skipRounding(skipRounding) .displayDensity(displayDensity) .digitGroupSeparator(digitGroupSeparator) - .relativePeriods(relativePeriods) - .categoryDimensions(categoryDimensions) - .filterDimensions(filterDimensions) - .rowDimensions(rowDimensions) - .columnDimensions(columnDimensions) - .dataDimensionItems(dataDimensionItems) - .organisationUnitLevels(organisationUnitLevels) - .userOrganisationUnit(userOrganisationUnit) - .userOrganisationUnitChildren(userOrganisationUnitChildren) - .userOrganisationUnitGrandChildren(userOrganisationUnitGrandChildren) - .organisationUnits(organisationUnits) - .periods(periods) .aggregationType(aggregationType) .legend( VisualizationLegend.builder() @@ -138,5 +116,8 @@ internal data class VisualizationAPI36( .showKey(false) .build() ) + .columns(columns) + .rows(rows) + .filters(filters) .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCollectionRepository.java index 95e8a0ca3d..436429dc44 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCollectionRepository.java @@ -33,7 +33,6 @@ import org.hisp.dhis.android.core.arch.repositories.filters.internal.BooleanFilterConnector; import org.hisp.dhis.android.core.arch.repositories.filters.internal.EnumFilterConnector; import org.hisp.dhis.android.core.arch.repositories.filters.internal.FilterConnectorFactory; -import org.hisp.dhis.android.core.arch.repositories.filters.internal.IntegerFilterConnector; import org.hisp.dhis.android.core.arch.repositories.filters.internal.StringFilterConnector; import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope; import org.hisp.dhis.android.core.visualization.VisualizationTableInfo.Columns; @@ -173,43 +172,7 @@ public EnumFilterConnector byRelativePeriods() { - return cf.string(Columns.RELATIVE_PERIODS); - } - - public StringFilterConnector byFilterDimensions() { - return cf.string(Columns.FILTER_DIMENSIONS); - } - - public StringFilterConnector byRowDimensions() { - return cf.string(Columns.ROW_DIMENSIONS); - } - - public StringFilterConnector byColumnDimensions() { - return cf.string(Columns.COLUMN_DIMENSIONS); - } - - public IntegerFilterConnector byOrganisationUnitLevels() { - return cf.integer(Columns.ORGANISATION_UNIT_LEVELS); - } - - public BooleanFilterConnector byUserOrganisationUnit() { - return cf.bool(Columns.USER_ORGANISATION_UNIT); - } - - public BooleanFilterConnector byUserOrganisationUnitChildren() { - return cf.bool(Columns.USER_ORGANISATION_UNIT_CHILDREN); - } - - public BooleanFilterConnector byUserOrganisationUnitGrandChildren() { - return cf.bool(Columns.USER_ORGANISATION_UNIT_GRAND_CHILDREN); - } - - public VisualizationCollectionRepository withCategoryDimensions() { - return cf.withChild(VisualizationFields.CATEGORY_DIMENSIONS); - } - - public VisualizationCollectionRepository withDataDimensionItems() { - return cf.withChild(VisualizationFields.DATA_DIMENSION_ITEMS); + public VisualizationCollectionRepository withColumnsRowsAndFilters() { + return cf.withChild(VisualizationFields.ITEMS); } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimension.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimension.java new file mode 100644 index 0000000000..9cc2cd27d0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimension.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.visualization; + +import androidx.annotation.Nullable; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.google.auto.value.AutoValue; + +import java.util.List; + +@AutoValue +@JsonDeserialize(builder = AutoValue_VisualizationDimension.Builder.class) +public abstract class VisualizationDimension { + + @Nullable + @JsonProperty() + public abstract String id(); + + @Nullable + @JsonProperty() + public abstract List items(); + + public static Builder builder() { + return new AutoValue_VisualizationDimension.Builder(); + } + + public abstract Builder toBuilder(); + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public static abstract class Builder { + + public abstract Builder id(String id); + + public abstract Builder items(List items); + + public abstract VisualizationDimension build(); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramAttribute.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItem.java similarity index 59% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramAttribute.java rename to core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItem.java index da8f2b23fe..45b0f89e2b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramAttribute.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItem.java @@ -28,61 +28,68 @@ package org.hisp.dhis.android.core.visualization; +import android.database.Cursor; + import androidx.annotation.Nullable; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.gabrielittner.auto.value.cursor.ColumnAdapter; import com.google.auto.value.AutoValue; -import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreObjectWithUidColumnAdapter; -import org.hisp.dhis.android.core.common.ObjectWithUid; -import org.hisp.dhis.android.core.common.ObjectWithUidInterface; -import org.hisp.dhis.android.core.visualization.internal.DataDimensionItemProgramAttributeFields; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.LayoutPositionColumnAdapter; +import org.hisp.dhis.android.core.common.CoreObject; @AutoValue -@JsonDeserialize(builder = AutoValue_DataDimensionItemProgramAttribute.Builder.class) -public abstract class DataDimensionItemProgramAttribute implements ObjectWithUidInterface { +@JsonDeserialize(builder = $$AutoValue_VisualizationDimensionItem.Builder.class) +public abstract class VisualizationDimensionItem implements CoreObject { @Nullable - @JsonProperty(DataDimensionItemProgramAttributeFields.DIMENSION_ITEM) - public abstract String uid(); + public abstract String visualization(); @Nullable - @JsonIgnore() - @ColumnAdapter(IgnoreObjectWithUidColumnAdapter.class) - public ObjectWithUid program() { - return uid() == null ? null : getTokenAt(0); - } + @ColumnAdapter(LayoutPositionColumnAdapter.class) + public abstract LayoutPosition position(); @Nullable - @JsonIgnore() - @ColumnAdapter(IgnoreObjectWithUidColumnAdapter.class) - public ObjectWithUid attribute() { - return uid() == null ? null : getTokenAt(1); - } + public abstract String dimension(); + + @Nullable + @JsonProperty() + public abstract String dimensionItem(); + + @Nullable + @JsonProperty() + public abstract String dimensionItemType(); + public static Builder builder() { - return new AutoValue_DataDimensionItemProgramAttribute.Builder(); + return new $$AutoValue_VisualizationDimensionItem.Builder(); } - public abstract Builder toBuilder(); - - private ObjectWithUid getTokenAt(int position) { - String[] tokens = uid() == null ? new String[] {} : uid().split("\\."); - String uid = tokens.length > position ? tokens[position] : null; - return uid == null ? null : ObjectWithUid.create(uid); + public static VisualizationDimensionItem create(Cursor cursor) { + return AutoValue_VisualizationDimensionItem.createFromCursor(cursor); } + public abstract Builder toBuilder(); + @AutoValue.Builder @JsonPOJOBuilder(withPrefix = "") public static abstract class Builder { - @JsonProperty(DataDimensionItemProgramAttributeFields.DIMENSION_ITEM) - public abstract Builder uid(String uid); + public abstract Builder id(Long id); + + public abstract Builder visualization(String visualization); + + public abstract Builder position(LayoutPosition position); + + public abstract Builder dimension(String dimension); + + public abstract Builder dimensionItem(String dimensionItem); + + public abstract Builder dimensionItemType(String dimensionItemType); - public abstract DataDimensionItemProgramAttribute build(); + public abstract VisualizationDimensionItem build(); } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCategoryDimensionLinkTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItemTableInfo.kt similarity index 79% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCategoryDimensionLinkTableInfo.kt rename to core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItemTableInfo.kt index 6dc2a2fbdc..2c1755f282 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCategoryDimensionLinkTableInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItemTableInfo.kt @@ -32,12 +32,12 @@ import org.hisp.dhis.android.core.arch.db.tableinfos.TableInfo import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper import org.hisp.dhis.android.core.common.CoreColumns -object VisualizationCategoryDimensionLinkTableInfo { +object VisualizationDimensionItemTableInfo { @JvmField val TABLE_INFO: TableInfo = object : TableInfo() { override fun name(): String { - return "VisualizationCategoryDimensionLink" + return "VisualizationDimensionItem" } override fun columns(): CoreColumns { @@ -49,25 +49,30 @@ object VisualizationCategoryDimensionLinkTableInfo { override fun all(): Array { return CollectionsHelper.appendInNewArray( super.all(), - VISUALIZATION, - CATEGORY, - CATEGORY_OPTION - ) + VISUALIZATION, + POSITION, + DIMENSION, + DIMENSION_ITEM, + DIMENSION_ITEM_TYPE, ) } override fun whereUpdate(): Array { return CollectionsHelper.appendInNewArray( super.all(), VISUALIZATION, - CATEGORY, - CATEGORY_OPTION + POSITION, + DIMENSION, + DIMENSION_ITEM, + DIMENSION_ITEM_TYPE, ) } companion object { const val VISUALIZATION = "visualization" - const val CATEGORY = "category" - const val CATEGORY_OPTION = "categoryOption" + const val POSITION = "position" + const val DIMENSION = "dimension" + const val DIMENSION_ITEM = "dimensionItem" + const val DIMENSION_ITEM_TYPE = "dimensionItemType" } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationTableInfo.kt index da137d8932..722f7f8798 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationTableInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationTableInfo.kt @@ -75,16 +75,6 @@ object VisualizationTableInfo { SKIP_ROUNDING, DISPLAY_DENSITY, DIGIT_GROUP_SEPARATOR, - RELATIVE_PERIODS, - FILTER_DIMENSIONS, - ROW_DIMENSIONS, - COLUMN_DIMENSIONS, - ORGANISATION_UNIT_LEVELS, - USER_ORGANISATION_UNIT, - USER_ORGANISATION_UNIT_CHILDREN, - USER_ORGANISATION_UNIT_GRAND_CHILDREN, - ORGANISATION_UNITS, - PERIODS, LEGEND_SHOW_KEY, LEGEND_STYLE, LEGEND_SET_ID, @@ -119,16 +109,6 @@ object VisualizationTableInfo { const val SKIP_ROUNDING = "skipRounding" const val DISPLAY_DENSITY = "displayDensity" const val DIGIT_GROUP_SEPARATOR = "digitGroupSeparator" - const val RELATIVE_PERIODS = "relativePeriods" - const val FILTER_DIMENSIONS = "filterDimensions" - const val ROW_DIMENSIONS = "rowDimensions" - const val COLUMN_DIMENSIONS = "columnDimensions" - const val ORGANISATION_UNIT_LEVELS = "organisationUnitLevels" - const val USER_ORGANISATION_UNIT = "userOrganisationUnit" - const val USER_ORGANISATION_UNIT_CHILDREN = "userOrganisationUnitChildren" - const val USER_ORGANISATION_UNIT_GRAND_CHILDREN = "userOrganisationUnitGrandChildren" - const val ORGANISATION_UNITS = "organisationUnits" - const val PERIODS = "periods" const val LEGEND_SHOW_KEY = "legendShowKey" const val LEGEND_STYLE = "legendStyle" const val LEGEND_SET_ID = "legendSetId" diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemFields.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemFields.kt deleted file mode 100644 index 1a025d6a80..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemFields.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization.internal - -import org.hisp.dhis.android.core.arch.api.fields.internal.Fields -import org.hisp.dhis.android.core.arch.fields.internal.FieldsHelper -import org.hisp.dhis.android.core.visualization.* - -internal object DataDimensionItemFields { - internal const val CATEGORY = "category" - internal const val CATEGORY_OPTIONS = "categoryOptions" - private val fh = FieldsHelper() - - val allFields: Fields = - Fields.builder() - .fields( - fh.field(DataDimensionItemTableInfo.Columns.DATA_DIMENSION_ITEM_TYPE), - fh.nestedFieldWithUid(DataDimensionItemTableInfo.Columns.INDICATOR), - fh.nestedFieldWithUid(DataDimensionItemTableInfo.Columns.DATA_ELEMENT), - fh.nestedFieldWithUid(DataDimensionItemTableInfo.Columns.DATA_ELEMENT_OPERAND), - fh.nestedFieldWithUid(DataDimensionItemTableInfo.Columns.REPORTING_RATE), - fh.nestedFieldWithUid(DataDimensionItemTableInfo.Columns.PROGRAM_INDICATOR), - fh.nestedField( - DataDimensionItemTableInfo.Columns.PROGRAM_DATA_ELEMENT - ).with(DataDimensionItemProgramDataElementFields.allFields), - fh.nestedField( - DataDimensionItemTableInfo.Columns.PROGRAM_ATTRIBUTE - ).with(DataDimensionItemProgramAttributeFields.allFields), - fh.nestedFieldWithUid(DataDimensionItemTableInfo.Columns.VALIDATION_RULE) - ) - .build() -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStore.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStore.kt deleted file mode 100644 index d5ef556eef..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStore.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization.internal - -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter -import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinder -import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapper -import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore -import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory -import org.hisp.dhis.android.core.arch.helpers.UidsHelper -import org.hisp.dhis.android.core.visualization.DataDimensionItem -import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo - -@Suppress("MagicNumber") -internal object DataDimensionItemStore { - private val BINDER = StatementBinder { o: DataDimensionItem, w: StatementWrapper -> - w.bind(1, o.visualization()) - w.bind(2, o.dataDimensionItemType()) - w.bind(3, UidsHelper.getUidOrNull(o.indicator())) - w.bind(4, UidsHelper.getUidOrNull(o.dataElement())) - w.bind(5, UidsHelper.getUidOrNull(o.dataElementOperand())) - w.bind(6, UidsHelper.getUidOrNull(o.reportingRate())) - w.bind(7, UidsHelper.getUidOrNull(o.programIndicator())) - w.bind(8, UidsHelper.getUidOrNull(o.programDataElement())) - w.bind(9, UidsHelper.getUidOrNull(o.programAttribute())) - w.bind(10, UidsHelper.getUidOrNull(o.validationRule())) - } - - fun create(databaseAdapter: DatabaseAdapter): LinkStore { - return StoreFactory.linkStore( - databaseAdapter, - DataDimensionItemTableInfo.TABLE_INFO, - DataDimensionItemTableInfo.Columns.VISUALIZATION, - BINDER - ) { DataDimensionItem.create(it) } - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionChildrenAppender.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionChildrenAppender.kt deleted file mode 100644 index 29f223d4c8..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionChildrenAppender.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.visualization.internal - -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder -import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore -import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender -import org.hisp.dhis.android.core.common.ObjectWithUid -import org.hisp.dhis.android.core.visualization.CategoryDimension -import org.hisp.dhis.android.core.visualization.Visualization -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLink -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLinkTableInfo - -internal class VisualizationCategoryDimensionChildrenAppender -private constructor(private val childStore: LinkStore) : - ChildrenAppender() { - - override fun appendChildren(visualization: Visualization): Visualization { - val builder = visualization.toBuilder() - builder.categoryDimensions(getChildren(visualization)) - return builder.build() - } - - private fun getChildren(o: Visualization): List { - val whereClause = WhereClauseBuilder() - .appendKeyStringValue(VisualizationCategoryDimensionLinkTableInfo.Columns.VISUALIZATION, o.uid()) - .build() - return this.childStore.selectWhere(whereClause) - .groupBy { it.category() } - .map { - CategoryDimension.builder() - .category(ObjectWithUid.create(it.key)) - .categoryOptions(it.value.mapNotNull { it.categoryOption()?.let { ObjectWithUid.create(it) } }) - .build() - } - } - - companion object { - fun create(databaseAdapter: DatabaseAdapter): ChildrenAppender { - return VisualizationCategoryDimensionChildrenAppender( - VisualizationCategoryDimensionLinkStore.create(databaseAdapter) - ) - } - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDataDimensionItemChildrenAppender.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationColumnsRowsFiltersChildrenAppender.kt similarity index 51% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDataDimensionItemChildrenAppender.kt rename to core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationColumnsRowsFiltersChildrenAppender.kt index b70e42cb7e..b71acb0ef2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDataDimensionItemChildrenAppender.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationColumnsRowsFiltersChildrenAppender.kt @@ -27,35 +27,55 @@ */ package org.hisp.dhis.android.core.visualization.internal +import android.database.Cursor import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder -import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore +import org.hisp.dhis.android.core.arch.db.stores.internal.SingleParentChildStore +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory.singleParentChildStore +import org.hisp.dhis.android.core.arch.db.stores.projections.internal.SingleParentChildProjection import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender -import org.hisp.dhis.android.core.visualization.DataDimensionItem -import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo +import org.hisp.dhis.android.core.visualization.LayoutPosition import org.hisp.dhis.android.core.visualization.Visualization +import org.hisp.dhis.android.core.visualization.VisualizationDimension +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo -internal class VisualizationDataDimensionItemChildrenAppender -private constructor(private val childStore: LinkStore) : - ChildrenAppender() { - +internal class VisualizationColumnsRowsFiltersChildrenAppender private constructor( + private val linkChildStore: SingleParentChildStore +) : ChildrenAppender() { override fun appendChildren(visualization: Visualization): Visualization { - val builder = visualization.toBuilder() - builder.dataDimensionItems(getChildren(visualization)) - return builder.build() - } + val items = linkChildStore.getChildren(visualization) + val groupedByPosition = items + .groupBy { it.position() } + .mapValues { (_, items) -> + items + .groupBy { it.dimension() } + .map { (dimension, items) -> + VisualizationDimension.builder() + .id(dimension) + .items(items) + .build() + } + } - private fun getChildren(o: Visualization): List { - val whereClause = WhereClauseBuilder() - .appendKeyStringValue(DataDimensionItemTableInfo.Columns.VISUALIZATION, o.uid()) + return visualization.toBuilder() + .columns(groupedByPosition[LayoutPosition.COLUMN] ?: emptyList()) + .rows(groupedByPosition[LayoutPosition.ROW] ?: emptyList()) + .filters(groupedByPosition[LayoutPosition.FILTER] ?: emptyList()) .build() - return this.childStore.selectWhere(whereClause) } companion object { + private val CHILD_PROJECTION = SingleParentChildProjection( + VisualizationDimensionItemTableInfo.TABLE_INFO, + VisualizationDimensionItemTableInfo.Columns.VISUALIZATION + ) + fun create(databaseAdapter: DatabaseAdapter): ChildrenAppender { - return VisualizationDataDimensionItemChildrenAppender( - DataDimensionItemStore.create(databaseAdapter) + return VisualizationColumnsRowsFiltersChildrenAppender( + singleParentChildStore( + databaseAdapter, + CHILD_PROJECTION + ) { cursor: Cursor -> VisualizationDimensionItem.create(cursor) } ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramDataElementFields.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionFields.kt similarity index 69% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramDataElementFields.kt rename to core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionFields.kt index b6a850596d..c3fda892d1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramDataElementFields.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionFields.kt @@ -29,17 +29,21 @@ package org.hisp.dhis.android.core.visualization.internal import org.hisp.dhis.android.core.arch.api.fields.internal.Fields import org.hisp.dhis.android.core.arch.fields.internal.FieldsHelper -import org.hisp.dhis.android.core.visualization.DataDimensionItemProgramDataElement +import org.hisp.dhis.android.core.common.BaseIdentifiableObject +import org.hisp.dhis.android.core.visualization.VisualizationDimension +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem -internal object DataDimensionItemProgramDataElementFields { - internal const val DIMENSION_ITEM = "dimensionItem" +internal object VisualizationDimensionFields { + private const val ITEMS = "items" - private val fh = FieldsHelper() + private val fh = FieldsHelper() - val allFields: Fields = - Fields.builder() - .fields( - fh.field(DIMENSION_ITEM) - ) - .build() + val allFields: Fields = + Fields.builder() + .fields( + fh.field(BaseIdentifiableObject.UID), + fh.nestedField(ITEMS) + .with(VisualizationDimensionItemFields.allFields), + ) + .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemEntityDIModule.kt similarity index 83% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionEntityDIModule.kt rename to core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemEntityDIModule.kt index b74814d466..65fe7a0581 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemEntityDIModule.kt @@ -34,21 +34,21 @@ import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandler import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandlerImpl -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLink +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem @Module -internal class VisualizationCategoryDimensionEntityDIModule { +internal class VisualizationDimensionItemEntityDIModule { @Provides @Reusable - fun store(databaseAdapter: DatabaseAdapter): LinkStore { - return VisualizationCategoryDimensionLinkStore.create(databaseAdapter) + fun store(databaseAdapter: DatabaseAdapter): LinkStore { + return VisualizationDimensionItemStore.create(databaseAdapter) } @Provides @Reusable - fun handler(store: LinkStore): - LinkHandler { + fun handler(store: LinkStore): + LinkHandler { return LinkHandlerImpl(store) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramAttributeFields.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemFields.kt similarity index 73% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramAttributeFields.kt rename to core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemFields.kt index 37b8b02ebd..369438947f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemProgramAttributeFields.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemFields.kt @@ -29,17 +29,19 @@ package org.hisp.dhis.android.core.visualization.internal import org.hisp.dhis.android.core.arch.api.fields.internal.Fields import org.hisp.dhis.android.core.arch.fields.internal.FieldsHelper -import org.hisp.dhis.android.core.visualization.DataDimensionItemProgramAttribute +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem -internal object DataDimensionItemProgramAttributeFields { - internal const val DIMENSION_ITEM = "dimensionItem" +internal object VisualizationDimensionItemFields { + private const val DIMENSION_ITEM = "dimensionItem" + private const val DIMENSION_ITEM_TYPE = "dimensionItemType" - private val fh = FieldsHelper() + private val fh = FieldsHelper() - val allFields: Fields = - Fields.builder() - .fields( - fh.field(DIMENSION_ITEM) - ) - .build() + val allFields: Fields = + Fields.builder() + .fields( + fh.field(DIMENSION_ITEM), + fh.field(DIMENSION_ITEM_TYPE), + ) + .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStore.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStore.kt similarity index 77% rename from core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStore.kt rename to core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStore.kt index 3751b07387..b53c529196 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStore.kt @@ -32,23 +32,25 @@ import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinde import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapper import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLink -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLinkTableInfo +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo @Suppress("MagicNumber") -internal object VisualizationCategoryDimensionLinkStore { - private val BINDER = StatementBinder { o: VisualizationCategoryDimensionLink, w: StatementWrapper -> +internal object VisualizationDimensionItemStore { + private val BINDER = StatementBinder { o: VisualizationDimensionItem, w: StatementWrapper -> w.bind(1, o.visualization()) - w.bind(2, o.category()) - w.bind(3, o.categoryOption()) + w.bind(2, o.position()) + w.bind(3, o.dimension()) + w.bind(4, o.dimensionItem()) + w.bind(5, o.dimensionItemType()) } - fun create(databaseAdapter: DatabaseAdapter): LinkStore { + fun create(databaseAdapter: DatabaseAdapter): LinkStore { return StoreFactory.linkStore( databaseAdapter, - VisualizationCategoryDimensionLinkTableInfo.TABLE_INFO, - VisualizationCategoryDimensionLinkTableInfo.Columns.VISUALIZATION, + VisualizationDimensionItemTableInfo.TABLE_INFO, + VisualizationDimensionItemTableInfo.Columns.VISUALIZATION, BINDER - ) { VisualizationCategoryDimensionLink.create(it) } + ) { VisualizationDimensionItem.create(it) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEntityDIModule.kt index f9359b8e66..75929c33d5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEntityDIModule.kt @@ -65,14 +65,7 @@ internal class VisualizationEntityDIModule : IdentifiableStoreProvider> { return mapOf( - Pair( - VisualizationFields.CATEGORY_DIMENSIONS, - VisualizationCategoryDimensionChildrenAppender.create(databaseAdapter) - ), - Pair( - VisualizationFields.DATA_DIMENSION_ITEMS, - VisualizationDataDimensionItemChildrenAppender.create(databaseAdapter) - ) + VisualizationFields.ITEMS to VisualizationColumnsRowsFiltersChildrenAppender.create(databaseAdapter) ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationFields.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationFields.kt index 0e6a0f761e..db9e653689 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationFields.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationFields.kt @@ -29,17 +29,28 @@ package org.hisp.dhis.android.core.visualization.internal import org.hisp.dhis.android.core.arch.api.fields.internal.Fields import org.hisp.dhis.android.core.arch.fields.internal.FieldsHelper -import org.hisp.dhis.android.core.visualization.* +import org.hisp.dhis.android.core.visualization.DigitGroupSeparator +import org.hisp.dhis.android.core.visualization.DisplayDensity +import org.hisp.dhis.android.core.visualization.HideEmptyItemStrategy +import org.hisp.dhis.android.core.visualization.Visualization +import org.hisp.dhis.android.core.visualization.VisualizationDimension +import org.hisp.dhis.android.core.visualization.VisualizationLegend +import org.hisp.dhis.android.core.visualization.VisualizationTableInfo +import org.hisp.dhis.android.core.visualization.VisualizationType internal object VisualizationFields { - internal const val CATEGORY_DIMENSIONS = "categoryDimensions" - internal const val DATA_DIMENSION_ITEMS = "dataDimensionItems" internal const val LEGEND = "legend" + private const val COLUMNS = "columns" + private const val ROWS = "rows" + private const val FILTERS = "filters" + private const val LEGEND_DISPLAY_STRATEGY = "legendDisplayStrategy" private const val LEGEND_DISPLAY_STYLE = "legendDisplayStyle" private const val LEGEND_SET = "legendSet" + internal const val ITEMS = "items" + private val fh = FieldsHelper() val uid = fh.uid() @@ -85,17 +96,8 @@ internal object VisualizationFields { fh.field(VisualizationTableInfo.Columns.SKIP_ROUNDING), fh.field(VisualizationTableInfo.Columns.DISPLAY_DENSITY), fh.field(VisualizationTableInfo.Columns.DIGIT_GROUP_SEPARATOR), - fh.field(VisualizationTableInfo.Columns.RELATIVE_PERIODS), - fh.nestedField(CATEGORY_DIMENSIONS).with(CategoryDimensionFields.allFields), - fh.field(VisualizationTableInfo.Columns.FILTER_DIMENSIONS), - fh.field(VisualizationTableInfo.Columns.ROW_DIMENSIONS), - fh.field(VisualizationTableInfo.Columns.COLUMN_DIMENSIONS), - fh.nestedField(DATA_DIMENSION_ITEMS).with(DataDimensionItemFields.allFields), - fh.field>(VisualizationTableInfo.Columns.ORGANISATION_UNIT_LEVELS), - fh.field(VisualizationTableInfo.Columns.USER_ORGANISATION_UNIT), - fh.field(VisualizationTableInfo.Columns.USER_ORGANISATION_UNIT_CHILDREN), - fh.field(VisualizationTableInfo.Columns.USER_ORGANISATION_UNIT_GRAND_CHILDREN), - fh.nestedFieldWithUid(VisualizationTableInfo.Columns.ORGANISATION_UNITS), - fh.nestedFieldWithUid(VisualizationTableInfo.Columns.PERIODS) + fh.nestedField(COLUMNS).with(VisualizationDimensionFields.allFields), + fh.nestedField(ROWS).with(VisualizationDimensionFields.allFields), + fh.nestedField(FILTERS).with(VisualizationDimensionFields.allFields), ) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt index acd5a3faa1..41ca3a3311 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt @@ -28,57 +28,31 @@ package org.hisp.dhis.android.core.visualization.internal import dagger.Reusable -import javax.inject.Inject import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore -import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandler -import org.hisp.dhis.android.core.visualization.CategoryDimension -import org.hisp.dhis.android.core.visualization.DataDimensionItem +import org.hisp.dhis.android.core.visualization.LayoutPosition import org.hisp.dhis.android.core.visualization.Visualization -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLink +import org.hisp.dhis.android.core.visualization.VisualizationDimension +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem +import javax.inject.Inject @Reusable internal class VisualizationHandler @Inject constructor( store: IdentifiableObjectStore, private val visualizationCollectionCleaner: CollectionCleaner, - private val visualizationCategoryDimensionLinkStore: LinkStore, - private val dataDimensionItemStore: LinkStore, - private val visualizationCategoryDimensionLinkHandler: - LinkHandler, - private val dataDimensionItemHandler: LinkHandler + private val itemHandler: LinkHandler ) : IdentifiableHandlerImpl(store) { - override fun beforeCollectionHandled( - oCollection: Collection - ): Collection { - visualizationCategoryDimensionLinkStore.delete() - dataDimensionItemStore.delete() - return oCollection - } - override fun afterObjectHandled(o: Visualization, action: HandleAction) { - val links = o.categoryDimensions()?.flatMap { categoryDimension: CategoryDimension -> - categoryDimension.category()?.let { category -> - val categoryOptions = - if (categoryDimension.categoryOptions().isNullOrEmpty()) listOf(null) - else categoryDimension.categoryOptions()!!.map { it.uid() } - - categoryOptions.map { - VisualizationCategoryDimensionLink.builder() - .visualization(o.uid()) - .category(category.uid()) - .categoryOption(it) - .build() - } - } ?: emptyList() - } - - visualizationCategoryDimensionLinkHandler.handleMany(o.uid(), links) { i -> i } + val items = + toItems(o.columns(), LayoutPosition.COLUMN) + + toItems(o.rows(), LayoutPosition.ROW) + + toItems(o.filters(), LayoutPosition.FILTER) - dataDimensionItemHandler.handleMany(o.uid(), o.dataDimensionItems()) { + itemHandler.handleMany(o.uid(), items) { it.toBuilder().visualization(o.uid()).build() } } @@ -86,4 +60,25 @@ internal class VisualizationHandler @Inject constructor( override fun afterCollectionHandled(oCollection: Collection?) { visualizationCollectionCleaner.deleteNotPresent(oCollection) } + + private fun toItems(dimensions: List?, + position: LayoutPosition): List { + return dimensions?.map { dimension -> + if (dimension.items().isNullOrEmpty()) { + listOf( + VisualizationDimensionItem.builder() + .position(position) + .dimension(dimension.id()) + .build() + ) + } else { + dimension.items()!!.map { item -> + item.toBuilder() + .position(position) + .dimension(dimension.id()) + .build() + } + } + }?.flatten() ?: emptyList() + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleWiper.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleWiper.kt index 8d916b791d..8bcd0adb95 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleWiper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleWiper.kt @@ -29,8 +29,7 @@ package org.hisp.dhis.android.core.visualization.internal import dagger.Reusable import javax.inject.Inject -import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLinkTableInfo +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo import org.hisp.dhis.android.core.visualization.VisualizationTableInfo import org.hisp.dhis.android.core.wipe.internal.ModuleWiper import org.hisp.dhis.android.core.wipe.internal.TableWiper @@ -40,8 +39,7 @@ class VisualizationModuleWiper @Inject internal constructor(private val tableWip override fun wipeMetadata() { tableWiper.wipeTables( VisualizationTableInfo.TABLE_INFO, - VisualizationCategoryDimensionLinkTableInfo.TABLE_INFO, - DataDimensionItemTableInfo.TABLE_INFO + VisualizationDimensionItemTableInfo.TABLE_INFO, ) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationPackageDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationPackageDIModule.java index 4456c0fc43..75f3f12722 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationPackageDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationPackageDIModule.java @@ -38,9 +38,8 @@ import retrofit2.Retrofit; @Module(includes = { - DataDimensionItemEntityDIModule.class, VisualizationEntityDIModule.class, - VisualizationCategoryDimensionEntityDIModule.class + VisualizationDimensionItemEntityDIModule.class, }) public final class VisualizationPackageDIModule { diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationStore.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationStore.kt index 32f889394e..afe7920549 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationStore.kt @@ -29,10 +29,6 @@ package org.hisp.dhis.android.core.visualization.internal import android.database.Cursor import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.IntegerListColumnAdapter -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.ObjectWithUidListColumnAdapter -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.RelativePeriodsColumnAdapter -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.StringListColumnAdapter import org.hisp.dhis.android.core.arch.db.stores.binders.internal.IdentifiableStatementBinder import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapper import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore @@ -71,21 +67,11 @@ internal object VisualizationStore { w.bind(29, o.skipRounding()) w.bind(30, o.displayDensity()) w.bind(31, o.digitGroupSeparator()) - w.bind(32, RelativePeriodsColumnAdapter.serialize(o.relativePeriods())) - w.bind(33, StringListColumnAdapter.serialize(o.filterDimensions())) - w.bind(34, StringListColumnAdapter.serialize(o.rowDimensions())) - w.bind(35, StringListColumnAdapter.serialize(o.columnDimensions())) - w.bind(36, IntegerListColumnAdapter.serialize(o.organisationUnitLevels())) - w.bind(37, o.userOrganisationUnit()) - w.bind(38, o.userOrganisationUnitChildren()) - w.bind(39, o.userOrganisationUnitGrandChildren()) - w.bind(40, ObjectWithUidListColumnAdapter.serialize(o.organisationUnits())) - w.bind(41, ObjectWithUidListColumnAdapter.serialize(o.periods())) - w.bind(42, o.legend()?.showKey()) - w.bind(43, o.legend()?.style()) - w.bind(44, UidsHelper.getUidOrNull(o.legend()?.set())) - w.bind(45, o.legend()?.strategy()) - w.bind(46, o.aggregationType()) + w.bind(32, o.legend()?.showKey()) + w.bind(33, o.legend()?.style()) + w.bind(34, UidsHelper.getUidOrNull(o.legend()?.set())) + w.bind(35, o.legend()?.strategy()) + w.bind(36, o.aggregationType()) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt b/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt index d4ccd1fa76..a053ea7fa8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt @@ -39,6 +39,7 @@ import org.hisp.dhis.android.core.datastore.internal.DataStoreModuleWiper import org.hisp.dhis.android.core.datavalue.internal.DataValueModuleWiper import org.hisp.dhis.android.core.enrollment.internal.EnrollmentModuleWiper import org.hisp.dhis.android.core.event.internal.EventModuleWiper +import org.hisp.dhis.android.core.expressiondimensionitem.internal.ExpressionDimensionItemModuleWiper import org.hisp.dhis.android.core.fileresource.internal.FileResourceModuleWiper import org.hisp.dhis.android.core.imports.internal.ImportModuleWiper import org.hisp.dhis.android.core.indicator.internal.IndicatorModuleWiper @@ -74,6 +75,7 @@ internal class D2ModuleWipers @Inject constructor( dataValue: DataValueModuleWiper, enrollment: EnrollmentModuleWiper, event: EventModuleWiper, + expressionDimensionItem: ExpressionDimensionItemModuleWiper, fileResource: FileResourceModuleWiper, importModule: ImportModuleWiper, indicator: IndicatorModuleWiper, @@ -111,6 +113,7 @@ internal class D2ModuleWipers @Inject constructor( dataValue, enrollment, event, + expressionDimensionItem, fileResource, importModule, indicator, diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/DataDimensionItemSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/DataDimensionItemSamples.kt deleted file mode 100644 index c8aad50226..0000000000 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/DataDimensionItemSamples.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.data.visualization - -import org.hisp.dhis.android.core.common.ObjectWithUid -import org.hisp.dhis.android.core.visualization.DataDimensionItem -import org.hisp.dhis.android.core.visualization.DataDimensionItemProgramAttribute -import org.hisp.dhis.android.core.visualization.DataDimensionItemProgramDataElement -import org.hisp.dhis.android.core.visualization.DataDimensionItemType - -object DataDimensionItemSamples { - - fun dataDimensionItem(): DataDimensionItem = - DataDimensionItem.builder() - .id(1L) - .visualization("visualization_uid") - .dataDimensionItemType(DataDimensionItemType.DATA_ELEMENT) - .dataElement(ObjectWithUid.create("data_element_uid")) - .build() - - fun dataDimensionItemProgramDataElement(): DataDimensionItem = - DataDimensionItem.builder() - .id(1L) - .visualization("visualization_uid") - .dataDimensionItemType(DataDimensionItemType.PROGRAM_DATA_ELEMENT) - .programDataElement(DataDimensionItemProgramDataElement.builder().uid("program.data_element").build()) - .build() - - fun dataDimensionItemAttribute(): DataDimensionItem = - DataDimensionItem.builder() - .id(1L) - .visualization("visualization_uid") - .dataDimensionItemType(DataDimensionItemType.PROGRAM_DATA_ELEMENT) - .programAttribute(DataDimensionItemProgramAttribute.builder().uid("program.data_element").build()) - .build() -} diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationDimensionItemSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationDimensionItemSamples.kt new file mode 100644 index 0000000000..a03c9d8161 --- /dev/null +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationDimensionItemSamples.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.data.visualization + +import org.hisp.dhis.android.core.visualization.LayoutPosition +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem + +object VisualizationDimensionItemSamples { + + fun visualizationDimensionItem(): VisualizationDimensionItem = + VisualizationDimensionItem.builder() + .id(1L) + .visualization("visualization_uid") + .position(LayoutPosition.COLUMN) + .dimension("dx") + .dimensionItem("item") + .dimensionItemType("DATA_ELEMENT") + .build() +} diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationSamples.kt index 02def10534..9952630592 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationSamples.kt +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationSamples.kt @@ -79,36 +79,85 @@ internal object VisualizationSamples { ) .displayDensity(DisplayDensity.NORMAL) .digitGroupSeparator(DigitGroupSeparator.COMMA) - .relativePeriods(hashMapOf(RelativePeriod.THIS_YEAR to false, RelativePeriod.LAST_12_MONTHS to true)) - .categoryDimensions( + .columns( listOf( - CategoryDimension.builder() - .category(ObjectWithUid.create("fMZEcRHuamy")) - .categoryOptions(listOf(ObjectWithUid.create("qkPbeWaFsnU"), ObjectWithUid.create("wbrDrL2aYEc"))) + VisualizationDimension.builder() + .id("dx") + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem("Uvn6LCg7dVU") + .dimensionItemType(DataDimensionItemType.INDICATOR.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem("cYeuwXTCPkU") + .dimensionItemType(DataDimensionItemType.DATA_ELEMENT.name) + .build() + ) + ) + .build(), + VisualizationDimension.builder() + .id("fMZEcRHuamy") + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem("qkPbeWaFsnU") + .dimensionItemType("CATEGORY_OPTION") + .build(), + VisualizationDimensionItem.builder() + .dimensionItem("wbrDrL2aYEc") + .dimensionItemType("CATEGORY_OPTION") + .build() + ) + ) .build() ) ) - .filterDimensions(listOf("ou")) - .rowDimensions(listOf("pe")) - .columnDimensions(listOf("dx", "fMZEcRHuamy", "fkAkrdC7eJF")) - .dataDimensionItems( + .rows( listOf( - DataDimensionItem.builder() - .dataDimensionItemType(DataDimensionItemType.INDICATOR) - .indicator(ObjectWithUid.create("Uvn6LCg7dVU")) - .build(), - DataDimensionItem.builder() - .dataDimensionItemType(DataDimensionItemType.DATA_ELEMENT) - .dataElement(ObjectWithUid.create("cYeuwXTCPkU")) + VisualizationDimension.builder() + .id("pe") + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem("202102") + .dimensionItemType("PERIOD") + .build(), + VisualizationDimensionItem.builder() + .dimensionItem("202103") + .dimensionItemType("PERIOD") + .build(), + VisualizationDimensionItem.builder() + .dimensionItem("2021S2") + .dimensionItemType("PERIOD") + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativePeriod.LAST_12_MONTHS.name) + .dimensionItemType("PERIOD") + .build() + ) + ) + .build() + ) + ) + .filters( + listOf( + VisualizationDimension.builder() + .id("ou") + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem("YuQRtpLP10I") + .dimensionItemType("ORGANISATION_UNIT") + .build(), + VisualizationDimensionItem.builder() + .dimensionItem("USER_ORGUNIT") + .build() + ) + ) .build() ) ) - .organisationUnitLevels(listOf(3)) - .userOrganisationUnit(false) - .userOrganisationUnitChildren(false) - .userOrganisationUnitGrandChildren(false) - .organisationUnits(listOf(ObjectWithUid.create("YuQRtpLP10I"), ObjectWithUid.create("vWbkYPRmKyS"))) - .periods(listOf(ObjectWithUid.create("202102"), ObjectWithUid.create("202103"), ObjectWithUid.create("2021S2"))) .aggregationType(AggregationType.SUM) .build() } diff --git a/core/src/sharedTest/resources/expressiondimensionitem/expression_dimension_items.json b/core/src/sharedTest/resources/expressiondimensionitem/expression_dimension_items.json new file mode 100644 index 0000000000..7782c2ce93 --- /dev/null +++ b/core/src/sharedTest/resources/expressiondimensionitem/expression_dimension_items.json @@ -0,0 +1,14 @@ +{ + "expressionDimensionItems": [ + { + "id": "yYo5Gy4sZa0", + "name": "Double ANC visit", + "displayName": "Double ANC visit", + "code": "ANC_code", + "created": "2023-05-16T00:42:44.670", + "lastUpdated": "2023-05-16T00:42:44.670", + "expression": "#{g9eOBujte1U} * 2", + "attributeValues": [] + } + ] +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/visualization/visualization.json b/core/src/sharedTest/resources/visualization/visualization.json index f5c312e268..1c1495dbd7 100644 --- a/core/src/sharedTest/resources/visualization/visualization.json +++ b/core/src/sharedTest/resources/visualization/visualization.json @@ -29,277 +29,102 @@ "skipRounding": false, "displayDensity": "NORMAL", "digitGroupSeparator": "COMMA", - "relativePeriods": { - "thisYear": false, - "quartersLastYear": false, - "last30Days": false, - "last52Weeks": false, - "thisWeek": false, - "last90Days": false, - "last60Days": false, - "lastMonth": false, - "last14Days": false, - "biMonthsThisYear": false, - "monthsThisYear": false, - "last2SixMonths": false, - "yesterday": false, - "thisQuarter": false, - "last12Months": true, - "last5FinancialYears": false, - "thisSixMonth": false, - "lastQuarter": false, - "thisFinancialYear": false, - "last4Weeks": false, - "last3Months": false, - "thisDay": false, - "thisMonth": false, - "last5Years": false, - "last6BiMonths": false, - "last4BiWeeks": false, - "lastFinancialYear": false, - "lastBiWeek": false, - "weeksThisYear": false, - "last6Months": false, - "last3Days": false, - "quartersThisYear": false, - "monthsLastYear": false, - "lastWeek": false, - "last7Days": false, - "last180Days": false, - "thisBimonth": false, - "lastBimonth": false, - "lastSixMonth": false, - "thisBiWeek": false, - "lastYear": false, - "last12Weeks": false, - "last4Quarters": false - }, - "categoryDimensions": [ + "filters": [ { - "category": { - "id": "fMZEcRHuamy" - }, - "categoryOptions": [ + "id": "ou", + "items": [ + { + "dimensionItem": "USER_ORGUNIT" + }, + { + "dimensionItem": "LEVEL-3" + }, { - "id": "qkPbeWaFsnU" + "dimensionItem": "YuQRtpLP10I", + "dimensionItemType": "ORGANISATION_UNIT" }, { - "id": "wbrDrL2aYEc" + "dimensionItem": "vWbkYPRmKyS", + "dimensionItemType": "ORGANISATION_UNIT" } ] - }, - { - "category": { - "id": "fkAkrdC7eJF" - }, - "categoryOptions": [ - ] } ], - "filterDimensions": [ - "ou" - ], - "rowDimensions": [ - "pe" - ], - "columnDimensions": [ - "dx", - "fMZEcRHuamy", - "fkAkrdC7eJF" - ], - "dataDimensionItems": [ - { - "dataDimensionItemType": "INDICATOR", - "indicator": { - "id": "Uvn6LCg7dVU" - } - }, - { - "dataDimensionItemType": "DATA_ELEMENT", - "dataElement": { - "id": "cYeuwXTCPkU" - } - }, + "rows": [ { - "dataDimensionItemType": "DATA_ELEMENT_OPERAND", - "dataElementOperand": { - "lastUpdated": "2021-06-16T14:27:07.790", - "id": "Jtf34kNZhzP.pq2XI5kz2BY", - "created": "2021-06-16T14:27:07.790", - "name": "ANC 3rd visit Fixed", - "shortName": "ANC 3rd visit Fixed", - "aggregationType": "SUM", - "displayName": "ANC 3rd visit Fixed", - "displayShortName": "ANC 3rd visit Fixed", - "externalAccess": false, - "periodOffset": 0, - "dimensionItem": "Jtf34kNZhzP.pq2XI5kz2BY", - "sharing": { - "external": false, - "users": {}, - "userGroups": {} - }, - "displayFormName": "ANC 3rd visit Fixed", - "favorite": false, - "dimensionItemType": "DATA_ELEMENT_OPERAND", - "access": { - "read": true, - "update": true, - "externalize": false, - "delete": true, - "write": true, - "manage": true + "id": "pe", + "items": [ + { + "dimensionItem": "202102", + "dimensionItemType": "PERIOD" }, - "categoryOptionCombo": { - "id": "pq2XI5kz2BY" + { + "dimensionItem": "202102", + "dimensionItemType": "PERIOD" }, - "dataElement": { - "id": "Jtf34kNZhzP" + { + "dimensionItem": "2021S2", + "dimensionItemType": "PERIOD" }, - "favorites": [], - "translations": [], - "userGroupAccesses": [], - "attributeValues": [], - "userAccesses": [], - "legendSets": [] - } - }, - { - "dataDimensionItemType": "PROGRAM_INDICATOR", - "programIndicator": { - "id": "p2Zxg0wcPQ3" - } - }, + { + "dimensionItem": "LAST_12_MONTHS", + "dimensionItemType": "PERIOD" + } + ] + } + ], + "columns": [ { - "dataDimensionItemType": "PROGRAM_DATA_ELEMENT", - "programDataElement": { - "name": "Antenatal care visit WHOMCH Smoking", - "shortName": "Antenatal care Smoking", - "aggregationType": "SUM", - "displayName": "Antenatal care visit WHOMCH Smoking", - "displayShortName": "Antenatal care Smoking", - "externalAccess": false, - "periodOffset": 0, - "valueType": "BOOLEAN", - "dimensionItem": "lxAQ7Zs9VYR.sWoqcoByYmD", - "displayFormName": "Antenatal care visit WHOMCH Smoking", - "favorite": false, - "dimensionItemType": "PROGRAM_DATA_ELEMENT", - "access": { - "read": true, - "update": true, - "externalize": true, - "delete": true, - "write": true, - "manage": true + "id": "dx", + "items": [ + { + "dimensionItem": "Uvn6LCg7dVU", + "dimensionItemType": "INDICATOR" }, - "program": { - "id": "lxAQ7Zs9VYR" + { + "dimensionItem": "cYeuwXTCPkU", + "dimensionItemType": "DATA_ELEMENT" }, - "dataElement": { - "id": "sWoqcoByYmD" + { + "dimensionItem": "Jtf34kNZhzP.pq2XI5kz2BY", + "dimensionItemType": "DATA_ELEMENT_OPERAND" }, - "favorites": [], - "translations": [], - "userGroupAccesses": [], - "attributeValues": [], - "userAccesses": [], - "legendSets": [] - } - }, - { - "dataDimensionItemType": "PROGRAM_ATTRIBUTE", - "programAttribute": { - "name": "XX TEST TRACKER PROGRAM Is dead", - "aggregationType": "NONE", - "displayName": "XX TEST TRACKER PROGRAM Is dead", - "externalAccess": false, - "periodOffset": 0, - "dimensionItem": "U5KybNCtA3E.iggSfNDnsCw", - "displayFormName": "XX TEST TRACKER PROGRAM Is dead", - "favorite": false, - "dimensionItemType": "PROGRAM_ATTRIBUTE", - "access": { - "read": true, - "update": true, - "externalize": true, - "delete": true, - "write": true, - "manage": true + { + "dimensionItem": "p2Zxg0wcPQ3", + "dimensionItemType": "PROGRAM_INDICATOR" }, - "program": { - "id": "U5KybNCtA3E" + { + "dimensionItem": "lxAQ7Zs9VYR.sWoqcoByYmD", + "dimensionItemType": "PROGRAM_DATA_ELEMENT" }, - "attribute": { - "id": "iggSfNDnsCw" + { + "dimensionItem": "U5KybNCtA3E.iggSfNDnsCw", + "dimensionItemType": "PROGRAM_ATTRIBUTE" }, - "favorites": [], - "translations": [], - "userGroupAccesses": [], - "attributeValues": [], - "userAccesses": [], - "legendSets": [] - } - } - ], - "organisationUnitLevels": [ - 3 - ], - "userOrganisationUnit": false, - "userOrganisationUnitChildren": false, - "userOrganisationUnitGrandChildren": false, - "organisationUnits": [ - { - "id": "YuQRtpLP10I" - }, - { - "id": "vWbkYPRmKyS" - } - ], - "periods": [ - { - "id": "202102" - }, - { - "id": "202103" - }, - { - "id": "2021S2" - } - ], - "columns": [ - { - "id": "dx" + { + "dimensionItem": "lVSuzOPSkwg", + "dimensionItemType": "EXPRESSION_DIMENSION_ITEM" + } + ] }, { - "id": "fMZEcRHuamy" + "id": "fMZEcRHuamy", + "items": [ + { + "dimensionItem": "qkPbeWaFsnU", + "dimensionItemType": "CATEGORY_OPTION" + }, + { + "dimensionItem": "wbrDrL2aYEc", + "dimensionItemType": "CATEGORY_OPTION" + } + ] }, { - "id": "fkAkrdC7eJF" - } - ], - "filters": [ - { - "id": "ou" - } - ], - "rows": [ - { - "id": "pe" + "id": "fkAkrdC7eJF", + "items": [] } ], - "yearlySeries": [], - "reportingParams": { - "parentOrganisationUnit": false, - "reportingPeriod": false, - "organisationUnit": false, - "grandParentOrganisationUnit": false - }, - "completedOnly": false, - "cumulativeValues": false, - "regression": false, - "regressionType": "NONE", - "numberType": "VALUE", "aggregationType": "SUM", "legend": { "hidden": false, @@ -309,70 +134,5 @@ "set": { "id": "Yf6UHoPkd56" } - }, - "fontSize": "NORMAL", - "favorite": false, - "sortOrder": 0, - "topLimit": 0, - "colorSet": "DEFAULT", - "showData": true, - "externalAccess": false, - "fontStyle": {}, - "parentGraphMap": { - "YuQRtpLP10I": "ImspTQPwCqd/O6uvpzGd5pu", - "vWbkYPRmKyS": "ImspTQPwCqd/O6uvpzGd5pu" - }, - "sharing": { - "owner": "xE7jOejl9FI", - "external": false, - "users": {}, - "userGroups": {}, - "public": "--------" - }, - "publicAccess": "--------", - "access": { - "read": true, - "update": true, - "externalize": true, - "delete": true, - "write": true, - "manage": true - }, - "lastUpdatedBy": { - "displayName": "John Traore", - "name": "John Traore", - "id": "xE7jOejl9FI", - "username": "admin" - }, - "createdBy": { - "displayName": "John Traore", - "name": "John Traore", - "id": "xE7jOejl9FI", - "username": "admin" - }, - "user": { - "displayName": "John Traore", - "name": "John Traore", - "id": "xE7jOejl9FI", - "username": "admin" - }, - "dataElementGroupSetDimensions": [], - "axes": [], - "attributeDimensions": [], - "translations": [], - "interpretations": [], - "userGroupAccesses": [], - "subscribers": [], - "optionalAxes": [], - "series": [], - "attributeValues": [], - "itemOrganisationUnitGroups": [], - "dataElementDimensions": [], - "programIndicatorDimensions": [], - "userAccesses": [], - "favorites": [], - "subscribed": false, - "categoryOptionGroupSetDimensions": [], - "organisationUnitGroupSetDimensions": [], - "href": "https://play.dhis2.org/2.36.0/api/visualizations/PYBH8ZaAQnC" + } } \ No newline at end of file diff --git a/core/src/sharedTest/resources/visualization/visualization_api_36.json b/core/src/sharedTest/resources/visualization/visualization_api_36.json index 0013c3513e..75e6ebe498 100644 --- a/core/src/sharedTest/resources/visualization/visualization_api_36.json +++ b/core/src/sharedTest/resources/visualization/visualization_api_36.json @@ -29,277 +29,102 @@ "skipRounding": false, "displayDensity": "NORMAL", "digitGroupSeparator": "COMMA", - "relativePeriods": { - "thisYear": false, - "quartersLastYear": false, - "last30Days": false, - "last52Weeks": false, - "thisWeek": false, - "last90Days": false, - "last60Days": false, - "lastMonth": false, - "last14Days": false, - "biMonthsThisYear": false, - "monthsThisYear": false, - "last2SixMonths": false, - "yesterday": false, - "thisQuarter": false, - "last12Months": true, - "last5FinancialYears": false, - "thisSixMonth": false, - "lastQuarter": false, - "thisFinancialYear": false, - "last4Weeks": false, - "last3Months": false, - "thisDay": false, - "thisMonth": false, - "last5Years": false, - "last6BiMonths": false, - "last4BiWeeks": false, - "lastFinancialYear": false, - "lastBiWeek": false, - "weeksThisYear": false, - "last6Months": false, - "last3Days": false, - "quartersThisYear": false, - "monthsLastYear": false, - "lastWeek": false, - "last7Days": false, - "last180Days": false, - "thisBimonth": false, - "lastBimonth": false, - "lastSixMonth": false, - "thisBiWeek": false, - "lastYear": false, - "last12Weeks": false, - "last4Quarters": false - }, - "categoryDimensions": [ + "filters": [ { - "category": { - "id": "fMZEcRHuamy" - }, - "categoryOptions": [ + "id": "ou", + "items": [ + { + "dimensionItem": "USER_ORGUNIT" + }, + { + "dimensionItem": "LEVEL-3" + }, { - "id": "qkPbeWaFsnU" + "dimensionItem": "YuQRtpLP10I", + "dimensionItemType": "ORGANISATION_UNIT" }, { - "id": "wbrDrL2aYEc" + "dimensionItem": "vWbkYPRmKyS", + "dimensionItemType": "ORGANISATION_UNIT" } ] - }, - { - "category": { - "id": "fkAkrdC7eJF" - }, - "categoryOptions": [ - ] } ], - "filterDimensions": [ - "ou" - ], - "rowDimensions": [ - "pe" - ], - "columnDimensions": [ - "dx", - "fMZEcRHuamy", - "fkAkrdC7eJF" - ], - "dataDimensionItems": [ - { - "dataDimensionItemType": "INDICATOR", - "indicator": { - "id": "Uvn6LCg7dVU" - } - }, - { - "dataDimensionItemType": "DATA_ELEMENT", - "dataElement": { - "id": "cYeuwXTCPkU" - } - }, + "rows": [ { - "dataDimensionItemType": "DATA_ELEMENT_OPERAND", - "dataElementOperand": { - "lastUpdated": "2021-06-16T14:27:07.790", - "id": "Jtf34kNZhzP.pq2XI5kz2BY", - "created": "2021-06-16T14:27:07.790", - "name": "ANC 3rd visit Fixed", - "shortName": "ANC 3rd visit Fixed", - "aggregationType": "SUM", - "displayName": "ANC 3rd visit Fixed", - "displayShortName": "ANC 3rd visit Fixed", - "externalAccess": false, - "periodOffset": 0, - "dimensionItem": "Jtf34kNZhzP.pq2XI5kz2BY", - "sharing": { - "external": false, - "users": {}, - "userGroups": {} - }, - "displayFormName": "ANC 3rd visit Fixed", - "favorite": false, - "dimensionItemType": "DATA_ELEMENT_OPERAND", - "access": { - "read": true, - "update": true, - "externalize": false, - "delete": true, - "write": true, - "manage": true + "id": "pe", + "items": [ + { + "dimensionItem": "202102", + "dimensionItemType": "PERIOD" }, - "categoryOptionCombo": { - "id": "pq2XI5kz2BY" + { + "dimensionItem": "202102", + "dimensionItemType": "PERIOD" }, - "dataElement": { - "id": "Jtf34kNZhzP" + { + "dimensionItem": "2021S2", + "dimensionItemType": "PERIOD" }, - "favorites": [], - "translations": [], - "userGroupAccesses": [], - "attributeValues": [], - "userAccesses": [], - "legendSets": [] - } - }, - { - "dataDimensionItemType": "PROGRAM_INDICATOR", - "programIndicator": { - "id": "p2Zxg0wcPQ3" - } - }, + { + "dimensionItem": "LAST_12_MONTHS", + "dimensionItemType": "PERIOD" + } + ] + } + ], + "columns": [ { - "dataDimensionItemType": "PROGRAM_DATA_ELEMENT", - "programDataElement": { - "name": "Antenatal care visit WHOMCH Smoking", - "shortName": "Antenatal care Smoking", - "aggregationType": "SUM", - "displayName": "Antenatal care visit WHOMCH Smoking", - "displayShortName": "Antenatal care Smoking", - "externalAccess": false, - "periodOffset": 0, - "valueType": "BOOLEAN", - "dimensionItem": "lxAQ7Zs9VYR.sWoqcoByYmD", - "displayFormName": "Antenatal care visit WHOMCH Smoking", - "favorite": false, - "dimensionItemType": "PROGRAM_DATA_ELEMENT", - "access": { - "read": true, - "update": true, - "externalize": true, - "delete": true, - "write": true, - "manage": true + "id": "dx", + "items": [ + { + "dimensionItem": "Uvn6LCg7dVU", + "dimensionItemType": "INDICATOR" }, - "program": { - "id": "lxAQ7Zs9VYR" + { + "dimensionItem": "cYeuwXTCPkU", + "dimensionItemType": "DATA_ELEMENT" }, - "dataElement": { - "id": "sWoqcoByYmD" + { + "dimensionItem": "Jtf34kNZhzP.pq2XI5kz2BY", + "dimensionItemType": "DATA_ELEMENT_OPERAND" }, - "favorites": [], - "translations": [], - "userGroupAccesses": [], - "attributeValues": [], - "userAccesses": [], - "legendSets": [] - } - }, - { - "dataDimensionItemType": "PROGRAM_ATTRIBUTE", - "programAttribute": { - "name": "XX TEST TRACKER PROGRAM Is dead", - "aggregationType": "NONE", - "displayName": "XX TEST TRACKER PROGRAM Is dead", - "externalAccess": false, - "periodOffset": 0, - "dimensionItem": "U5KybNCtA3E.iggSfNDnsCw", - "displayFormName": "XX TEST TRACKER PROGRAM Is dead", - "favorite": false, - "dimensionItemType": "PROGRAM_ATTRIBUTE", - "access": { - "read": true, - "update": true, - "externalize": true, - "delete": true, - "write": true, - "manage": true + { + "dimensionItem": "p2Zxg0wcPQ3", + "dimensionItemType": "PROGRAM_INDICATOR" }, - "program": { - "id": "U5KybNCtA3E" + { + "dimensionItem": "lxAQ7Zs9VYR.sWoqcoByYmD", + "dimensionItemType": "PROGRAM_DATA_ELEMENT" }, - "attribute": { - "id": "iggSfNDnsCw" + { + "dimensionItem": "U5KybNCtA3E.iggSfNDnsCw", + "dimensionItemType": "PROGRAM_ATTRIBUTE" }, - "favorites": [], - "translations": [], - "userGroupAccesses": [], - "attributeValues": [], - "userAccesses": [], - "legendSets": [] - } - } - ], - "organisationUnitLevels": [ - 3 - ], - "userOrganisationUnit": false, - "userOrganisationUnitChildren": false, - "userOrganisationUnitGrandChildren": false, - "organisationUnits": [ - { - "id": "YuQRtpLP10I" - }, - { - "id": "vWbkYPRmKyS" - } - ], - "periods": [ - { - "id": "202102" - }, - { - "id": "202103" - }, - { - "id": "2021S2" - } - ], - "columns": [ - { - "id": "dx" + { + "dimensionItem": "lVSuzOPSkwg", + "dimensionItemType": "EXPRESSION_DIMENSION_ITEM" + } + ] }, { - "id": "fMZEcRHuamy" + "id": "fMZEcRHuamy", + "items": [ + { + "dimensionItem": "qkPbeWaFsnU", + "dimensionItemType": "CATEGORY_OPTION" + }, + { + "dimensionItem": "wbrDrL2aYEc", + "dimensionItemType": "CATEGORY_OPTION" + } + ] }, { - "id": "fkAkrdC7eJF" + "id": "fkAkrdC7eJF", + "items": [] } ], - "filters": [ - { - "id": "ou" - } - ], - "rows": [ - { - "id": "pe" - } - ], - "yearlySeries": [], - "reportingParams": { - "parentOrganisationUnit": false, - "reportingPeriod": false, - "organisationUnit": false, - "grandParentOrganisationUnit": false - }, - "completedOnly": false, - "cumulativeValues": false, - "regression": false, - "regressionType": "NONE", - "numberType": "VALUE", "aggregationType": "SUM", "legend": { "label": { @@ -313,70 +138,5 @@ "legendDisplayStyle": "FILL", "legendSet": { "id": "Yf6UHoPkd56" - }, - "fontSize": "NORMAL", - "favorite": false, - "sortOrder": 0, - "topLimit": 0, - "colorSet": "DEFAULT", - "showData": true, - "externalAccess": false, - "fontStyle": {}, - "parentGraphMap": { - "YuQRtpLP10I": "ImspTQPwCqd/O6uvpzGd5pu", - "vWbkYPRmKyS": "ImspTQPwCqd/O6uvpzGd5pu" - }, - "sharing": { - "owner": "xE7jOejl9FI", - "external": false, - "users": {}, - "userGroups": {}, - "public": "--------" - }, - "publicAccess": "--------", - "access": { - "read": true, - "update": true, - "externalize": true, - "delete": true, - "write": true, - "manage": true - }, - "lastUpdatedBy": { - "displayName": "John Traore", - "name": "John Traore", - "id": "xE7jOejl9FI", - "username": "admin" - }, - "createdBy": { - "displayName": "John Traore", - "name": "John Traore", - "id": "xE7jOejl9FI", - "username": "admin" - }, - "user": { - "displayName": "John Traore", - "name": "John Traore", - "id": "xE7jOejl9FI", - "username": "admin" - }, - "dataElementGroupSetDimensions": [], - "axes": [], - "attributeDimensions": [], - "translations": [], - "interpretations": [], - "userGroupAccesses": [], - "subscribers": [], - "optionalAxes": [], - "series": [], - "attributeValues": [], - "itemOrganisationUnitGroups": [], - "dataElementDimensions": [], - "programIndicatorDimensions": [], - "userAccesses": [], - "favorites": [], - "subscribed": false, - "categoryOptionGroupSetDimensions": [], - "organisationUnitGroupSetDimensions": [], - "href": "https://play.dhis2.org/2.36.0/api/visualizations/PYBH8ZaAQnC" + } } \ No newline at end of file diff --git a/core/src/sharedTest/resources/visualization/visualization_simplified.json b/core/src/sharedTest/resources/visualization/visualization_simplified.json deleted file mode 100644 index f7a8e3551b..0000000000 --- a/core/src/sharedTest/resources/visualization/visualization_simplified.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "id": "PYBH8ZaAQnC", - "name": "Android SDK Visualization sample", - "displayName": "Android SDK Visualization sample", - "description": "Sample visualization for the Android SDK", - "displayDescription": "Sample visualization for the Android SDK", - "displayFormName": "Android SDK Visualization sample", - "title": "Sample title", - "displayTitle": "Sample display title", - "subtitle": "Sample subtitle", - "displaySubtitle": "Sample display subtitle", - "lastUpdated": "2021-06-16T14:26:50.195", - "created": "2021-06-16T14:26:50.195", - "type": "PIVOT_TABLE", - "hideTitle": false, - "hideSubtitle": false, - "hideEmptyColumns": false, - "hideEmptyRows": false, - "hideEmptyRowItems": "NONE", - "hideLegend": false, - "legend": { - "showKey": false, - "style": "FILL", - "strategy": "FIXED", - "set": { - "id": "Yf6UHoPkd57" - } - }, - "showHierarchy": false, - "rowTotals": true, - "rowSubTotals": false, - "colTotals": false, - "colSubTotals": false, - "showDimensionLabels": false, - "percentStackedValues": false, - "noSpaceBetweenColumns": false, - "skipRounding": false, - "displayDensity": "NORMAL", - "digitGroupSeparator": "COMMA", - "relativePeriods": { - "thisYear": false, - "last12Months": true - }, - "categoryDimensions": [ - { - "category": { - "id": "fMZEcRHuamy" - }, - "categoryOptions": [ - { - "id": "qkPbeWaFsnU" - }, - { - "id": "wbrDrL2aYEc" - } - ] - } - ], - "filterDimensions": [ - "ou" - ], - "rowDimensions": [ - "pe" - ], - "columnDimensions": [ - "dx", - "fMZEcRHuamy", - "fkAkrdC7eJF" - ], - "dataDimensionItems": [ - { - "dataDimensionItemType": "INDICATOR", - "indicator": { - "id": "Uvn6LCg7dVU" - } - }, - { - "dataDimensionItemType": "DATA_ELEMENT", - "dataElement": { - "id": "cYeuwXTCPkU" - } - } - ], - "organisationUnitLevels": [ - 3 - ], - "userOrganisationUnit": false, - "userOrganisationUnitChildren": false, - "userOrganisationUnitGrandChildren": false, - "organisationUnits": [ - { - "id": "YuQRtpLP10I" - }, - { - "id": "vWbkYPRmKyS" - } - ], - "periods": [ - { - "id": "202102" - }, - { - "id": "202103" - }, - { - "id": "2021S2" - } - ] -} \ No newline at end of file diff --git a/core/src/sharedTest/resources/visualization/visualizations.json b/core/src/sharedTest/resources/visualization/visualizations.json index ad8cb6d971..9e63505a68 100644 --- a/core/src/sharedTest/resources/visualization/visualizations.json +++ b/core/src/sharedTest/resources/visualization/visualizations.json @@ -39,82 +39,72 @@ "id": "Yf6UHoPkd57" } }, - "relativePeriods": { - "thisYear": false, - "last12Months": true - }, - "categoryDimensions": [ + "filters": [ { - "category": { - "id": "KfdsGBcoiCa" - }, - "categoryOptions": [ + "id": "ou", + "items": [ { - "id": "TNYQzTHdoxL" + "dimensionItem": "YuQRtpLP10I", + "dimensionItemType": "ORGANISATION_UNIT" }, { - "id": "TXGfLxZlInA" + "dimensionItem": "vWbkYPRmKyS", + "dimensionItemType": "ORGANISATION_UNIT" }, { - "id": "uZUnebiT5DI" + "dimensionItem": "LEVEL-3" } ] } ], - "filterDimensions": [ - "ou" - ], - "rowDimensions": [ - "pe" - ], - "columnDimensions": [ - "dx", - "fMZEcRHuamy", - "fkAkrdC7eJF" - ], - "dataDimensionItems": [ - { - "dataDimensionItemType": "INDICATOR", - "indicator": { - "id": "Uvn6LCg7dVU" - } - }, - { - "dataDimensionItemType": "DATA_ELEMENT", - "dataElement": { - "id": "cYeuwXTCPkU" - } - }, + "rows": [ { - "dataDimensionItemType": "PROGRAM_INDICATOR", - "programIndicator": { - "id": "p2Zxg0wcPQ3" - } - } - ], - "organisationUnitLevels": [ - 3 - ], - "userOrganisationUnit": false, - "userOrganisationUnitChildren": false, - "userOrganisationUnitGrandChildren": false, - "organisationUnits": [ - { - "id": "YuQRtpLP10I" - }, - { - "id": "vWbkYPRmKyS" + "id": "pe", + "items": [ + { + "dimensionItem": "202102", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "202103", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "2021S2", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "LAST_12_MONTHS", + "dimensionItemType": "PERIOD" + } + ] } ], - "periods": [ + "columns": [ { - "id": "202102" + "id": "dx", + "items": [ + { + "dimensionItem": "Uvn6LCg7dVU", + "dimensionItemType": "INDICATOR" + }, + { + "dimensionItem": "cYeuwXTCPkU", + "dimensionItemType": "DATA_ELEMENT" + }, + { + "dimensionItem": "p2Zxg0wcPQ3", + "dimensionItemType": "PROGRAM_INDICATOR" + } + ] }, { - "id": "202103" + "id": "fMZEcRHuamy", + "items": [] }, { - "id": "2021S2" + "id": "fkAkrdC7eJF", + "items": [] } ] }, @@ -153,52 +143,53 @@ "id": "TiOkbpGEud4" } }, - "relativePeriods": { - }, - "categoryDimensions": [ - ], - "filterDimensions": [ - "ou" - ], - "rowDimensions": [ - "pe" - ], - "columnDimensions": [ - "dx" - ], - "dataDimensionItems": [ - { - "dataDimensionItemType": "DATA_ELEMENT", - "dataElement": { - "id": "g9eOBujte1U" - } - }, + "filters": [ { - "dataDimensionItemType": "PROGRAM_INDICATOR", - "programIndicator": { - "id": "GSae40Fyppf" - } + "id": "ou", + "items": [ + { + "dimensionItem": "DiszpKrYNg8", + "dimensionItemType": "ORGANISATION_UNIT" + } + ] } ], - "organisationUnitLevels": [ - ], - "userOrganisationUnit": false, - "userOrganisationUnitChildren": false, - "userOrganisationUnitGrandChildren": false, - "organisationUnits": [ + "rows": [ { - "id": "DiszpKrYNg8" + "id": "pe", + "items": [ + { + "dimensionItem": "2016", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "2017", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "2018", + "dimensionItemType": "PERIOD" + } + ] } ], - "periods": [ - { - "id": "2016" - }, - { - "id": "2017" - }, + "columns": [ { - "id": "2018" + "id": "dx", + "items": [ + { + "dimensionItem": "g9eOBujte1U", + "dimensionItemType": "DATA_ELEMENT" + }, + { + "dimensionItem": "GSae40Fyppf", + "dimensionItemType": "PROGRAM_INDICATOR" + }, + { + "dimensionItem": "yYo5Gy4sZa0", + "dimensionItemType": "EXPRESSION_DIMENSION_ITEM" + } + ] } ] } diff --git a/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt index 6de1529622..a69923b694 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt @@ -37,7 +37,6 @@ import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStor import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore import org.hisp.dhis.android.core.category.Category import org.hisp.dhis.android.core.category.CategoryCategoryOptionLink -import org.hisp.dhis.android.core.common.ObjectWithUid import org.hisp.dhis.android.core.common.RelativeOrganisationUnit import org.hisp.dhis.android.core.common.RelativePeriod import org.hisp.dhis.android.core.organisationunit.OrganisationUnitLevel @@ -55,7 +54,6 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { private val categoryOptionLinkStore: LinkStore = mock() private val organisationUnitLevelStore: IdentifiableObjectStore = mock() private val category: Category = mock() - private val visualization: Visualization = mock() private val orgUnitLevel: OrganisationUnitLevel = mock() private val uid1 = "GMpWZUg2QUf" @@ -70,16 +68,19 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse dataElement dimension items`() { - val dataDimensionItems = listOf( - DataDimensionItem.builder() - .dataElement(ObjectWithUid.create(uid1)) - .dataDimensionItemType(DataDimensionItemType.DATA_ELEMENT) + val dataDimensions = listOf( + VisualizationDimension.builder() + .id("dx") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.DATA_ELEMENT.name) + .build() + )) .build() ) - whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems - - val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + val dimensionItems = helper.getDimensionItems(dataDimensions) assertThat(dimensionItems).hasSize(1) when (val item = dimensionItems.first()) { @@ -92,16 +93,19 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse dataElementOperand dimension items`() { - val dataDimensionItems = listOf( - DataDimensionItem.builder() - .dataElementOperand(ObjectWithUid.create("$uid1.$uid2")) - .dataDimensionItemType(DataDimensionItemType.DATA_ELEMENT_OPERAND) + val dataDimensions = listOf( + VisualizationDimension.builder() + .id("dx") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem("$uid1.$uid2") + .dimensionItemType(DimensionItemType.DATA_ELEMENT_OPERAND.name) + .build() + )) .build() ) - whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems - - val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + val dimensionItems = helper.getDimensionItems(dataDimensions) assertThat(dimensionItems).hasSize(1) when (val item = dimensionItems.first()) { @@ -116,16 +120,19 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse indicator dimension items`() { - val dataDimensionItems = listOf( - DataDimensionItem.builder() - .indicator(ObjectWithUid.create(uid1)) - .dataDimensionItemType(DataDimensionItemType.INDICATOR) + val dataDimensions = listOf( + VisualizationDimension.builder() + .id("dx") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.INDICATOR.name) + .build() + )) .build() ) - whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems - - val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + val dimensionItems = helper.getDimensionItems(dataDimensions) assertThat(dimensionItems).hasSize(1) when (val item = dimensionItems.first()) { @@ -138,16 +145,19 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse programIndicator dimension items`() { - val dataDimensionItems = listOf( - DataDimensionItem.builder() - .programIndicator(ObjectWithUid.create(uid1)) - .dataDimensionItemType(DataDimensionItemType.PROGRAM_INDICATOR) + val dataDimensions = listOf( + VisualizationDimension.builder() + .id("dx") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.PROGRAM_INDICATOR.name) + .build() + )) .build() ) - whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems - - val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + val dimensionItems = helper.getDimensionItems(dataDimensions) assertThat(dimensionItems).hasSize(1) when (val item = dimensionItems.first()) { @@ -160,20 +170,19 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse event dataElements dimension items`() { - val dataDimensionItems = listOf( - DataDimensionItem.builder() - .programDataElement( - DataDimensionItemProgramDataElement.builder() - .uid("$uid1.$uid2") + val dataDimensions = listOf( + VisualizationDimension.builder() + .id("dx") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem("$uid1.$uid2") + .dimensionItemType(DimensionItemType.PROGRAM_DATA_ELEMENT.name) .build() - ) - .dataDimensionItemType(DataDimensionItemType.PROGRAM_DATA_ELEMENT) + )) .build() ) - whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems - - val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + val dimensionItems = helper.getDimensionItems(dataDimensions) assertThat(dimensionItems).hasSize(1) when (val item = dimensionItems.first()) { @@ -188,20 +197,19 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse event attribute dimension items`() { - val dataDimensionItems = listOf( - DataDimensionItem.builder() - .programAttribute( - DataDimensionItemProgramAttribute.builder() - .uid("$uid1.$uid2") + val dataDimensions = listOf( + VisualizationDimension.builder() + .id("dx") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem("$uid1.$uid2") + .dimensionItemType(DimensionItemType.PROGRAM_ATTRIBUTE.name) .build() - ) - .dataDimensionItemType(DataDimensionItemType.PROGRAM_ATTRIBUTE) + )) .build() ) - whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems - - val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + val dimensionItems = helper.getDimensionItems(dataDimensions) assertThat(dimensionItems).hasSize(1) when (val item = dimensionItems.first()) { @@ -214,20 +222,57 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { } } + @Test + fun `Should parse expression dimension items`() { + val dataDimensions = listOf( + VisualizationDimension.builder() + .id("dx") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.EXPRESSION_DIMENSION_ITEM.name) + .build() + )) + .build() + ) + + val dimensionItems = helper.getDimensionItems(dataDimensions) + + assertThat(dimensionItems).hasSize(1) + when (val item = dimensionItems.first()) { + is DimensionItem.DataItem.ExpressionDimensionItem -> { + assertThat(item.uid).isEqualTo(uid1) + } + else -> + fail("Unexpected dimension item type") + } + } + @Test fun `Should parse organisation unit uids and levels`() { - val orgunitItems = listOf( - ObjectWithUid.create(uid1), - ObjectWithUid.create(uid2) + val orgunitDimensions = listOf( + VisualizationDimension.builder() + .id("ou") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(uid2) + .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem("LEVEL-1") + .build() + )) + .build() ) - val orgunitLevels = listOf(1) - whenever(visualization.organisationUnits()) doReturn orgunitItems - whenever(visualization.organisationUnitLevels()) doReturn orgunitLevels whenever(organisationUnitLevelStore.selectOneWhere(anyString())) doReturn orgUnitLevel whenever(orgUnitLevel.uid()) doReturn uid3 - val dimensionItems = helper.getDimensionItems(visualization, listOf("ou")) + val dimensionItems = helper.getDimensionItems(orgunitDimensions) assertThat(dimensionItems).hasSize(3) @@ -240,11 +285,24 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse relative organisation unit`() { - whenever(visualization.userOrganisationUnit()) doReturn true - whenever(visualization.userOrganisationUnitChildren()) doReturn true - whenever(visualization.userOrganisationUnitGrandChildren()) doReturn true + val orgunitDimensions = listOf( + VisualizationDimension.builder() + .id("ou") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT_CHILDREN.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT_GRANDCHILDREN.name) + .build() + )) + .build() + ) - val dimensionItems = helper.getDimensionItems(visualization, listOf("ou")) + val dimensionItems = helper.getDimensionItems(orgunitDimensions) assertThat(dimensionItems).hasSize(3) @@ -258,19 +316,31 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse period dimension items`() { - val periods = listOf( - ObjectWithUid.create(uid1), - ObjectWithUid.create(uid2) - ) - val relativePeriods = mapOf( - RelativePeriod.THIS_MONTH to true, - RelativePeriod.LAST_MONTH to true + val periodDimensions = listOf( + VisualizationDimension.builder() + .id("pe") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(uid2) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativePeriod.THIS_MONTH.name) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativePeriod.LAST_MONTH.name) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build() + )) + .build() ) - whenever(visualization.periods()) doReturn periods - whenever(visualization.relativePeriods()) doReturn relativePeriods - - val dimensionItems = helper.getDimensionItems(visualization, listOf("pe")) + val dimensionItems = helper.getDimensionItems(periodDimensions) assertThat(dimensionItems).hasSize(4) @@ -284,16 +354,25 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should parse category dimension items`() { val categoryDimensions = listOf( - CategoryDimension.builder() - .category(ObjectWithUid.create(uid1)) - .categoryOptions(listOf(ObjectWithUid.create(uid2), ObjectWithUid.create(uid3))) + VisualizationDimension.builder() + .id(uid1) + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid2) + .dimensionItemType(DimensionItemType.CATEGORY_OPTION.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(uid3) + .dimensionItemType(DimensionItemType.CATEGORY_OPTION.name) + .build() + )) .build() ) - whenever(visualization.categoryDimensions()) doReturn categoryDimensions whenever(categoryStore.selectByUid(uid1)) doReturn category + whenever(category.uid()) doReturn uid1 - val dimensionItems = helper.getDimensionItems(visualization, listOf(uid1)) + val dimensionItems = helper.getDimensionItems(categoryDimensions) assertThat(dimensionItems).hasSize(2) @@ -304,18 +383,32 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { @Test fun `Should combine dimensions items`() { - val periods = listOf( - ObjectWithUid.create(uid1), - ObjectWithUid.create(uid2) - ) - val orgunits = listOf( - ObjectWithUid.create(uid3) + val dimensions = listOf( + VisualizationDimension.builder() + .id("pe") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(uid2) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build() + )) + .build(), + VisualizationDimension.builder() + .id("ou") + .items(listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid3) + .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) + .build() + )) + .build() ) - whenever(visualization.periods()) doReturn periods - whenever(visualization.organisationUnits()) doReturn orgunits - - val dimensionItems = helper.getDimensionItems(visualization, listOf("pe", "ou")) + val dimensionItems = helper.getDimensionItems(dimensions) assertThat(dimensionItems).hasSize(3) diff --git a/core/src/test/java/org/hisp/dhis/android/core/domain/metadata/MetadataCallShould.kt b/core/src/test/java/org/hisp/dhis/android/core/domain/metadata/MetadataCallShould.kt index a7c6b0f639..f1059e9964 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/domain/metadata/MetadataCallShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/domain/metadata/MetadataCallShould.kt @@ -40,6 +40,7 @@ import org.hisp.dhis.android.core.common.BaseCallShould import org.hisp.dhis.android.core.configuration.internal.MultiUserDatabaseManager import org.hisp.dhis.android.core.constant.internal.ConstantModuleDownloader import org.hisp.dhis.android.core.dataset.internal.DataSetModuleDownloader +import org.hisp.dhis.android.core.expressiondimensionitem.internal.ExpressionDimensionItemModuleDownloader import org.hisp.dhis.android.core.indicator.internal.IndicatorModuleDownloader import org.hisp.dhis.android.core.legendset.internal.LegendSetModuleDownloader import org.hisp.dhis.android.core.maintenance.D2Error @@ -85,9 +86,10 @@ class MetadataCallShould : BaseCallShould() { private val multiUserDatabaseManager: MultiUserDatabaseManager = mock() private val credentialsSecureStore: CredentialsSecureStore = mock() private val legendSetModuleDownloader: LegendSetModuleDownloader = mock() + private val expressionDimensIndicatorModuleDownloader: ExpressionDimensionItemModuleDownloader = mock() // object to test - private var metadataCall: MetadataCall? = null + private lateinit var metadataCall: MetadataCall @Before @Throws(Exception::class) @@ -114,6 +116,7 @@ class MetadataCallShould : BaseCallShould() { Single.just(emptyList()) ) whenever(legendSetModuleDownloader.downloadMetadata()).thenReturn(Completable.complete()) + whenever(expressionDimensIndicatorModuleDownloader.downloadMetadata()).thenReturn(Completable.complete()) whenever(constantDownloader.downloadMetadata()).thenReturn(Single.just(emptyList())) whenever(indicatorDownloader.downloadMetadata()).thenReturn(Completable.complete()) whenever(categoryDownloader.downloadMetadata()).thenReturn(Completable.complete()) @@ -148,12 +151,13 @@ class MetadataCallShould : BaseCallShould() { multiUserDatabaseManager, credentialsSecureStore, legendSetModuleDownloader, + expressionDimensIndicatorModuleDownloader, ) } @Test fun succeed_when_endpoint_calls_succeed() { - metadataCall!!.blockingDownload() + metadataCall.blockingDownload() } @Test @@ -169,7 +173,7 @@ class MetadataCallShould : BaseCallShould() { } private fun downloadAndAssertError() { - val testObserver = metadataCall!!.download().test() + val testObserver = metadataCall.download().test() testObserver.assertError(D2Error::class.java) testObserver.dispose() } @@ -218,8 +222,8 @@ class MetadataCallShould : BaseCallShould() { @Test fun call_wrapObservableTransactionally() { - metadataCall!!.blockingDownload() - Mockito.verify(rxAPICallExecutor).wrapObservableTransactionally( + metadataCall.blockingDownload() + verify(rxAPICallExecutor).wrapObservableTransactionally( any(), eq(true) ) @@ -227,7 +231,7 @@ class MetadataCallShould : BaseCallShould() { @Test fun delete_foreign_key_violations_before_calls() { - metadataCall!!.blockingDownload() - Mockito.verify(databaseAdapter).delete(ForeignKeyViolationTableInfo.TABLE_INFO.name()) + metadataCall.blockingDownload() + verify(databaseAdapter).delete(ForeignKeyViolationTableInfo.TABLE_INFO.name()) } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationShould.kt b/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationShould.kt index 2ef3385b29..9daea81387 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationShould.kt @@ -43,27 +43,23 @@ class VisualizationShould : BaseObjectShould("visualization/visualization.json") assertThat(visualization.type()).isEqualTo(VisualizationType.PIVOT_TABLE) assertThat(visualization.digitGroupSeparator()).isEqualTo(DigitGroupSeparator.COMMA) assertThat(visualization.aggregationType()).isEqualTo(AggregationType.SUM) - assertThat(visualization.dataDimensionItems()!![0].indicator()!!.uid()).isEqualTo("Uvn6LCg7dVU") - assertThat(visualization.dataDimensionItems()!![1].dataElement()!!.uid()).isEqualTo("cYeuwXTCPkU") - assertThat(visualization.dataDimensionItems()!![2].dataElementOperand()!!.uid()) - .isEqualTo("Jtf34kNZhzP.pq2XI5kz2BY") - assertThat(visualization.dataDimensionItems()!![3].programIndicator()!!.uid()).isEqualTo("p2Zxg0wcPQ3") - assertThat(visualization.dataDimensionItems()!![4].programDataElement()!!.uid()) - .isEqualTo("lxAQ7Zs9VYR.sWoqcoByYmD") - assertThat(visualization.dataDimensionItems()!![4].programDataElement()!!.program()!!.uid()) - .isEqualTo("lxAQ7Zs9VYR") - assertThat(visualization.dataDimensionItems()!![4].programDataElement()!!.dataElement()!!.uid()) - .isEqualTo("sWoqcoByYmD") - assertThat(visualization.dataDimensionItems()!![5].programAttribute()!!.uid()) - .isEqualTo("U5KybNCtA3E.iggSfNDnsCw") - assertThat(visualization.dataDimensionItems()!![5].programAttribute()!!.program()!!.uid()) - .isEqualTo("U5KybNCtA3E") - assertThat(visualization.dataDimensionItems()!![5].programAttribute()!!.attribute()!!.uid()) - .isEqualTo("iggSfNDnsCw") - assertThat(visualization.categoryDimensions()!![0].category()!!.uid()).isEqualTo("fMZEcRHuamy") - assertThat(visualization.categoryDimensions()!![0].categoryOptions()!!.size).isEqualTo(2) - assertThat(visualization.categoryDimensions()!![1].category()!!.uid()).isEqualTo("fkAkrdC7eJF") - assertThat(visualization.categoryDimensions()!![1].categoryOptions()!!.size).isEqualTo(0) + assertThat(visualization.columns()?.size).isEqualTo(3) + assertThat(visualization.columns()!![0].id()).isEqualTo("dx") + assertThat(visualization.columns()!![0].items()?.size).isEqualTo(7) + assertThat(visualization.columns()!![1].id()).isEqualTo("fMZEcRHuamy") + assertThat(visualization.columns()!![1].items()?.size).isEqualTo(2) + assertThat(visualization.columns()!![2].id()).isEqualTo("fkAkrdC7eJF") + assertThat(visualization.columns()!![2].items()?.size).isEqualTo(0) + + assertThat(visualization.rows()?.size).isEqualTo(1) + assertThat(visualization.rows()!![0].id()).isEqualTo("pe") + assertThat(visualization.rows()!![0].items()?.size).isEqualTo(4) + assertThat(visualization.rows()!![0].items()!![0]?.dimensionItem()).isEqualTo("202102") + assertThat(visualization.rows()!![0].items()!![0]?.dimensionItemType()).isEqualTo("PERIOD") + + assertThat(visualization.filters()?.size).isEqualTo(1) + assertThat(visualization.filters()!![0].id()).isEqualTo("ou") + assertThat(visualization.filters()!![0].items()?.size).isEqualTo(4) } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationSimplifiedShould.kt b/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationSimplifiedShould.kt deleted file mode 100644 index 09a10da314..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationSimplifiedShould.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2004-2022, 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.android.core.visualization - -import com.google.common.truth.Truth.assertThat -import org.hisp.dhis.android.core.common.BaseObjectShould -import org.hisp.dhis.android.core.common.ObjectShould -import org.junit.Test - -class VisualizationSimplifiedShould : BaseObjectShould("visualization/visualization_simplified.json"), ObjectShould { - - @Test - override fun map_from_json_string() { - val jsonVisualization: Visualization = objectMapper.readValue(jsonStream, Visualization::class.java) - - assertThat(jsonVisualization.name()).isEqualTo("Android SDK Visualization sample") - assertThat(jsonVisualization.displayName()).isEqualTo("Android SDK Visualization sample") - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.kt b/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.kt index 40e5a65830..2aaf60ab0e 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.kt @@ -30,14 +30,12 @@ package org.hisp.dhis.android.core.visualization.internal import com.nhaarman.mockitokotlin2.* import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore -import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandler import org.hisp.dhis.android.core.common.ObjectWithUid -import org.hisp.dhis.android.core.visualization.CategoryDimension -import org.hisp.dhis.android.core.visualization.DataDimensionItem import org.hisp.dhis.android.core.visualization.Visualization -import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLink +import org.hisp.dhis.android.core.visualization.VisualizationDimension +import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -48,14 +46,9 @@ class VisualizationHandlerShould { private val visualizationStore: IdentifiableObjectStore = mock() private val visualizationCollectionCleaner: CollectionCleaner = mock() - private val dataDimensionItemStore: LinkStore = mock() - private val visualizationCategoryDimensionLinkStore: LinkStore = mock() - private val visualizationCategoryDimensionLinkHandler: - LinkHandler = mock() - private val dataDimensionItemHandler: LinkHandler = mock() - private val dataDimensionItem: DataDimensionItem = mock() - private val categoryDimension: CategoryDimension = mock() - private val category: ObjectWithUid = mock() + private val visualizationDimensionItemHandler: + LinkHandler = mock() + private val visualizationDimension: VisualizationDimension = mock() private val visualization: Visualization = mock() private var categories: List = mock() @@ -67,40 +60,20 @@ class VisualizationHandlerShould { visualizationHandler = VisualizationHandler( visualizationStore, visualizationCollectionCleaner, - visualizationCategoryDimensionLinkStore, - dataDimensionItemStore, - visualizationCategoryDimensionLinkHandler, - dataDimensionItemHandler + visualizationDimensionItemHandler ) - val dataDimensionItems = listOf(dataDimensionItem) - val categoryDimensions = listOf(categoryDimension) - categories = listOf(category) - whenever(visualization.dataDimensionItems()).doReturn(dataDimensionItems) + whenever(visualization.columns()).doReturn(listOf(visualizationDimension)) + whenever(visualization.rows()).doReturn(listOf(visualizationDimension)) + whenever(visualization.filters()).doReturn(listOf(visualizationDimension)) whenever(visualizationStore.updateOrInsert(any())).doReturn(HandleAction.Insert) whenever(visualization.uid()).doReturn("visualization_uid") - whenever(category.uid()).doReturn("category_uid") - whenever(categoryDimension.category()).doReturn(category) - whenever(visualization.categoryDimensions()).doReturn(categoryDimensions) } @Test - fun call_stores_to_delete_before_collection_handled() { + fun call_items_handler() { visualizationHandler.handleMany(listOf(visualization)) - verify(visualizationCategoryDimensionLinkStore).delete() - verify(dataDimensionItemStore).delete() - } - - @Test - fun call_data_dimension_items_handler() { - visualizationHandler.handleMany(listOf(visualization)) - verify(dataDimensionItemHandler).handleMany(any(), any(), any()) - } - - @Test - fun call_category_dimensions_link_handler() { - visualizationHandler.handleMany(listOf(visualization)) - verify(visualizationCategoryDimensionLinkHandler).handleMany(any(), any(), any()) + verify(visualizationDimensionItemHandler).handleMany(any(), any(), any()) } @Test diff --git a/core/src/test/java/org/hisp/dhis/android/testapp/visualization/DataDimensionItemPublicAccessShould.java b/core/src/test/java/org/hisp/dhis/android/testapp/visualization/DataDimensionItemPublicAccessShould.java deleted file mode 100644 index a7fb91ca6c..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/testapp/visualization/DataDimensionItemPublicAccessShould.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2004-2022, 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.android.testapp.visualization; - -import org.hisp.dhis.android.core.visualization.DataDimensionItem; -import org.hisp.dhis.android.testapp.arch.BasePublicAccessShould; -import org.mockito.Mock; - -public class DataDimensionItemPublicAccessShould extends BasePublicAccessShould { - - @Mock - private DataDimensionItem object; - - @Override - public DataDimensionItem object() { - return object; - } - - @Override - public void has_public_create_method() { - DataDimensionItem.create(null); - } - - @Override - public void has_public_builder_method() { - DataDimensionItem.builder(); - } - - @Override - public void has_public_to_builder_method() { - object().toBuilder(); - } -} \ No newline at end of file From 58dd38fe0597dbb1b2d78a1aa9edba972efad45c Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 22 May 2023 12:31:27 +1000 Subject: [PATCH 09/23] [ANDROSDK-1687] Evaluate ExpressionDimensionItems in analytic engine --- ...isualizationRepositoryIntegrationShould.kt | 6 +- .../BaseEvaluatorIntegrationShould.kt | 115 ++++++++++++ ...ataElementSQLEvaluatorIntegrationShould.kt | 21 --- ...nsionItemEvaluatorIntegrationBaseShould.kt | 166 ++++++++++++++++++ ...IndicatorEvaluatorIntegrationBaseShould.kt | 104 ----------- .../IndicatorEvaluatorIntegrationShould.kt | 39 +--- .../IndicatorSQLEvaluatorIntegrationShould.kt | 29 +-- .../AnalyticExpressionEngineFactoryHelper.kt | 73 ++++++++ .../core/analytics/AnalyticsException.kt | 1 + .../analytics/aggregated/AnalyticsModel.kt | 4 + .../AnalyticsServiceEvaluatorHelper.kt | 4 +- .../AnalyticsServiceMetadataHelper.kt | 7 +- .../ExpressionDimensionItemEvaluator.kt | 64 +++++++ .../ExpressionDimensionItemEvaluatorHelper.kt | 59 +++++++ .../AnalyticExpressionEngine.kt | 43 +++++ .../AnalyticExpressionEngineFactory.kt | 107 +++++++++++ .../AnalyticExpressionParserUtils.kt} | 7 +- .../ExpressionDimensionItemEngine.kt | 64 +++++++ .../indicatorengine/IndicatorEngine.kt | 79 ++------- .../indicatorengine/IndicatorSQLEngine.kt | 77 ++------ .../AnalyticsServiceMetadataHelperShould.kt | 3 + 21 files changed, 745 insertions(+), 327 deletions(-) create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactoryHelper.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluator.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorHelper.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngine.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactory.kt rename core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/{indicatorengine/IndicatorParserUtils.kt => analyticexpressionengine/AnalyticExpressionParserUtils.kt} (97%) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/expressiondimensionitemengine/ExpressionDimensionItemEngine.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsVisualizationRepositoryIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsVisualizationRepositoryIntegrationShould.kt index 68d36694d6..2b02b542d3 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsVisualizationRepositoryIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsVisualizationRepositoryIntegrationShould.kt @@ -48,7 +48,7 @@ class AnalyticsVisualizationRepositoryIntegrationShould : BaseMockIntegrationTes assertThat(result.dimensions.columns.size).isEqualTo(1) assertThat(result.dimensions.rows.size).isEqualTo(1) - assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(2) + assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(3) assertThat(result.dimensionItems[Dimension.OrganisationUnit]!!.size).isEqualTo(1) assertThat(result.dimensionItems[Dimension.Period]!!.size).isEqualTo(3) assertThat(result.metadata).isNotEmpty() @@ -65,7 +65,7 @@ class AnalyticsVisualizationRepositoryIntegrationShould : BaseMockIntegrationTes assertThat(result.dimensions.columns.size).isEqualTo(1) assertThat(result.dimensions.rows.size).isEqualTo(1) - assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(2) + assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(3) assertThat(result.dimensionItems[Dimension.OrganisationUnit]!!.size).isEqualTo(1) assertThat(result.dimensionItems[Dimension.Period]).isEqualTo( listOf( @@ -113,7 +113,7 @@ class AnalyticsVisualizationRepositoryIntegrationShould : BaseMockIntegrationTes assertThat(result.dimensions.columns.size).isEqualTo(1) assertThat(result.dimensions.rows.size).isEqualTo(1) - assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(2) + assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(3) assertThat(result.dimensionItems[Dimension.OrganisationUnit]).isEqualTo( listOf( DimensionItem.OrganisationUnitItem.Relative(RelativeOrganisationUnit.USER_ORGUNIT) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt index 615b152562..6fed7bb9a2 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt @@ -74,18 +74,26 @@ import org.hisp.dhis.android.core.common.RelativeOrganisationUnit import org.hisp.dhis.android.core.common.RelativePeriod import org.hisp.dhis.android.core.constant.internal.ConstantStore import org.hisp.dhis.android.core.dataelement.internal.DataElementStore +import org.hisp.dhis.android.core.datavalue.DataValue import org.hisp.dhis.android.core.datavalue.internal.DataValueStore +import org.hisp.dhis.android.core.enrollment.Enrollment import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStoreImpl +import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.event.internal.EventStoreImpl +import org.hisp.dhis.android.core.expressiondimensionitem.internal.ExpressionDimensionItemStore import org.hisp.dhis.android.core.indicator.internal.IndicatorStore import org.hisp.dhis.android.core.indicator.internal.IndicatorTypeStore import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitGroupStore import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitLevelStore import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitStore +import org.hisp.dhis.android.core.parser.internal.service.ExpressionService import org.hisp.dhis.android.core.period.internal.PeriodStoreImpl import org.hisp.dhis.android.core.program.internal.ProgramStageStore import org.hisp.dhis.android.core.program.internal.ProgramStore import org.hisp.dhis.android.core.relationship.internal.* +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue +import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance import org.hisp.dhis.android.core.trackedentity.internal.* import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyDispatcher import org.junit.After @@ -130,6 +138,15 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt protected val constantStore = ConstantStore.create(databaseAdapter) + protected val expressionDimensionItemStore = ExpressionDimensionItemStore.create(databaseAdapter) + + protected val expressionService = ExpressionService( + dataElementStore, + categoryOptionComboStore, + organisationUnitGroupStore, + programStageStore + ) + protected val metadata: Map = mapOf( orgunitParent.uid() to MetadataItem.OrganisationUnitItem(orgunitParent), orgunitChild1.uid() to MetadataItem.OrganisationUnitItem(orgunitChild1), @@ -181,6 +198,7 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt @Before fun setUpBase() { + println("AAAA setUpBase") organisationUnitLevelStore.insert(level1) organisationUnitLevelStore.insert(level2) @@ -233,6 +251,7 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt @After fun tearDown() { + println("AAAA tearDown") organisationUnitLevelStore.delete() organisationUnitStore.delete() organisationUnitGroupStore.delete() @@ -256,4 +275,100 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt relationshipConstraintStore.delete() constantStore.delete() } + + protected fun createDataValue( + value: String, + dataElementUid: String = dataElement1.uid(), + orgunitUid: String = orgunitParent.uid(), + periodId: String = period201912.periodId()!!, + categoryOptionComboUid: String = categoryOptionCombo.uid(), + attributeOptionComboUid: String = attributeOptionCombo.uid() + ) { + val dataValue = DataValue.builder() + .value(value) + .dataElement(dataElementUid) + .period(periodId) + .organisationUnit(orgunitUid) + .categoryOptionCombo(categoryOptionComboUid) + .attributeOptionCombo(attributeOptionComboUid) + .build() + + dataValueStore.insert(dataValue) + } + + protected fun createEventAndValue( + value: String, + dataElementUid: String, + enrollmentUid: String? = null + ) { + val event = Event.builder() + .uid(BaseEvaluatorSamples.generator.generate()) + .eventDate(period201912.startDate()) + .enrollment(enrollmentUid) + .program(program.uid()) + .programStage(programStage1.uid()) + .organisationUnit(orgunitChild1.uid()) + .deleted(false) + .build() + + eventStore.insert(event) + + val dataValue = TrackedEntityDataValue.builder() + .event(event.uid()) + .dataElement(dataElementUid) + .value(value) + .build() + + trackedEntityDataValueStore.insert(dataValue) + } + + protected fun createTEIAndAttribute( + value: String?, + attributeUid: String + ) { + val tei = TrackedEntityInstance.builder() + .uid(BaseEvaluatorSamples.generator.generate()) + .trackedEntityType(trackedEntityType.uid()) + .organisationUnit(orgunitChild1.uid()) + .deleted(false) + .build() + + trackedEntityStore.insert(tei) + + val enrollment = Enrollment.builder() + .uid(BaseEvaluatorSamples.generator.generate()) + .trackedEntityInstance(tei.uid()) + .organisationUnit(orgunitChild1.uid()) + .program(program.uid()) + .deleted(false) + .build() + + enrollmentStore.insert(enrollment) + + val attributeValue = TrackedEntityAttributeValue.builder() + .trackedEntityInstance(tei.uid()) + .trackedEntityAttribute(attributeUid) + .value(value) + .build() + + trackedEntityAttributeValueStore.insert(attributeValue) + createEventAndValue("0", dataElement1.uid(), enrollment.uid()) + } + + protected fun de(dataElementUid: String): String { + return "#{$dataElementUid}" + } + + protected fun eventDE(programUid: String, dataElementUid: String): String { + return "D{$programUid.$dataElementUid}" + } + + protected fun eventAtt(programUid: String, attributeUid: String): String { + return "A{$programUid.$attributeUid}" + } + + protected fun cons(constantUid: String): String { + return "C{$constantUid}" + } + } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluatorIntegrationShould.kt index 7a316557d9..7581c6e41e 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluatorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluatorIntegrationShould.kt @@ -51,7 +51,6 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEv import org.hisp.dhis.android.core.common.AggregationType import org.hisp.dhis.android.core.common.RelativeOrganisationUnit import org.hisp.dhis.android.core.common.RelativePeriod -import org.hisp.dhis.android.core.datavalue.DataValue import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.Test import org.junit.runner.RunWith @@ -454,26 +453,6 @@ internal class DataElementSQLEvaluatorIntegrationShould : BaseEvaluatorIntegrati assertThat(dataElementEvaluator.evaluate(overrideEvaluationItem, metadata)).isEqualTo("5.5") } - private fun createDataValue( - value: String, - dataElementUid: String = dataElement1.uid(), - orgunitUid: String = orgunitParent.uid(), - periodId: String = period201912.periodId()!!, - categoryOptionComboUid: String = categoryOptionCombo.uid(), - attributeOptionComboUid: String = attributeOptionCombo.uid() - ) { - val dataValue = DataValue.builder() - .value(value) - .dataElement(dataElementUid) - .period(periodId) - .organisationUnit(orgunitUid) - .categoryOptionCombo(categoryOptionComboUid) - .attributeOptionCombo(attributeOptionComboUid) - .build() - - dataValueStore.insert(dataValue) - } - private fun evaluateAggregation( periodId: String, aggregator: AggregationType diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt new file mode 100644 index 0000000000..8ff731b6d7 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.analytics.aggregated.internal.evaluator + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.attribute1 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.constant1 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.dataElement1 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.dataElement2 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.generator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.orgunitParent +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.program +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactoryHelper +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.expressiondimensionitemengine.ExpressionDimensionItemEngine +import org.hisp.dhis.android.core.common.RelativePeriod +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +internal class ExpressionDimensionItemEvaluatorIntegrationBaseShould : BaseEvaluatorIntegrationShould() { + + private lateinit var evaluator: ExpressionDimensionItemEvaluator + + @Before + fun setup() { + val engine = ExpressionDimensionItemEngine( + AnalyticExpressionEngineFactoryHelper.getFactory(d2), + expressionService + ) + + evaluator = ExpressionDimensionItemEvaluator(engine) + } + + @Test + fun should_evaluate_mathematical_expressions() { + val item = createExpression(expression = "4 * 5 / 2") + + val value = evaluateForThisMonth(item) + + assertThat(value).isEqualTo("10.0") + } + + @Test + fun should_evaluate_sum_of_data_elements() { + createDataValue("2", dataElementUid = dataElement1.uid()) + createDataValue("3", dataElementUid = dataElement2.uid()) + + val item = createExpression(expression = "${de(dataElement1.uid())} + ${de(dataElement2.uid())}") + + val value = evaluateForThisMonth(item) + + assertThat(value).isEqualTo("5.0") + } + + @Test + fun should_evaluate_days_variable() { + createDataValue("62", dataElementUid = dataElement1.uid()) + + val item = createExpression(expression = "${de(dataElement1.uid())} + [days]") + + val value = evaluateForThisMonth(item) + + assertThat(value).isEqualTo("93.0") + } + + @Test + fun should_evaluate_constants() { + createDataValue("10", dataElementUid = dataElement1.uid()) + + val item = createExpression(expression = cons(constant1.uid())) + + val value = evaluateForThisMonth(item) + + assertThat(value).isEqualTo("5.0") + } + + @Test + fun should_evaluate_event_data_elements() { + createEventAndValue("5", dataElement1.uid()) + createEventAndValue("15", dataElement1.uid()) + + val indicator = createExpression( + expression = eventDE(program.uid(), dataElement1.uid()) + ) + + val value = evaluateForThisMonth(indicator) + + assertThat(value).isEqualTo("20.0") + } + + @Test + fun should_evaluate_event_attributes() { + createTEIAndAttribute("10", attribute1.uid()) + createTEIAndAttribute("5", attribute1.uid()) + + val indicator = createExpression( + expression = eventAtt(program.uid(), attribute1.uid()) + ) + + val value = evaluateForThisMonth(indicator) + + assertThat(value).isEqualTo("15.0") + } + + private fun createExpression( + expression: String + ): ExpressionDimensionItem { + val expressionDimensionItem = ExpressionDimensionItem.builder() + .uid(generator.generate()) + .displayName("Expression Dimension Item") + .expression(expression) + .build() + + expressionDimensionItemStore.insert(expressionDimensionItem) + + return expressionDimensionItem + } + + private fun evaluateForThisMonth(expressionDimensionItem: ExpressionDimensionItem): String? { + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.ExpressionDimensionItem(expressionDimensionItem.uid()), + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()) + ), + filters = listOf( + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + return evaluator.evaluate( + evaluationItem, + metadata + + (expressionDimensionItem.uid() to MetadataItem.ExpressionDimensionItemItem(expressionDimensionItem))) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt index 488a37a571..0d12239b10 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt @@ -32,13 +32,10 @@ import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.attribute1 -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.attributeOptionCombo -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.categoryOptionCombo import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.constant1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.dataElement1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.dataElement2 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.generator -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.orgunitChild1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.orgunitParent import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201910 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201911 @@ -47,19 +44,11 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEv import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019SunW25 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period202001 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.program -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.programStage1 -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.trackedEntityType import org.hisp.dhis.android.core.common.AggregationType import org.hisp.dhis.android.core.common.ObjectWithUid import org.hisp.dhis.android.core.common.RelativePeriod -import org.hisp.dhis.android.core.datavalue.DataValue -import org.hisp.dhis.android.core.enrollment.Enrollment -import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType -import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue -import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.Test import org.junit.runner.RunWith @@ -359,97 +348,4 @@ internal abstract class IndicatorEvaluatorIntegrationBaseShould : BaseEvaluatorI return indicator } - - private fun createDataValue( - value: String, - dataElementUid: String = dataElement1.uid(), - orgunitUid: String = orgunitParent.uid(), - periodId: String = period201912.periodId()!! - ) { - val dataValue = DataValue.builder() - .value(value) - .dataElement(dataElementUid) - .period(periodId) - .organisationUnit(orgunitUid) - .categoryOptionCombo(categoryOptionCombo.uid()) - .attributeOptionCombo(attributeOptionCombo.uid()) - .build() - - dataValueStore.insert(dataValue) - } - - private fun createEventAndValue( - value: String, - dataElementUid: String, - enrollmentUid: String? = null - ) { - val event = Event.builder() - .uid(generator.generate()) - .eventDate(period201912.startDate()) - .enrollment(enrollmentUid) - .program(program.uid()) - .programStage(programStage1.uid()) - .organisationUnit(orgunitChild1.uid()) - .deleted(false) - .build() - - eventStore.insert(event) - - val dataValue = TrackedEntityDataValue.builder() - .event(event.uid()) - .dataElement(dataElementUid) - .value(value) - .build() - - trackedEntityDataValueStore.insert(dataValue) - } - - private fun createTEIAndAttribute( - value: String?, - attributeUid: String - ) { - val tei = TrackedEntityInstance.builder() - .uid(generator.generate()) - .trackedEntityType(trackedEntityType.uid()) - .organisationUnit(orgunitChild1.uid()) - .deleted(false) - .build() - - trackedEntityStore.insert(tei) - - val enrollment = Enrollment.builder() - .uid(generator.generate()) - .trackedEntityInstance(tei.uid()) - .organisationUnit(orgunitChild1.uid()) - .program(program.uid()) - .deleted(false) - .build() - - enrollmentStore.insert(enrollment) - - val attributeValue = TrackedEntityAttributeValue.builder() - .trackedEntityInstance(tei.uid()) - .trackedEntityAttribute(attributeUid) - .value(value) - .build() - - trackedEntityAttributeValueStore.insert(attributeValue) - createEventAndValue("0", dataElement1.uid(), enrollment.uid()) - } - - private fun de(dataElementUid: String): String { - return "#{$dataElementUid}" - } - - private fun eventDE(programUid: String, dataElementUid: String): String { - return "D{$programUid.$dataElementUid}" - } - - private fun eventAtt(programUid: String, attributeUid: String): String { - return "A{$programUid.$attributeUid}" - } - - private fun cons(constantUid: String): String { - return "C{$constantUid}" - } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationShould.kt index fd86b5b14b..279afdf35a 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationShould.kt @@ -27,52 +27,19 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactoryHelper import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.IndicatorEngine -import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStoreImpl -import org.hisp.dhis.android.core.constant.internal.ConstantStore -import org.hisp.dhis.android.core.dataelement.internal.DataElementStore -import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitGroupStore -import org.hisp.dhis.android.core.parser.internal.service.ExpressionService -import org.hisp.dhis.android.core.program.internal.ProgramStageStore -import org.hisp.dhis.android.core.program.internal.ProgramStore -import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLExecutor -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityAttributeStore import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.runner.RunWith @RunWith(D2JunitRunner::class) internal class IndicatorEvaluatorIntegrationShould : IndicatorEvaluatorIntegrationBaseShould() { - private val dataElementEvaluator = DataElementSQLEvaluator(databaseAdapter) - private val programIndicatorExecutor = ProgramIndicatorSQLExecutor( - ConstantStore.create(databaseAdapter), - DataElementStore.create(databaseAdapter), - TrackedEntityAttributeStore.create(databaseAdapter), - databaseAdapter - ) - private val programIndicatorEvaluator = ProgramIndicatorSQLEvaluator( - programIndicatorExecutor - ) - private val eventDataItemEvaluator = EventDataItemSQLEvaluator(databaseAdapter) - - private val expressionService = ExpressionService( - DataElementStore.create(databaseAdapter), - CategoryOptionComboStoreImpl.create(databaseAdapter), - OrganisationUnitGroupStore.create(databaseAdapter), - ProgramStageStore.create(databaseAdapter) - ) + private val analyticExpressionEngineFactory = AnalyticExpressionEngineFactoryHelper.getFactory(d2) private val indicatorEngine = IndicatorEngine( indicatorTypeStore, - DataElementStore.create(databaseAdapter), - TrackedEntityAttributeStore.create(databaseAdapter), - CategoryOptionComboStoreImpl.create(databaseAdapter), - ProgramStore.create(databaseAdapter), - d2.programModule().programIndicators(), - dataElementEvaluator, - programIndicatorEvaluator, - eventDataItemEvaluator, - ConstantStore.create(databaseAdapter), + analyticExpressionEngineFactory, expressionService ) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorSQLEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorSQLEvaluatorIntegrationShould.kt index 9ca7257f8d..b2cefbed24 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorSQLEvaluatorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorSQLEvaluatorIntegrationShould.kt @@ -27,42 +27,19 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactoryHelper import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.IndicatorSQLEngine -import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStoreImpl -import org.hisp.dhis.android.core.constant.internal.ConstantStore -import org.hisp.dhis.android.core.dataelement.internal.DataElementStore -import org.hisp.dhis.android.core.program.internal.ProgramStore -import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLExecutor -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityAttributeStore import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.runner.RunWith @RunWith(D2JunitRunner::class) internal class IndicatorSQLEvaluatorIntegrationShould : IndicatorEvaluatorIntegrationBaseShould() { - private val dataElementEvaluator = DataElementSQLEvaluator(databaseAdapter) - private val programIndicatorExecutor = ProgramIndicatorSQLExecutor( - ConstantStore.create(databaseAdapter), - DataElementStore.create(databaseAdapter), - TrackedEntityAttributeStore.create(databaseAdapter), - databaseAdapter - ) - private val programIndicatorEvaluator = ProgramIndicatorSQLEvaluator( - programIndicatorExecutor - ) - private val eventDataItemEvaluator = EventDataItemSQLEvaluator(databaseAdapter) + private val analyticExpressionEngineFactory = AnalyticExpressionEngineFactoryHelper.getFactory(d2) private val indicatorEngine = IndicatorSQLEngine( indicatorTypeStore, - DataElementStore.create(databaseAdapter), - TrackedEntityAttributeStore.create(databaseAdapter), - CategoryOptionComboStoreImpl.create(databaseAdapter), - ProgramStore.create(databaseAdapter), - d2.programModule().programIndicators(), - dataElementEvaluator, - programIndicatorEvaluator, - eventDataItemEvaluator, - ConstantStore.create(databaseAdapter), + analyticExpressionEngineFactory, databaseAdapter ) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactoryHelper.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactoryHelper.kt new file mode 100644 index 0000000000..823d504121 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactoryHelper.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine + +import org.hisp.dhis.android.core.D2 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementSQLEvaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.EventDataItemSQLEvaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.ProgramIndicatorSQLEvaluator +import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStoreImpl +import org.hisp.dhis.android.core.constant.internal.ConstantStore +import org.hisp.dhis.android.core.dataelement.internal.DataElementStore +import org.hisp.dhis.android.core.program.internal.ProgramStore +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLExecutor +import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityAttributeStore + +internal object AnalyticExpressionEngineFactoryHelper { + + fun getFactory(d2: D2): AnalyticExpressionEngineFactory { + val databaseAdapter = d2.databaseAdapter() + + val dataElementEvaluator = DataElementSQLEvaluator(databaseAdapter) + + val programIndicatorExecutor = ProgramIndicatorSQLExecutor( + ConstantStore.create(databaseAdapter), + DataElementStore.create(databaseAdapter), + TrackedEntityAttributeStore.create(databaseAdapter), + databaseAdapter + ) + + val programIndicatorEvaluator = ProgramIndicatorSQLEvaluator( + programIndicatorExecutor + ) + + val eventDataItemEvaluator = EventDataItemSQLEvaluator(databaseAdapter) + + return AnalyticExpressionEngineFactory( + DataElementStore.create(databaseAdapter), + TrackedEntityAttributeStore.create(databaseAdapter), + CategoryOptionComboStoreImpl.create(databaseAdapter), + ProgramStore.create(databaseAdapter), + d2.programModule().programIndicators(), + dataElementEvaluator, + programIndicatorEvaluator, + eventDataItemEvaluator, + ConstantStore.create(databaseAdapter), + ) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsException.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsException.kt index c4822e9d9e..d0fbfe69e7 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsException.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsException.kt @@ -39,6 +39,7 @@ sealed class AnalyticsException(message: String) : Throwable(message) { class InvalidProgramIndicator(val uid: String) : AnalyticsException("Missing ProgramIndicator $uid") class InvalidProgram(val uid: String) : AnalyticsException("Missing Program $uid") class InvalidIndicator(val uid: String) : AnalyticsException("Missing Indicator $uid") + class InvalidExpressionDimensionItem(val uid: String): AnalyticsException("Missing ExpressionDimensionItem $uid") class InvalidOrganisationUnit(val uid: String) : AnalyticsException("Missing organisation unit $uid") class InvalidOrganisationUnitGroup(val uid: String) : AnalyticsException("Missing organisation unit group $uid") class InvalidOrganisationUnitLevel(val id: String) : AnalyticsException("Missing organisation unit level $id") diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt index ab7b9e62f1..3ec7abc445 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt @@ -34,6 +34,7 @@ import org.hisp.dhis.android.core.common.RelativeOrganisationUnit import org.hisp.dhis.android.core.common.RelativePeriod import org.hisp.dhis.android.core.dataelement.DataElement import org.hisp.dhis.android.core.dataelement.DataElementOperand +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.legendset.Legend import org.hisp.dhis.android.core.organisationunit.OrganisationUnit @@ -56,6 +57,9 @@ sealed class MetadataItem(val id: String, val displayName: String) { class EventAttributeItem(val item: TrackedEntityAttribute, val program: Program) : MetadataItem("${program.uid()}.${item.uid()}", "${program.displayName()} ${item.displayName()}") + class ExpressionDimensionItemItem(val item: ExpressionDimensionItem) : + MetadataItem(item.uid(), item.displayName()!!) + class CategoryItem(val item: Category) : MetadataItem(item.uid(), item.displayName()!!) class CategoryOptionItem(val item: CategoryOption) : MetadataItem(item.uid(), item.displayName()!!) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt index 2f5b5852a8..67de329316 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt @@ -38,6 +38,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.AnalyticsEvaluator import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementSQLEvaluator import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.EventDataItemSQLEvaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.ExpressionDimensionItemEvaluator import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.IndicatorEvaluator import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.ProgramIndicatorSQLEvaluator @@ -46,6 +47,7 @@ internal class AnalyticsServiceEvaluatorHelper @Inject constructor( private val programIndicatorEvaluator: ProgramIndicatorSQLEvaluator, private val indicatorEvaluator: IndicatorEvaluator, private val eventDataItemEvaluator: EventDataItemSQLEvaluator, + private val expressionDimensionItemEvaluator: ExpressionDimensionItemEvaluator, private val legendEvaluator: LegendEvaluator ) { fun evaluate( @@ -108,7 +110,7 @@ internal class AnalyticsServiceEvaluatorHelper @Inject constructor( is DimensionItem.DataItem.ProgramIndicatorItem -> programIndicatorEvaluator is DimensionItem.DataItem.IndicatorItem -> indicatorEvaluator is DimensionItem.DataItem.EventDataItem -> eventDataItemEvaluator - is DimensionItem.DataItem.ExpressionDimensionItem -> TODO() + is DimensionItem.DataItem.ExpressionDimensionItem -> expressionDimensionItemEvaluator } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt index 75b05c62d0..170fa7e789 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt @@ -39,6 +39,7 @@ import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStore import org.hisp.dhis.android.core.common.ObjectWithUid import org.hisp.dhis.android.core.dataelement.DataElement import org.hisp.dhis.android.core.dataelement.DataElementOperand +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.legendset.Legend import org.hisp.dhis.android.core.organisationunit.OrganisationUnit @@ -57,6 +58,7 @@ internal class AnalyticsServiceMetadataHelper @Inject constructor( private val categoryOptionComboStore: CategoryOptionComboStore, private val dataElementStore: IdentifiableObjectStore, private val indicatorStore: IdentifiableObjectStore, + private val expressionDimensionItemStore: IdentifiableObjectStore, private val legendStore: IdentifiableObjectStore, private val organisationUnitStore: IdentifiableObjectStore, private val organisationUnitGroupStore: IdentifiableObjectStore, @@ -168,7 +170,10 @@ internal class AnalyticsServiceMetadataHelper @Inject constructor( } is DimensionItem.DataItem.ExpressionDimensionItem -> { - TODO() + val expressionItem = expressionDimensionItemStore.selectByUid(item.uid) + ?: throw AnalyticsException.InvalidExpressionDimensionItem(item.uid) + + MetadataItem.ExpressionDimensionItemItem(expressionItem) } } ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluator.kt new file mode 100644 index 0000000000..12113e9286 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluator.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.analytics.aggregated.internal.evaluator + +import org.hisp.dhis.android.core.analytics.AnalyticsException +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.expressiondimensionitemengine.ExpressionDimensionItemEngine +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods + +internal class ExpressionDimensionItemEvaluator @Inject constructor( + private val expressionDimensionItemEngine: ExpressionDimensionItemEngine +) : AnalyticsEvaluator { + + override fun evaluate( + evaluationItem: AnalyticsServiceEvaluationItem, + metadata: Map, + queryMods: QueryMods?, + ): String? { + val item = ExpressionDimensionItemEvaluatorHelper.getItem(evaluationItem, metadata) + val contextEvaluationItem = ExpressionDimensionItemEvaluatorHelper + .getContextEvaluationItem(evaluationItem, item) + + return expressionDimensionItemEngine.evaluateIndicator( + expressionDimensionItem = item, + contextEvaluationItem = contextEvaluationItem, + contextMetadata = metadata + ) + } + + override fun getSql( + evaluationItem: AnalyticsServiceEvaluationItem, + metadata: Map, + queryMods: QueryMods?, + ): String { + throw AnalyticsException.SQLException("Method getSql not implemented for ExpressionDimensionItemEvaluator") + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorHelper.kt new file mode 100644 index 0000000000..4091d00a10 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorHelper.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.analytics.aggregated.internal.evaluator + +import org.hisp.dhis.android.core.analytics.AnalyticsException +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem + +internal object ExpressionDimensionItemEvaluatorHelper { + + fun getItem( + evaluationItem: AnalyticsServiceEvaluationItem, + metadata: Map + ): ExpressionDimensionItem { + val expressionDimensionItem = evaluationItem.allDimensionItems + .find { it is DimensionItem.DataItem.ExpressionDimensionItem } + ?: throw AnalyticsException.InvalidArguments("Invalid arguments: no expression dimension item provided.") + + return (metadata[expressionDimensionItem.id] as MetadataItem.ExpressionDimensionItemItem).item + } + + fun getContextEvaluationItem( + evaluationItem: AnalyticsServiceEvaluationItem, + item: ExpressionDimensionItem + ): AnalyticsServiceEvaluationItem { + return AnalyticsServiceEvaluationItem( + dimensionItems = evaluationItem.dimensionItems.filter { (it as DimensionItem).id != item.uid() }, + filters = evaluationItem.filters.filter { it.id != item.uid() }, + aggregationType = evaluationItem.aggregationType + ) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngine.kt new file mode 100644 index 0000000000..ef1398a12b --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngine.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.CommonParser +import javax.inject.Inject + +internal class AnalyticExpressionEngine @Inject constructor( + private val visitor: CommonExpressionVisitor +) { + + fun evaluate( + expression: String, + ): Any? { + return CommonParser.visit(expression, visitor) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactory.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactory.kt new file mode 100644 index 0000000000..19c7e0dc9c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactory.kt @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine + +import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementSQLEvaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.EventDataItemSQLEvaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.ProgramIndicatorSQLEvaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.IndicatorContext +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.arch.helpers.UidsHelper.mapByUid +import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStore +import org.hisp.dhis.android.core.constant.Constant +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitorScope +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItemMethod +import org.hisp.dhis.android.core.program.ProgramIndicatorCollectionRepository +import org.hisp.dhis.android.core.program.internal.ProgramStoreInterface +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute +import javax.inject.Inject + +internal class AnalyticExpressionEngineFactory @Inject constructor( + private val dataElementStore: IdentifiableObjectStore, + private val trackedEntityAttributeStore: IdentifiableObjectStore, + private val categoryOptionComboStore: CategoryOptionComboStore, + private val programStore: ProgramStoreInterface, + private val programIndicatorRepository: ProgramIndicatorCollectionRepository, + private val dataElementEvaluator: DataElementSQLEvaluator, + private val programIndicatorEvaluator: ProgramIndicatorSQLEvaluator, + private val eventDataItemEvaluator: EventDataItemSQLEvaluator, + private val constantStore: IdentifiableObjectStore +) { + + fun getEngine( + method: ExpressionItemMethod, + contextEvaluationItem: AnalyticsServiceEvaluationItem, + contextMetadata: Map, + days: Int? + ): AnalyticExpressionEngine { + val indicatorContext = IndicatorContext( + dataElementStore = dataElementStore, + trackedEntityAttributeStore = trackedEntityAttributeStore, + categoryOptionComboStore = categoryOptionComboStore, + programStore = programStore, + programIndicatorRepository = programIndicatorRepository, + dataElementEvaluator = dataElementEvaluator, + programIndicatorEvaluator = programIndicatorEvaluator, + eventDataItemEvaluator = eventDataItemEvaluator, + evaluationItem = contextEvaluationItem, + contextMetadata = contextMetadata + ) + + val visitor = newVisitor(indicatorContext, method) + + days?.let { + visitor.days = it.toDouble() + } + + return AnalyticExpressionEngine(visitor) + } + + private val constantMap: Map + get() { + val constants = constantStore.selectAll() + return mapByUid(constants) + } + + private fun newVisitor(indicatorContext: IndicatorContext, + method: ExpressionItemMethod + ): CommonExpressionVisitor { + return CommonExpressionVisitor( + CommonExpressionVisitorScope.AnalyticsIndicator( + itemMap = AnalyticExpressionParserUtils.ANALYTIC_EXPRESSION_ITEMS, + itemMethod = method, + constantMap = constantMap, + indicatorContext = indicatorContext + ) + ) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionParserUtils.kt similarity index 97% rename from core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt rename to core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionParserUtils.kt index c5de2fcbd6..8b95968d87 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionParserUtils.kt @@ -26,7 +26,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine +package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem @@ -36,7 +36,6 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indica import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramDataElementItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramIndicatorItem import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils -import org.hisp.dhis.android.core.parser.internal.expression.function.* import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionAggregationType import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionMaxDate import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionMinDate @@ -77,9 +76,9 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -internal object IndicatorParserUtils { +internal object AnalyticExpressionParserUtils { - val INDICATOR_EXPRESSION_ITEMS = ParserUtils.COMMON_EXPRESSION_ITEMS + + val ANALYTIC_EXPRESSION_ITEMS = ParserUtils.COMMON_EXPRESSION_ITEMS + mapOf( ExpressionParser.D2_CEIL to D2Ceil(), ExpressionParser.D2_CONCATENATE to D2Concatenate(), diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/expressiondimensionitemengine/ExpressionDimensionItemEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/expressiondimensionitemengine/ExpressionDimensionItemEngine.kt new file mode 100644 index 0000000000..16953869a0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/expressiondimensionitemengine/ExpressionDimensionItemEngine.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.analytics.aggregated.internal.evaluator.expressiondimensionitemengine + +import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactory +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionParserUtils +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.parser.internal.service.ExpressionService +import javax.inject.Inject + +internal class ExpressionDimensionItemEngine @Inject constructor( + private val analyticExpressionEngineFactory: AnalyticExpressionEngineFactory, + private val expressionService: ExpressionService +) { + + fun evaluateIndicator( + expressionDimensionItem: ExpressionDimensionItem, + contextEvaluationItem: AnalyticsServiceEvaluationItem, + contextMetadata: Map + ): String? { + val engine = analyticExpressionEngineFactory.getEngine( + method = ParserUtils.ITEM_EVALUATE, + contextEvaluationItem = contextEvaluationItem, + contextMetadata = contextMetadata, + days = AnalyticExpressionParserUtils.getDays(contextEvaluationItem, contextMetadata) + ) + + val evaluatedExpression = engine.evaluate(expressionDimensionItem.expression()!!) + + return if (evaluatedExpression != null ) { + expressionService.getExpressionValue(evaluatedExpression.toString())?.toString() + } else { + null + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt index c552ddfa99..f625006249 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt @@ -27,40 +27,20 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine -import javax.inject.Inject import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementSQLEvaluator -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.EventDataItemSQLEvaluator -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.ProgramIndicatorSQLEvaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactory +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionParserUtils import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore -import org.hisp.dhis.android.core.arch.helpers.UidsHelper.mapByUid -import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStore -import org.hisp.dhis.android.core.constant.Constant -import org.hisp.dhis.android.core.dataelement.DataElement import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitorScope -import org.hisp.dhis.android.core.parser.internal.expression.CommonParser import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.parser.internal.service.ExpressionService -import org.hisp.dhis.android.core.program.ProgramIndicatorCollectionRepository -import org.hisp.dhis.android.core.program.internal.ProgramStoreInterface -import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute +import javax.inject.Inject -@Suppress("LongParameterList") internal class IndicatorEngine @Inject constructor( private val indicatorTypeStore: IdentifiableObjectStore, - private val dataElementStore: IdentifiableObjectStore, - private val trackedEntityAttributeStore: IdentifiableObjectStore, - private val categoryOptionComboStore: CategoryOptionComboStore, - private val programStore: ProgramStoreInterface, - private val programIndicatorRepository: ProgramIndicatorCollectionRepository, - private val dataElementEvaluator: DataElementSQLEvaluator, - private val programIndicatorEvaluator: ProgramIndicatorSQLEvaluator, - private val eventDataItemEvaluator: EventDataItemSQLEvaluator, - private val constantStore: IdentifiableObjectStore, + private val analyticExpressionEngineFactory: AnalyticExpressionEngineFactory, private val expressionService: ExpressionService ) { @@ -69,61 +49,28 @@ internal class IndicatorEngine @Inject constructor( contextEvaluationItem: AnalyticsServiceEvaluationItem, contextMetadata: Map ): String? { - val indicatorContext = IndicatorContext( - dataElementStore = dataElementStore, - trackedEntityAttributeStore = trackedEntityAttributeStore, - categoryOptionComboStore = categoryOptionComboStore, - programStore = programStore, - programIndicatorRepository = programIndicatorRepository, - dataElementEvaluator = dataElementEvaluator, - programIndicatorEvaluator = programIndicatorEvaluator, - eventDataItemEvaluator = eventDataItemEvaluator, - evaluationItem = contextEvaluationItem, - contextMetadata = contextMetadata - ) - val indicatorType = indicator.indicatorType()?.let { indicatorTypeStore.selectByUid(it.uid()) } - val visitor = newVisitor(indicatorContext) - - IndicatorParserUtils.getDays(contextEvaluationItem, contextMetadata)?.let { - visitor.days = it.toDouble() - } + val engine = analyticExpressionEngineFactory.getEngine( + method = ParserUtils.ITEM_EVALUATE, + contextEvaluationItem = contextEvaluationItem, + contextMetadata = contextMetadata, + days = AnalyticExpressionParserUtils.getDays(contextEvaluationItem, contextMetadata) + ) - val numerator = evaluate(indicator.numerator()!!, visitor) - val denominator = evaluate(indicator.denominator()!!, visitor) + val numerator = engine.evaluate(indicator.numerator()!!) + val denominator = engine.evaluate(indicator.denominator()!!) return if (numerator != null && denominator != null) { val formula = "${indicatorType?.factor() ?: 1} * $numerator / $denominator" expressionService.getExpressionValue(formula)?.let { val valueStr = it.toString() - IndicatorParserUtils.roundValue(valueStr, indicator.decimals()) + AnalyticExpressionParserUtils.roundValue(valueStr, indicator.decimals()) } } else { null } } - - private fun evaluate(expression: String, visitor: CommonExpressionVisitor): Any? { - return CommonParser.visit(expression, visitor) - } - - private val constantMap: Map - get() { - val constants = constantStore.selectAll() - return mapByUid(constants) - } - - private fun newVisitor(indicatorContext: IndicatorContext): CommonExpressionVisitor { - return CommonExpressionVisitor( - CommonExpressionVisitorScope.AnalyticsIndicator( - itemMap = IndicatorParserUtils.INDICATOR_EXPRESSION_ITEMS, - itemMethod = ParserUtils.ITEM_EVALUATE, - constantMap = constantMap, - indicatorContext = indicatorContext - ) - ) - } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt index 55fc9d1b3a..c50013c375 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt @@ -27,40 +27,20 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine -import javax.inject.Inject import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementSQLEvaluator -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.EventDataItemSQLEvaluator -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.ProgramIndicatorSQLEvaluator +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactory +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionParserUtils import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore -import org.hisp.dhis.android.core.arch.helpers.UidsHelper.mapByUid -import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStore -import org.hisp.dhis.android.core.constant.Constant -import org.hisp.dhis.android.core.dataelement.DataElement import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitorScope -import org.hisp.dhis.android.core.parser.internal.expression.CommonParser import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils -import org.hisp.dhis.android.core.program.ProgramIndicatorCollectionRepository -import org.hisp.dhis.android.core.program.internal.ProgramStoreInterface -import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute +import javax.inject.Inject -@Suppress("LongParameterList") internal class IndicatorSQLEngine @Inject constructor( private val indicatorTypeStore: IdentifiableObjectStore, - private val dataElementStore: IdentifiableObjectStore, - private val trackedEntityAttributeStore: IdentifiableObjectStore, - private val categoryOptionComboStore: CategoryOptionComboStore, - private val programStore: ProgramStoreInterface, - private val programIndicatorRepository: ProgramIndicatorCollectionRepository, - private val dataElementEvaluator: DataElementSQLEvaluator, - private val programIndicatorEvaluator: ProgramIndicatorSQLEvaluator, - private val eventDataItemEvaluator: EventDataItemSQLEvaluator, - private val constantStore: IdentifiableObjectStore, + private val analyticExpressionEngineFactory: AnalyticExpressionEngineFactory, private val databaseAdapter: DatabaseAdapter ) { @@ -74,7 +54,7 @@ internal class IndicatorSQLEngine @Inject constructor( return databaseAdapter.rawQuery(sqlQuery)?.use { c -> c.moveToFirst() val valueStr = c.getString(0) - IndicatorParserUtils.roundValue(valueStr, indicator.decimals()) + AnalyticExpressionParserUtils.roundValue(valueStr, indicator.decimals()) } } @@ -83,54 +63,21 @@ internal class IndicatorSQLEngine @Inject constructor( contextEvaluationItem: AnalyticsServiceEvaluationItem, contextMetadata: Map ): String { - val indicatorContext = IndicatorContext( - dataElementStore = dataElementStore, - trackedEntityAttributeStore = trackedEntityAttributeStore, - categoryOptionComboStore = categoryOptionComboStore, - programStore = programStore, - programIndicatorRepository = programIndicatorRepository, - dataElementEvaluator = dataElementEvaluator, - programIndicatorEvaluator = programIndicatorEvaluator, - eventDataItemEvaluator = eventDataItemEvaluator, - evaluationItem = contextEvaluationItem, - contextMetadata = contextMetadata + val engine = analyticExpressionEngineFactory.getEngine( + method = ParserUtils.ITEM_GET_SQL, + contextEvaluationItem = contextEvaluationItem, + contextMetadata = contextMetadata, + days = AnalyticExpressionParserUtils.getDays(contextEvaluationItem, contextMetadata) ) val indicatorType = indicator.indicatorType()?.let { indicatorTypeStore.selectByUid(it.uid()) } - val visitor = newVisitor(indicatorContext) - - IndicatorParserUtils.getDays(contextEvaluationItem, contextMetadata)?.let { - visitor.days = it.toDouble() - } - - val numerator = evaluate(indicator.numerator()!!, visitor) - val denominator = evaluate(indicator.denominator()!!, visitor) + val numerator = engine.evaluate(indicator.numerator()!!) + val denominator = engine.evaluate(indicator.denominator()!!) val factor = indicatorType?.factor() ?: 1 return "SELECT $factor * ($numerator) / ($denominator)" } - - private fun evaluate(expression: String, visitor: CommonExpressionVisitor): Any? { - return CommonParser.visit(expression, visitor) - } - - private val constantMap: Map - get() { - val constants = constantStore.selectAll() - return mapByUid(constants) - } - - private fun newVisitor(indicatorContext: IndicatorContext): CommonExpressionVisitor { - return CommonExpressionVisitor( - CommonExpressionVisitorScope.AnalyticsIndicator( - itemMap = IndicatorParserUtils.INDICATOR_EXPRESSION_ITEMS, - itemMethod = ParserUtils.ITEM_GET_SQL, - constantMap = constantMap, - indicatorContext = indicatorContext - ) - ) - } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelperShould.kt index 56f3c64dfb..c7703f7414 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelperShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelperShould.kt @@ -38,6 +38,7 @@ import org.hisp.dhis.android.core.category.Category import org.hisp.dhis.android.core.category.CategoryOption import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStore import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.legendset.Legend import org.hisp.dhis.android.core.organisationunit.OrganisationUnit @@ -60,6 +61,7 @@ class AnalyticsServiceMetadataHelperShould { private val dataElementStore: IdentifiableObjectStore = mock() private val categoryOptionComboStore: CategoryOptionComboStore = mock() private val indicatorStore: IdentifiableObjectStore = mock() + private val expressionDimensionItemStore: IdentifiableObjectStore = mock() private val legendStore: IdentifiableObjectStore = mock() private val organisationUnitStore: IdentifiableObjectStore = mock() private val organisationUnitGroupStore: IdentifiableObjectStore = mock() @@ -77,6 +79,7 @@ class AnalyticsServiceMetadataHelperShould { categoryOptionComboStore, dataElementStore, indicatorStore, + expressionDimensionItemStore, legendStore, organisationUnitStore, organisationUnitGroupStore, From 62dae5cd058f1b3c670f2799c0fd0fb9c6661704 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 22 May 2023 13:01:17 +1000 Subject: [PATCH 10/23] [ANDROSDK-1687] Add ExpressionDimensionItem repository; checks --- .../BaseEvaluatorIntegrationShould.kt | 1 - ...nsionItemEvaluatorIntegrationBaseShould.kt | 3 +- ...sionDimensionItemStoreIntegrationShould.kt | 14 +- ...llectionRepositoryMockIntegrationShould.kt | 54 ++++ ...llectionRepositoryMockIntegrationShould.kt | 6 +- .../java/org/hisp/dhis/android/core/D2.kt | 5 + .../core/analytics/AnalyticsException.kt | 2 +- .../ExpressionDimensionItemEvaluator.kt | 2 +- .../AnalyticExpressionEngine.kt | 2 +- .../AnalyticExpressionEngineFactory.kt | 7 +- .../ExpressionDimensionItemEngine.kt | 4 +- .../indicatorengine/IndicatorEngine.kt | 2 +- .../indicatorengine/IndicatorSQLEngine.kt | 2 +- .../core/arch/d2/internal/D2Modules.kt | 2 + ...sionDimensionItemCollectionRepository.java | 62 +++++ .../ExpressionDimensionItemModule.kt | 32 +++ .../ExpressionDimensionItemTableInfo.kt | 5 +- .../internal/ExpressionDimensionItemCall.kt | 20 +- .../ExpressionDimensionItemEntityDIModule.kt | 7 + .../internal/ExpressionDimensionItemFields.kt | 8 +- .../ExpressionDimensionItemHandler.kt | 2 +- .../ExpressionDimensionItemModuleImpl.kt | 41 +++ .../ExpressionDimensionItemModuleWiper.kt | 4 +- .../ExpressionDimensionItemPackageDIModule.kt | 12 +- .../internal/ExpressionDimensionItemStore.kt | 6 +- .../ExpressionDimensionItemUidsSeeker.kt | 2 +- .../indicator/internal/IndicatorUidsSeeker.kt | 3 +- .../internal/ProgramIndicatorUidsSeeker.kt | 3 +- .../core/visualization/Visualization.java | 1 + .../core/visualization/VisualizationAPI36.kt | 6 +- .../VisualizationDimensionItemTableInfo.kt | 19 +- .../internal/VisualizationDimensionFields.kt | 14 +- .../VisualizationDimensionItemFields.kt | 12 +- .../VisualizationDimensionItemStore.kt | 4 +- .../internal/VisualizationHandler.kt | 8 +- .../ExpressionDimensionItemSamples.kt | 2 +- .../VisualizationDimensionItemSamples.kt | 16 +- ...ualizationsServiceDimensionHelperShould.kt | 246 ++++++++++-------- .../ExpressionDimensionItemShould.kt | 4 +- .../internal/VisualizationHandlerShould.kt | 2 - 40 files changed, 444 insertions(+), 203 deletions(-) create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/expressiondimensionitem/ExpressionDimensionItemCollectionRepositoryMockIntegrationShould.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemCollectionRepository.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemModule.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleImpl.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt index 6fed7bb9a2..996a9eca31 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt @@ -370,5 +370,4 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt protected fun cons(constantUid: String): String { return "C{$constantUid}" } - } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt index 8ff731b6d7..860f940e61 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt @@ -161,6 +161,7 @@ internal class ExpressionDimensionItemEvaluatorIntegrationBaseShould : BaseEvalu return evaluator.evaluate( evaluationItem, metadata + - (expressionDimensionItem.uid() to MetadataItem.ExpressionDimensionItemItem(expressionDimensionItem))) + (expressionDimensionItem.uid() to MetadataItem.ExpressionDimensionItemItem(expressionDimensionItem)) + ) } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemStoreIntegrationShould.kt index fdfb891697..d7ac6e7212 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemStoreIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemStoreIntegrationShould.kt @@ -36,11 +36,11 @@ import org.junit.runner.RunWith @RunWith(D2JunitRunner::class) class ExpressionDimensionItemStoreIntegrationShould : - IdentifiableObjectStoreAbstractIntegrationShould( - ExpressionDimensionItemStore.create(TestDatabaseAdapterFactory.get()), - ExpressionDimensionItemTableInfo.TABLE_INFO, - TestDatabaseAdapterFactory.get() - ) { + IdentifiableObjectStoreAbstractIntegrationShould( + ExpressionDimensionItemStore.create(TestDatabaseAdapterFactory.get()), + ExpressionDimensionItemTableInfo.TABLE_INFO, + TestDatabaseAdapterFactory.get() + ) { override fun buildObject(): ExpressionDimensionItem { return ExpressionDimensionItemSamples.getExpressionDimensionItem() @@ -48,7 +48,7 @@ class ExpressionDimensionItemStoreIntegrationShould : override fun buildObjectToUpdate(): ExpressionDimensionItem { return ExpressionDimensionItemSamples.getExpressionDimensionItem().toBuilder() - .expression("#{fbfJHSPpUQD}") - .build() + .expression("#{fbfJHSPpUQD}") + .build() } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/expressiondimensionitem/ExpressionDimensionItemCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/expressiondimensionitem/ExpressionDimensionItemCollectionRepositoryMockIntegrationShould.kt new file mode 100644 index 0000000000..71a23b99a2 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/expressiondimensionitem/ExpressionDimensionItemCollectionRepositoryMockIntegrationShould.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2004-2022, 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.android.testapp.expressiondimensionitem + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class ExpressionDimensionItemCollectionRepositoryMockIntegrationShould : BaseMockIntegrationTestFullDispatcher() { + @Test + fun find_all() { + val items = d2.expressionDimensionItemModule().expressionDimensionItems() + .blockingGet() + + assertThat(items.size).isEqualTo(1) + } + + @Test + fun filter_by_expression() { + val items = d2.expressionDimensionItemModule().expressionDimensionItems() + .byExpression().like("g9eOBujte1U") + .blockingGet() + + assertThat(items.size).isEqualTo(1) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt index ec6a83ae38..b9ba2a1fab 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, 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 diff --git a/core/src/main/java/org/hisp/dhis/android/core/D2.kt b/core/src/main/java/org/hisp/dhis/android/core/D2.kt index 50dcf95030..4a6663cf4d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/D2.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/D2.kt @@ -43,6 +43,7 @@ import org.hisp.dhis.android.core.datavalue.DataValueModule import org.hisp.dhis.android.core.domain.aggregated.AggregatedModule import org.hisp.dhis.android.core.enrollment.EnrollmentModule import org.hisp.dhis.android.core.event.EventModule +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemModule import org.hisp.dhis.android.core.fileresource.FileResourceModule import org.hisp.dhis.android.core.imports.internal.ImportModule import org.hisp.dhis.android.core.indicator.IndicatorModule @@ -149,6 +150,10 @@ class D2 internal constructor(internal val d2DIComponent: D2DIComponent) { return modules.event } + fun expressionDimensionItemModule(): ExpressionDimensionItemModule { + return modules.expressionDimensionItem + } + fun fileResourceModule(): FileResourceModule { return modules.fileResource } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsException.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsException.kt index d0fbfe69e7..5837a7de7d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsException.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsException.kt @@ -39,7 +39,7 @@ sealed class AnalyticsException(message: String) : Throwable(message) { class InvalidProgramIndicator(val uid: String) : AnalyticsException("Missing ProgramIndicator $uid") class InvalidProgram(val uid: String) : AnalyticsException("Missing Program $uid") class InvalidIndicator(val uid: String) : AnalyticsException("Missing Indicator $uid") - class InvalidExpressionDimensionItem(val uid: String): AnalyticsException("Missing ExpressionDimensionItem $uid") + class InvalidExpressionDimensionItem(val uid: String) : AnalyticsException("Missing ExpressionDimensionItem $uid") class InvalidOrganisationUnit(val uid: String) : AnalyticsException("Missing organisation unit $uid") class InvalidOrganisationUnitGroup(val uid: String) : AnalyticsException("Missing organisation unit group $uid") class InvalidOrganisationUnitLevel(val id: String) : AnalyticsException("Missing organisation unit level $id") diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluator.kt index 12113e9286..f9005f17a6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluator.kt @@ -27,8 +27,8 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator -import org.hisp.dhis.android.core.analytics.AnalyticsException import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.AnalyticsException import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.expressiondimensionitemengine.ExpressionDimensionItemEngine diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngine.kt index ef1398a12b..19624bdfd6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngine.kt @@ -27,9 +27,9 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine +import javax.inject.Inject import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor import org.hisp.dhis.android.core.parser.internal.expression.CommonParser -import javax.inject.Inject internal class AnalyticExpressionEngine @Inject constructor( private val visitor: CommonExpressionVisitor diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactory.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactory.kt index 19c7e0dc9c..35f7af6060 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactory.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/analyticexpressionengine/AnalyticExpressionEngineFactory.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine +import javax.inject.Inject import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementSQLEvaluator @@ -44,7 +45,6 @@ import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItemMetho import org.hisp.dhis.android.core.program.ProgramIndicatorCollectionRepository import org.hisp.dhis.android.core.program.internal.ProgramStoreInterface import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute -import javax.inject.Inject internal class AnalyticExpressionEngineFactory @Inject constructor( private val dataElementStore: IdentifiableObjectStore, @@ -92,8 +92,9 @@ internal class AnalyticExpressionEngineFactory @Inject constructor( return mapByUid(constants) } - private fun newVisitor(indicatorContext: IndicatorContext, - method: ExpressionItemMethod + private fun newVisitor( + indicatorContext: IndicatorContext, + method: ExpressionItemMethod ): CommonExpressionVisitor { return CommonExpressionVisitor( CommonExpressionVisitorScope.AnalyticsIndicator( diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/expressiondimensionitemengine/ExpressionDimensionItemEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/expressiondimensionitemengine/ExpressionDimensionItemEngine.kt index 16953869a0..9453ccefb8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/expressiondimensionitemengine/ExpressionDimensionItemEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/expressiondimensionitemengine/ExpressionDimensionItemEngine.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.expressiondimensionitemengine +import javax.inject.Inject import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactory @@ -34,7 +35,6 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyt import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.parser.internal.service.ExpressionService -import javax.inject.Inject internal class ExpressionDimensionItemEngine @Inject constructor( private val analyticExpressionEngineFactory: AnalyticExpressionEngineFactory, @@ -55,7 +55,7 @@ internal class ExpressionDimensionItemEngine @Inject constructor( val evaluatedExpression = engine.evaluate(expressionDimensionItem.expression()!!) - return if (evaluatedExpression != null ) { + return if (evaluatedExpression != null) { expressionService.getExpressionValue(evaluatedExpression.toString())?.toString() } else { null diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt index f625006249..2d5dc7a02f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine +import javax.inject.Inject import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactory @@ -36,7 +37,6 @@ import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.parser.internal.service.ExpressionService -import javax.inject.Inject internal class IndicatorEngine @Inject constructor( private val indicatorTypeStore: IdentifiableObjectStore, diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt index c50013c375..ae967c1900 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine +import javax.inject.Inject import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.analyticexpressionengine.AnalyticExpressionEngineFactory @@ -36,7 +37,6 @@ import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStor import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils -import javax.inject.Inject internal class IndicatorSQLEngine @Inject constructor( private val indicatorTypeStore: IdentifiableObjectStore, diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt index bdea094b4c..168e9d8753 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt @@ -38,6 +38,7 @@ import org.hisp.dhis.android.core.datastore.DataStoreModule import org.hisp.dhis.android.core.datavalue.DataValueModule import org.hisp.dhis.android.core.enrollment.EnrollmentModule import org.hisp.dhis.android.core.event.EventModule +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemModule import org.hisp.dhis.android.core.fileresource.FileResourceModule import org.hisp.dhis.android.core.imports.internal.ImportModule import org.hisp.dhis.android.core.indicator.IndicatorModule @@ -71,6 +72,7 @@ internal class D2Modules @Inject constructor( val dataValue: DataValueModule, val enrollment: EnrollmentModule, val event: EventModule, + val expressionDimensionItem: ExpressionDimensionItemModule, val fileResource: FileResourceModule, val importModule: ImportModule, val indicator: IndicatorModule, diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemCollectionRepository.java new file mode 100644 index 0000000000..45b1bdcae5 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemCollectionRepository.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem; + +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; +import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; +import org.hisp.dhis.android.core.arch.repositories.collection.internal.ReadOnlyIdentifiableCollectionRepositoryImpl; +import org.hisp.dhis.android.core.arch.repositories.filters.internal.FilterConnectorFactory; +import org.hisp.dhis.android.core.arch.repositories.filters.internal.StringFilterConnector; +import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope; +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemTableInfo.Columns; + +import java.util.Map; + +import javax.inject.Inject; + +import dagger.Reusable; + +@Reusable +public final class ExpressionDimensionItemCollectionRepository + extends ReadOnlyIdentifiableCollectionRepositoryImpl { + + @Inject + ExpressionDimensionItemCollectionRepository( + final IdentifiableObjectStore store, + final Map> childrenAppenders, + final RepositoryScope scope) { + super(store, childrenAppenders, scope, new FilterConnectorFactory<>(scope, + s -> new ExpressionDimensionItemCollectionRepository(store, childrenAppenders, s))); + } + + public StringFilterConnector byExpression() { + return cf.string(Columns.EXPRESSION); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemModule.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemModule.kt new file mode 100644 index 0000000000..82513d03e6 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemModule.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem + +interface ExpressionDimensionItemModule { + fun expressionDimensionItems(): ExpressionDimensionItemCollectionRepository +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemTableInfo.kt index 1b0ba49d49..83e3afbd88 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemTableInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemTableInfo.kt @@ -45,8 +45,9 @@ object ExpressionDimensionItemTableInfo { class Columns : IdentifiableColumns() { override fun all(): Array { - return CollectionsHelper.appendInNewArray(super.all(), - EXPRESSION + return CollectionsHelper.appendInNewArray( + super.all(), + EXPRESSION ) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemCall.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemCall.kt index df0147e613..26b579b63b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemCall.kt @@ -29,28 +29,28 @@ package org.hisp.dhis.android.core.expressiondimensionitem.internal import dagger.Reusable import io.reactivex.Single +import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.APIDownloader import org.hisp.dhis.android.core.arch.call.factories.internal.UidsCall import org.hisp.dhis.android.core.arch.handlers.internal.Handler import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem -import javax.inject.Inject @Reusable internal class ExpressionDimensionItemCall @Inject constructor( - private val service: ExpressionDimensionItemService, - private val handler: Handler, - private val apiDownloader: APIDownloader + private val service: ExpressionDimensionItemService, + private val handler: Handler, + private val apiDownloader: APIDownloader ) : UidsCall { override fun download(uids: Set): Single> { return apiDownloader.downloadPartitioned( - uids, - MAX_UID_LIST_SIZE, - handler + uids, + MAX_UID_LIST_SIZE, + handler ) { partitionUids: Set -> service.getExpressionDimensionItems( - ExpressionDimensionItemFields.uid.`in`(partitionUids), - ExpressionDimensionItemFields.allFields, - false + ExpressionDimensionItemFields.uid.`in`(partitionUids), + ExpressionDimensionItemFields.allFields, + false ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemEntityDIModule.kt index a214e94ed2..47db4cab44 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemEntityDIModule.kt @@ -33,6 +33,7 @@ import dagger.Reusable import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem import retrofit2.Retrofit @@ -56,4 +57,10 @@ internal class ExpressionDimensionItemEntityDIModule { fun service(retrofit: Retrofit): ExpressionDimensionItemService { return retrofit.create(ExpressionDimensionItemService::class.java) } + + @Reusable + @Provides + fun childrenAppenders(): Map> { + return emptyMap() + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemFields.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemFields.kt index d370f98222..9d1fc0c766 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemFields.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemFields.kt @@ -38,7 +38,7 @@ internal object ExpressionDimensionItemFields { val uid = fh.uid() val allFields: Fields = Fields.builder() - .fields(fh.getIdentifiableFields()) - .fields(fh.field(Columns.EXPRESSION)) - .build() -} \ No newline at end of file + .fields(fh.getIdentifiableFields()) + .fields(fh.field(Columns.EXPRESSION)) + .build() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemHandler.kt index 4b1d025eac..fa1672312f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemHandler.kt @@ -28,10 +28,10 @@ package org.hisp.dhis.android.core.expressiondimensionitem.internal import dagger.Reusable +import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItem -import javax.inject.Inject @Reusable internal class ExpressionDimensionItemHandler @Inject constructor( diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleImpl.kt new file mode 100644 index 0000000000..aeb0b95ba4 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleImpl.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.expressiondimensionitem.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemCollectionRepository +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemModule + +@Reusable +internal class ExpressionDimensionItemModuleImpl @Inject internal constructor( + private val expressionDimensionItems: ExpressionDimensionItemCollectionRepository +) : ExpressionDimensionItemModule { + + override fun expressionDimensionItems(): ExpressionDimensionItemCollectionRepository = expressionDimensionItems +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleWiper.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleWiper.kt index 5a1a8f7d0c..6c59e536c9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleWiper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemModuleWiper.kt @@ -28,10 +28,10 @@ package org.hisp.dhis.android.core.expressiondimensionitem.internal import dagger.Reusable +import javax.inject.Inject import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemTableInfo import org.hisp.dhis.android.core.wipe.internal.ModuleWiper import org.hisp.dhis.android.core.wipe.internal.TableWiper -import javax.inject.Inject @Reusable internal class ExpressionDimensionItemModuleWiper @Inject constructor( @@ -44,4 +44,4 @@ internal class ExpressionDimensionItemModuleWiper @Inject constructor( override fun wipeData() { // No data to wipe } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemPackageDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemPackageDIModule.kt index 004d8cceaa..2d6f962757 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemPackageDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemPackageDIModule.kt @@ -28,10 +28,20 @@ package org.hisp.dhis.android.core.expressiondimensionitem.internal import dagger.Module +import dagger.Provides +import dagger.Reusable +import org.hisp.dhis.android.core.expressiondimensionitem.ExpressionDimensionItemModule @Module( includes = [ ExpressionDimensionItemEntityDIModule::class ] ) -internal class ExpressionDimensionItemPackageDIModule \ No newline at end of file +internal class ExpressionDimensionItemPackageDIModule { + + @Provides + @Reusable + fun module(impl: ExpressionDimensionItemModuleImpl): ExpressionDimensionItemModule { + return impl + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemStore.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemStore.kt index ae8550ab29..5e1dfd6b71 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemStore.kt @@ -49,9 +49,9 @@ internal object ExpressionDimensionItemStore { fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { return objectWithUidStore( - databaseAdapter, - ExpressionDimensionItemTableInfo.TABLE_INFO, - BINDER + databaseAdapter, + ExpressionDimensionItemTableInfo.TABLE_INFO, + BINDER ) { cursor: Cursor -> ExpressionDimensionItem.create(cursor) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemUidsSeeker.kt index 386137da58..abb0b79ddd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemUidsSeeker.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/internal/ExpressionDimensionItemUidsSeeker.kt @@ -29,11 +29,11 @@ package org.hisp.dhis.android.core.expressiondimensionitem.internal import dagger.Reusable +import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.uidseeker.internal.BaseUidsSeeker import org.hisp.dhis.android.core.visualization.DimensionItemType import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo -import javax.inject.Inject @Reusable internal class ExpressionDimensionItemUidsSeeker @Inject constructor( diff --git a/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt index 7bac1c8ade..3bbabdb625 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt @@ -28,6 +28,7 @@ package org.hisp.dhis.android.core.indicator.internal import dagger.Reusable +import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.querybuilders.internal.MultipleTableQueryBuilder import org.hisp.dhis.android.core.arch.db.uidseeker.internal.BaseUidsSeeker @@ -35,7 +36,6 @@ import org.hisp.dhis.android.core.dataset.SectionIndicatorLinkTableInfo import org.hisp.dhis.android.core.indicator.DataSetIndicatorLinkTableInfo import org.hisp.dhis.android.core.visualization.DimensionItemType import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo -import javax.inject.Inject @Reusable internal class IndicatorUidsSeeker @Inject constructor( @@ -55,7 +55,6 @@ internal class IndicatorUidsSeeker @Inject constructor( "WHERE ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM_TYPE} = " + "'${DimensionItemType.PROGRAM_INDICATOR.name}'" - return readSingleColumnResults(tablesQuery) + readSingleColumnResults(visualizationQuery) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramIndicatorUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramIndicatorUidsSeeker.kt index 4316aaf409..d0927988c2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramIndicatorUidsSeeker.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramIndicatorUidsSeeker.kt @@ -29,12 +29,11 @@ package org.hisp.dhis.android.core.program.internal import dagger.Reusable +import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.MultipleTableQueryBuilder import org.hisp.dhis.android.core.arch.db.uidseeker.internal.BaseUidsSeeker import org.hisp.dhis.android.core.visualization.DimensionItemType import org.hisp.dhis.android.core.visualization.VisualizationDimensionItemTableInfo -import javax.inject.Inject @Reusable internal class ProgramIndicatorUidsSeeker @Inject constructor( diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java index 7cef61d14b..6b43b1a44e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java @@ -53,6 +53,7 @@ @AutoValue @JsonDeserialize(builder = $$AutoValue_Visualization.Builder.class) +@SuppressWarnings({"PMD.ExcessivePublicCount"}) public abstract class Visualization extends BaseIdentifiableObject implements CoreObject { @Nullable diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationAPI36.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationAPI36.kt index f6b50e8701..9561258c4c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationAPI36.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationAPI36.kt @@ -116,8 +116,8 @@ internal data class VisualizationAPI36( .showKey(false) .build() ) - .columns(columns) - .rows(rows) - .filters(filters) + .columns(columns) + .rows(rows) + .filters(filters) .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItemTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItemTableInfo.kt index 2c1755f282..8a835b99da 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItemTableInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItemTableInfo.kt @@ -49,21 +49,22 @@ object VisualizationDimensionItemTableInfo { override fun all(): Array { return CollectionsHelper.appendInNewArray( super.all(), - VISUALIZATION, - POSITION, - DIMENSION, - DIMENSION_ITEM, - DIMENSION_ITEM_TYPE, ) + VISUALIZATION, + POSITION, + DIMENSION, + DIMENSION_ITEM, + DIMENSION_ITEM_TYPE, + ) } override fun whereUpdate(): Array { return CollectionsHelper.appendInNewArray( super.all(), VISUALIZATION, - POSITION, - DIMENSION, - DIMENSION_ITEM, - DIMENSION_ITEM_TYPE, + POSITION, + DIMENSION, + DIMENSION_ITEM, + DIMENSION_ITEM_TYPE, ) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionFields.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionFields.kt index c3fda892d1..6ba17c9850 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionFields.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionFields.kt @@ -39,11 +39,11 @@ internal object VisualizationDimensionFields { private val fh = FieldsHelper() val allFields: Fields = - Fields.builder() - .fields( - fh.field(BaseIdentifiableObject.UID), - fh.nestedField(ITEMS) - .with(VisualizationDimensionItemFields.allFields), - ) - .build() + Fields.builder() + .fields( + fh.field(BaseIdentifiableObject.UID), + fh.nestedField(ITEMS) + .with(VisualizationDimensionItemFields.allFields), + ) + .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemFields.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemFields.kt index 369438947f..8f99d66df2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemFields.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemFields.kt @@ -38,10 +38,10 @@ internal object VisualizationDimensionItemFields { private val fh = FieldsHelper() val allFields: Fields = - Fields.builder() - .fields( - fh.field(DIMENSION_ITEM), - fh.field(DIMENSION_ITEM_TYPE), - ) - .build() + Fields.builder() + .fields( + fh.field(DIMENSION_ITEM), + fh.field(DIMENSION_ITEM_TYPE), + ) + .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStore.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStore.kt index b53c529196..ac1728f272 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDimensionItemStore.kt @@ -48,8 +48,8 @@ internal object VisualizationDimensionItemStore { fun create(databaseAdapter: DatabaseAdapter): LinkStore { return StoreFactory.linkStore( databaseAdapter, - VisualizationDimensionItemTableInfo.TABLE_INFO, - VisualizationDimensionItemTableInfo.Columns.VISUALIZATION, + VisualizationDimensionItemTableInfo.TABLE_INFO, + VisualizationDimensionItemTableInfo.Columns.VISUALIZATION, BINDER ) { VisualizationDimensionItem.create(it) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt index 41ca3a3311..e00adc163e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt @@ -28,6 +28,7 @@ package org.hisp.dhis.android.core.visualization.internal import dagger.Reusable +import javax.inject.Inject import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction @@ -37,7 +38,6 @@ import org.hisp.dhis.android.core.visualization.LayoutPosition import org.hisp.dhis.android.core.visualization.Visualization import org.hisp.dhis.android.core.visualization.VisualizationDimension import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem -import javax.inject.Inject @Reusable internal class VisualizationHandler @Inject constructor( @@ -61,8 +61,10 @@ internal class VisualizationHandler @Inject constructor( visualizationCollectionCleaner.deleteNotPresent(oCollection) } - private fun toItems(dimensions: List?, - position: LayoutPosition): List { + private fun toItems( + dimensions: List?, + position: LayoutPosition + ): List { return dimensions?.map { dimension -> if (dimension.items().isNullOrEmpty()) { listOf( diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/expressiondimensionitem/ExpressionDimensionItemSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/expressiondimensionitem/ExpressionDimensionItemSamples.kt index eabca08060..e7e0ddd054 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/expressiondimensionitem/ExpressionDimensionItemSamples.kt +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/expressiondimensionitem/ExpressionDimensionItemSamples.kt @@ -38,7 +38,7 @@ object ExpressionDimensionItemSamples { fillIdentifiableProperties(builder) return builder .id(1L) - .expression("#{fbfJHSPpUQD}+#{cYeuwXTCPkU}") + .expression("#{fbfJHSPpUQD}+#{cYeuwXTCPkU}") .build() } } diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationDimensionItemSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationDimensionItemSamples.kt index a03c9d8161..79674078b6 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationDimensionItemSamples.kt +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationDimensionItemSamples.kt @@ -33,12 +33,12 @@ import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem object VisualizationDimensionItemSamples { fun visualizationDimensionItem(): VisualizationDimensionItem = - VisualizationDimensionItem.builder() - .id(1L) - .visualization("visualization_uid") - .position(LayoutPosition.COLUMN) - .dimension("dx") - .dimensionItem("item") - .dimensionItemType("DATA_ELEMENT") - .build() + VisualizationDimensionItem.builder() + .id(1L) + .visualization("visualization_uid") + .position(LayoutPosition.COLUMN) + .dimension("dx") + .dimensionItem("item") + .dimensionItemType("DATA_ELEMENT") + .build() } diff --git a/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt index a69923b694..628e6ca030 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt @@ -71,12 +71,14 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val dataDimensions = listOf( VisualizationDimension.builder() .id("dx") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid1) - .dimensionItemType(DimensionItemType.DATA_ELEMENT.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.DATA_ELEMENT.name) + .build() + ) + ) .build() ) @@ -96,12 +98,14 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val dataDimensions = listOf( VisualizationDimension.builder() .id("dx") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem("$uid1.$uid2") - .dimensionItemType(DimensionItemType.DATA_ELEMENT_OPERAND.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem("$uid1.$uid2") + .dimensionItemType(DimensionItemType.DATA_ELEMENT_OPERAND.name) + .build() + ) + ) .build() ) @@ -123,12 +127,14 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val dataDimensions = listOf( VisualizationDimension.builder() .id("dx") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid1) - .dimensionItemType(DimensionItemType.INDICATOR.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.INDICATOR.name) + .build() + ) + ) .build() ) @@ -148,12 +154,14 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val dataDimensions = listOf( VisualizationDimension.builder() .id("dx") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid1) - .dimensionItemType(DimensionItemType.PROGRAM_INDICATOR.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.PROGRAM_INDICATOR.name) + .build() + ) + ) .build() ) @@ -173,12 +181,14 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val dataDimensions = listOf( VisualizationDimension.builder() .id("dx") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem("$uid1.$uid2") - .dimensionItemType(DimensionItemType.PROGRAM_DATA_ELEMENT.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem("$uid1.$uid2") + .dimensionItemType(DimensionItemType.PROGRAM_DATA_ELEMENT.name) + .build() + ) + ) .build() ) @@ -200,12 +210,14 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val dataDimensions = listOf( VisualizationDimension.builder() .id("dx") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem("$uid1.$uid2") - .dimensionItemType(DimensionItemType.PROGRAM_ATTRIBUTE.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem("$uid1.$uid2") + .dimensionItemType(DimensionItemType.PROGRAM_ATTRIBUTE.name) + .build() + ) + ) .build() ) @@ -227,12 +239,14 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val dataDimensions = listOf( VisualizationDimension.builder() .id("dx") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid1) - .dimensionItemType(DimensionItemType.EXPRESSION_DIMENSION_ITEM.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.EXPRESSION_DIMENSION_ITEM.name) + .build() + ) + ) .build() ) @@ -253,19 +267,21 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val orgunitDimensions = listOf( VisualizationDimension.builder() .id("ou") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid1) - .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem(uid2) - .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem("LEVEL-1") - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(uid2) + .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem("LEVEL-1") + .build() + ) + ) .build() ) @@ -288,17 +304,19 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val orgunitDimensions = listOf( VisualizationDimension.builder() .id("ou") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT_CHILDREN.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT_GRANDCHILDREN.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT_CHILDREN.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativeOrganisationUnit.USER_ORGUNIT_GRANDCHILDREN.name) + .build() + ) + ) .build() ) @@ -319,24 +337,26 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val periodDimensions = listOf( VisualizationDimension.builder() .id("pe") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid1) - .dimensionItemType(DimensionItemType.PERIOD.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem(uid2) - .dimensionItemType(DimensionItemType.PERIOD.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem(RelativePeriod.THIS_MONTH.name) - .dimensionItemType(DimensionItemType.PERIOD.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem(RelativePeriod.LAST_MONTH.name) - .dimensionItemType(DimensionItemType.PERIOD.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(uid2) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativePeriod.THIS_MONTH.name) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(RelativePeriod.LAST_MONTH.name) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build() + ) + ) .build() ) @@ -356,16 +376,18 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val categoryDimensions = listOf( VisualizationDimension.builder() .id(uid1) - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid2) - .dimensionItemType(DimensionItemType.CATEGORY_OPTION.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem(uid3) - .dimensionItemType(DimensionItemType.CATEGORY_OPTION.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid2) + .dimensionItemType(DimensionItemType.CATEGORY_OPTION.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(uid3) + .dimensionItemType(DimensionItemType.CATEGORY_OPTION.name) + .build() + ) + ) .build() ) @@ -386,25 +408,29 @@ class AnalyticsVisualizationsServiceDimensionHelperShould { val dimensions = listOf( VisualizationDimension.builder() .id("pe") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid1) - .dimensionItemType(DimensionItemType.PERIOD.name) - .build(), - VisualizationDimensionItem.builder() - .dimensionItem(uid2) - .dimensionItemType(DimensionItemType.PERIOD.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid1) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build(), + VisualizationDimensionItem.builder() + .dimensionItem(uid2) + .dimensionItemType(DimensionItemType.PERIOD.name) + .build() + ) + ) .build(), VisualizationDimension.builder() .id("ou") - .items(listOf( - VisualizationDimensionItem.builder() - .dimensionItem(uid3) - .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) - .build() - )) + .items( + listOf( + VisualizationDimensionItem.builder() + .dimensionItem(uid3) + .dimensionItemType(DimensionItemType.ORGANISATION_UNIT.name) + .build() + ) + ) .build() ) diff --git a/core/src/test/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemShould.kt b/core/src/test/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemShould.kt index 6a1f3f1490..bed00d8ba6 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItemShould.kt @@ -34,8 +34,8 @@ import org.hisp.dhis.android.core.common.ObjectShould import org.junit.Test class ExpressionDimensionItemShould : - BaseObjectShould("expressiondimensionitem/expression_dimension_item.json"), - ObjectShould { + BaseObjectShould("expressiondimensionitem/expression_dimension_item.json"), + ObjectShould { @Test override fun map_from_json_string() { diff --git a/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.kt b/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.kt index 2aaf60ab0e..add391cc94 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.kt @@ -32,7 +32,6 @@ import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandler -import org.hisp.dhis.android.core.common.ObjectWithUid import org.hisp.dhis.android.core.visualization.Visualization import org.hisp.dhis.android.core.visualization.VisualizationDimension import org.hisp.dhis.android.core.visualization.VisualizationDimensionItem @@ -50,7 +49,6 @@ class VisualizationHandlerShould { LinkHandler = mock() private val visualizationDimension: VisualizationDimension = mock() private val visualization: Visualization = mock() - private var categories: List = mock() // object to test private lateinit var visualizationHandler: VisualizationHandler From 65853253822879b175e038e8c1623e7381272e70 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 23 May 2023 11:45:49 +1000 Subject: [PATCH 11/23] [ANDROSDK-1687] Adapt migration --- core/src/main/assets/migrations/146.sql | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/core/src/main/assets/migrations/146.sql b/core/src/main/assets/migrations/146.sql index 5a9a502e33..57dfb69604 100644 --- a/core/src/main/assets/migrations/146.sql +++ b/core/src/main/assets/migrations/146.sql @@ -2,17 +2,7 @@ CREATE TABLE ExpressionDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, expression TEXT); -CREATE TABLE VisualizationDimensionItem(_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, position TEXT NOT NULL, dimension TEXT NOT NULL, dimensionItem TEXT NOT NULL, dimensionItemType TEXT, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); - -ALTER TABLE Visualization RENAME TO Visualization_old; +DROP TABLE IF EXISTS Visualization; CREATE TABLE Visualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, displayDescription TEXT, displayFormName TEXT, title TEXT, displayTitle TEXT, subtitle TEXT, displaySubtitle TEXT, type TEXT, hideTitle INTEGER, hideSubtitle INTEGER, hideEmptyColumns INTEGER, hideEmptyRows INTEGER, hideEmptyRowItems TEXT, hideLegend INTEGER, showHierarchy INTEGER, rowTotals INTEGER, rowSubTotals INTEGER, colTotals INTEGER, colSubTotals INTEGER, showDimensionLabels INTEGER, percentStackedValues INTEGER, noSpaceBetweenColumns INTEGER, skipRounding INTEGER, displayDensity TEXT, digitGroupSeparator TEXT, legendShowKey TEXT, legendStyle TEXT, legendSetId TEXT, legendStrategy TEXT, aggregationType TEXT); -INSERT INTO Visualization (_id, uid, code, name, displayName, created, lastUpdated, description, displayDescription, displayFormName, title, displayTitle, subtitle, displaySubtitle, type, hideTitle, hideSubtitle, hideEmptyColumns, hideEmptyRows, hideEmptyRowItems, hideLegend, showHierarchy, rowTotals, rowSubTotals, colTotals, colSubTotals, showDimensionLabels, percentStackedValues, noSpaceBetweenColumns, skipRounding, displayDensity, digitGroupSeparator, legendShowKey, legendStyle, legendSetId, legendStrategy, aggregationType) SELECT _id, uid, code, name, displayName, created, lastUpdated, description, displayDescription, displayFormName, title, displayTitle, subtitle, displaySubtitle, type, hideTitle, hideSubtitle, hideEmptyColumns, hideEmptyRows, hideEmptyRowItems, hideLegend, showHierarchy, rowTotals, rowSubTotals, colTotals, colSubTotals, showDimensionLabels, percentStackedValues, noSpaceBetweenColumns, skipRounding, displayDensity, digitGroupSeparator, legendShowKey, legendStyle, legendSetId, legendStrategy, aggregationType FROM Visualization_old; -DROP TABLE IF EXISTS Visualization_old; -DROP TABLE IF EXISTS VisualizationCategoryDimensionLink; -DROP TABLE IF EXISTS DataDimensionItem; - -# TO DELETE -#CREATE TABLE Visualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, displayDescription TEXT, displayFormName TEXT, type TEXT, hideTitle INTEGER, hideSubtitle INTEGER, hideEmptyColumns INTEGER, hideEmptyRows INTEGER, hideEmptyRowItems TEXT, hideLegend INTEGER, showHierarchy INTEGER, rowTotals INTEGER, rowSubTotals INTEGER, colTotals INTEGER, colSubTotals INTEGER, showDimensionLabels INTEGER, percentStackedValues INTEGER, noSpaceBetweenColumns INTEGER, skipRounding INTEGER, displayDensity TEXT, digitGroupSeparator TEXT, relativePeriods TEXT, filterDimensions TEXT, rowDimensions TEXT, columnDimensions TEXT, organisationUnitLevels TEXT, userOrganisationUnit INTEGER, userOrganisationUnitChildren INTEGER, userOrganisationUnitGrandChildren INTEGER, organisationUnits TEXT, periods TEXT, title TEXT, displayTitle TEXT, subtitle TEXT, displaySubtitle TEXT, legendShowKey TEXT, legendStyle TEXT, legendSetId TEXT, legendStrategy TEXT, aggregationType TEXT); -#CREATE TABLE VisualizationCategoryDimensionLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, category TEXT NOT NULL, categoryOption TEXT, FOREIGN KEY (category) REFERENCES Category (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryOption) REFERENCES CategoryOption (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -#CREATE TABLE DataDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, dataDimensionItemType TEXT, indicator TEXT, dataElement TEXT, dataElementOperand TEXT, reportingRate TEXT, programIndicator TEXT, programDataElement TEXT, programAttribute TEXT, validationRule TEXT, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE VisualizationDimensionItem(_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, position TEXT NOT NULL, dimension TEXT NOT NULL, dimensionItem TEXT NOT NULL, dimensionItemType TEXT, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); From b6a44a1cd01c7af41cbda741b4b8aea2b282cebb Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 23 May 2023 12:04:24 +1000 Subject: [PATCH 12/23] [ANDROSDK-1687] Refactor metadata test class to Kotlin --- ...a => MetadataCallRealIntegrationShould.kt} | 87 +++++++++---------- 1 file changed, 41 insertions(+), 46 deletions(-) rename core/src/androidTest/java/org/hisp/dhis/android/core/{MetadataCallRealIntegrationShould.java => MetadataCallRealIntegrationShould.kt} (60%) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.kt similarity index 60% rename from core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.java rename to core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.kt index 9b0bcd45dd..253c9bae3d 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.kt @@ -25,21 +25,20 @@ * (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.android.core -package org.hisp.dhis.android.core; +import android.util.Log +import io.reactivex.schedulers.Schedulers +import org.hisp.dhis.android.core.arch.call.D2Progress +import org.hisp.dhis.android.core.user.User -import android.util.Log; - -import io.reactivex.schedulers.Schedulers; - -public class MetadataCallRealIntegrationShould extends BaseRealIntegrationTest { +class MetadataCallRealIntegrationShould : BaseRealIntegrationTest() { /** * A quick integration test that is probably flaky, but will help with finding bugs related to * the * metadataSyncCall. It works against the demo server. */ - - /* How to extract database from tests: + /* How to extract database from tests: edit: AbsStoreTestCase.java (adding database name.) DbOpenHelper dbOpenHelper = new DbOpenHelper(InstrumentationRegistry.getTargetContext() .getApplicationContext(), @@ -53,59 +52,55 @@ make a debugger break point where desired (after sync complete) in datagrip: pragma foreign_keys = on; pragma foreign_key_check;*/ + // This test is uncommented because technically it is flaky. + // It depends on a live server to operate and the login is hardcoded here. + // Uncomment in order to quickly test changes vs a real server, but keep it uncommented after. - //This test is uncommented because technically it is flaky. - //It depends on a live server to operate and the login is hardcoded here. - //Uncomment in order to quickly test changes vs a real server, but keep it uncommented after. - //@Test - public void response_successful_on_sync_meta_data_once() throws Exception { - d2.userModule().logIn(username, password, url).blockingGet(); + // @Test + fun response_successful_on_sync_meta_data_once() { + d2.userModule().logIn(username, password, url).blockingGet() - d2.metadataModule().blockingDownload(); + d2.metadataModule().blockingDownload() - //TODO: add additional sync + break point. - //when debugger stops at the new break point manually change metadata online & resume. - //This way I can make sure that additive (updates) work as well. - //The changes could be to one of the programs, adding stuff to it. + // TODO: add additional sync + break point. + // when debugger stops at the new break point manually change metadata online & resume. + // This way I can make sure that additive (updates) work as well. + // The changes could be to one of the programs, adding stuff to it. // adding a new program..etc. } - //@Test - public void download_metadata_in_io_scheduler() throws Exception { + // @Test + fun download_metadata_in_io_scheduler() { d2.userModule().logIn(username, password, url) - .flatMapObservable(user -> d2.metadataModule().download()) - .subscribeOn(Schedulers.io()) - .subscribe(progress -> Log.i("META", progress.lastCall())); + .flatMapObservable { user: User? -> d2.metadataModule().download() } + .subscribeOn(Schedulers.io()) + .subscribe { progress: D2Progress -> Log.i("META", progress.lastCall()!!) } - Thread.sleep(60000); + Thread.sleep(60000) } - //@Test - public void response_successful_on_sync_meta_data_two_times() throws Exception { - d2.userModule().logIn(username, password, url).blockingGet(); + // @Test + fun response_successful_on_sync_meta_data_two_times() { + d2.userModule().logIn(username, password, url).blockingGet() - //first sync: - d2.metadataModule().blockingDownload(); + // first sync: + d2.metadataModule().blockingDownload() - //second sync: - d2.metadataModule().blockingDownload(); + // second sync: + d2.metadataModule().blockingDownload() } - //@Test - public void response_successful_on_login_wipe_db_and_login() throws Exception { - d2.userModule().logIn(username, password, url).blockingGet(); - - d2.wipeModule().wipeEverything(); - - d2.userModule().logIn(username, password, url).blockingGet(); + // @Test + fun response_successful_on_login_wipe_db_and_login() { + d2.userModule().logIn(username, password, url).blockingGet() + d2.wipeModule().wipeEverything() + d2.userModule().logIn(username, password, url).blockingGet() } - //@Test - public void response_successful_on_login_logout_and_login() throws Exception { - d2.userModule().logIn(username, password, url).blockingGet(); - - d2.userModule().logOut().blockingAwait(); - - d2.userModule().logIn(username, password, url).blockingGet(); + // @Test + fun response_successful_on_login_logout_and_login() { + d2.userModule().logIn(username, password, url).blockingGet() + d2.userModule().logOut().blockingAwait() + d2.userModule().logIn(username, password, url).blockingGet() } } From 1d4a817ec6b98d340519ea9d3b40f995a1e05e1f Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 23 May 2023 12:21:29 +1000 Subject: [PATCH 13/23] [ANDROSDK-1687] Evaluate LegendSets for expressionDimensionItems --- .../org/hisp/dhis/android/core/analytics/LegendEvaluator.kt | 1 - .../aggregated/internal/AnalyticsServiceEvaluatorHelper.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/LegendEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/LegendEvaluator.kt index dc611b119f..20b279e7e2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/LegendEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/LegendEvaluator.kt @@ -83,7 +83,6 @@ internal class LegendEvaluator @Inject constructor( } } - @Suppress("UnusedPrivateMember", "FunctionOnlyReturningConstant") fun getLegendByTrackedEntityAttribute( trackedEntityAttributeUid: String, value: String? diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt index 67de329316..038c92caed 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt @@ -145,7 +145,7 @@ internal class AnalyticsServiceEvaluatorHelper @Inject constructor( dimensionDataItem.attribute, value ) - is DimensionItem.DataItem.ExpressionDimensionItem -> TODO() + is DimensionItem.DataItem.ExpressionDimensionItem -> null } } } From 42da7df232e935982fe2d98a6122b15104b1c56e Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 23 May 2023 13:10:12 +1000 Subject: [PATCH 14/23] [ANDROSDK-1687] Fix migration --- core/src/main/assets/migrations/146.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/assets/migrations/146.sql b/core/src/main/assets/migrations/146.sql index 57dfb69604..1eca836719 100644 --- a/core/src/main/assets/migrations/146.sql +++ b/core/src/main/assets/migrations/146.sql @@ -3,6 +3,8 @@ CREATE TABLE ExpressionDimensionItem (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, expression TEXT); DROP TABLE IF EXISTS Visualization; -CREATE TABLE Visualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, displayDescription TEXT, displayFormName TEXT, title TEXT, displayTitle TEXT, subtitle TEXT, displaySubtitle TEXT, type TEXT, hideTitle INTEGER, hideSubtitle INTEGER, hideEmptyColumns INTEGER, hideEmptyRows INTEGER, hideEmptyRowItems TEXT, hideLegend INTEGER, showHierarchy INTEGER, rowTotals INTEGER, rowSubTotals INTEGER, colTotals INTEGER, colSubTotals INTEGER, showDimensionLabels INTEGER, percentStackedValues INTEGER, noSpaceBetweenColumns INTEGER, skipRounding INTEGER, displayDensity TEXT, digitGroupSeparator TEXT, legendShowKey TEXT, legendStyle TEXT, legendSetId TEXT, legendStrategy TEXT, aggregationType TEXT); +DROP TABLE IF EXISTS VisualizationCategoryDimensionLink; +DROP TABLE IF EXISTS DataDimensionItem; +CREATE TABLE Visualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, displayDescription TEXT, displayFormName TEXT, title TEXT, displayTitle TEXT, subtitle TEXT, displaySubtitle TEXT, type TEXT, hideTitle INTEGER, hideSubtitle INTEGER, hideEmptyColumns INTEGER, hideEmptyRows INTEGER, hideEmptyRowItems TEXT, hideLegend INTEGER, showHierarchy INTEGER, rowTotals INTEGER, rowSubTotals INTEGER, colTotals INTEGER, colSubTotals INTEGER, showDimensionLabels INTEGER, percentStackedValues INTEGER, noSpaceBetweenColumns INTEGER, skipRounding INTEGER, displayDensity TEXT, digitGroupSeparator TEXT, legendShowKey TEXT, legendStyle TEXT, legendSetId TEXT, legendStrategy TEXT, aggregationType TEXT); CREATE TABLE VisualizationDimensionItem(_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, position TEXT NOT NULL, dimension TEXT NOT NULL, dimensionItem TEXT NOT NULL, dimensionItemType TEXT, FOREIGN KEY (visualization) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); From 452563ccffe854a1afad49d39da109441e153c86 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 23 May 2023 20:52:36 +1000 Subject: [PATCH 15/23] [ANDROSDK-1687] Fix code smells --- .../core/expressiondimensionitem/ExpressionDimensionItem.java | 2 +- .../android/core/visualization/VisualizationDimension.java | 2 +- .../core/visualization/VisualizationDimensionItem.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItem.java b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItem.java index 125b432bf6..5996390d66 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItem.java +++ b/core/src/main/java/org/hisp/dhis/android/core/expressiondimensionitem/ExpressionDimensionItem.java @@ -60,7 +60,7 @@ public static ExpressionDimensionItem create(Cursor cursor) { @AutoValue.Builder @JsonPOJOBuilder(withPrefix = "") - public static abstract class Builder extends BaseIdentifiableObject.Builder { + public abstract static class Builder extends BaseIdentifiableObject.Builder { public abstract Builder id(Long id); public abstract Builder expression(String expression); diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimension.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimension.java index 9cc2cd27d0..fdf530915e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimension.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimension.java @@ -57,7 +57,7 @@ public static Builder builder() { @AutoValue.Builder @JsonPOJOBuilder(withPrefix = "") - public static abstract class Builder { + public abstract static class Builder { public abstract Builder id(String id); diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItem.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItem.java index 45b0f89e2b..8009a5c915 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItem.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationDimensionItem.java @@ -69,14 +69,14 @@ public static Builder builder() { } public static VisualizationDimensionItem create(Cursor cursor) { - return AutoValue_VisualizationDimensionItem.createFromCursor(cursor); + return $AutoValue_VisualizationDimensionItem.createFromCursor(cursor); } public abstract Builder toBuilder(); @AutoValue.Builder @JsonPOJOBuilder(withPrefix = "") - public static abstract class Builder { + public abstract static class Builder { public abstract Builder id(Long id); From e8088a409e9122db5502ed6ad88781fe868625f8 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 29 May 2023 17:15:04 +1000 Subject: [PATCH 16/23] [ANDROSDK-1687] Create single query per visualization --- .../VisualizationEndpointCallShould.kt | 3 +- ...llectionRepositoryMockIntegrationShould.kt | 2 +- .../core/mockwebserver/Dhis2MockServer.java | 12 +- .../internal/VisualizationCall.kt | 35 ++-- .../internal/VisualizationService.kt | 27 ++- .../visualization/visualizations.json | 197 ------------------ .../visualization/visualizations_1.json | 108 ++++++++++ .../visualization/visualizations_2.json | 85 ++++++++ 8 files changed, 242 insertions(+), 227 deletions(-) delete mode 100644 core/src/sharedTest/resources/visualization/visualizations.json create mode 100644 core/src/sharedTest/resources/visualization/visualizations_1.json create mode 100644 core/src/sharedTest/resources/visualization/visualizations_2.json diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEndpointCallShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEndpointCallShould.kt index d9f3719078..b6f29d0796 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEndpointCallShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEndpointCallShould.kt @@ -65,7 +65,8 @@ class VisualizationEndpointCallShould : BaseMockIntegrationTestEmptyEnqueable() dhis2MockServer.enqueueLoginResponses() d2.userModule().blockingLogIn("u1", "pass1", dhis2MockServer.baseEndpoint) - dhis2MockServer.enqueueMockResponse("visualization/visualizations.json") + dhis2MockServer.enqueueMockResponse("visualization/visualizations_1.json") + dhis2MockServer.enqueueMockResponse("visualization/visualizations_2.json") d2.databaseAdapter().setForeignKeyConstraintsEnabled(false) var visualizations = visualizationsSingle.blockingGet() diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt index b9ba2a1fab..f3756bd684 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.kt @@ -174,7 +174,7 @@ class VisualizationCollectionRepositoryMockIntegrationShould : BaseMockIntegrati fun include_columns_rows_and_filters_as_children() { val visualization = d2.visualizationModule().visualizations() .withColumnsRowsAndFilters() - .one() + .uid("PYBH8ZaAQnC") .blockingGet() assertThat(visualization.columns()!![0].id()).isEqualTo("dx") diff --git a/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java b/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java index 01edb89444..fa12ae9c76 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java +++ b/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java @@ -92,7 +92,8 @@ public class Dhis2MockServer { private static final String CATEGORIES_JSON = "category/categories.json"; private static final String CATEGORY_OPTIONS_JSON = "category/category_options.json"; private static final String CATEGORY_OPTION_ORGUNITS_JSON = "category/category_option_orgunits.json"; - private static final String VISUALIZATIONS_JSON = "visualization/visualizations.json"; + private static final String VISUALIZATIONS_1_JSON = "visualization/visualizations_1.json"; + private static final String VISUALIZATIONS_2_JSON = "visualization/visualizations_2.json"; private static final String ORGANISATION_UNIT_LEVELS_JSON = "organisationunit/organisation_unit_levels.json"; private static final String CONSTANTS_JSON = "constant/constants.json"; private static final String USER_JSON = "user/user38.json"; @@ -260,8 +261,10 @@ public MockResponse dispatch(RecordedRequest request) { return createMockResponse(CATEGORY_OPTIONS_JSON); } else if (path.startsWith("/api/categoryOptions/orgUnits?")) { return createMockResponse(CATEGORY_OPTION_ORGUNITS_JSON); - } else if (path.startsWith("/api/visualizations?")) { - return createMockResponse(VISUALIZATIONS_JSON); + } else if (path.startsWith("/api/visualizations/PYBH8ZaAQnC?")) { + return createMockResponse(VISUALIZATIONS_1_JSON); + } else if (path.startsWith("/api/visualizations/FAFa11yFeFe?")) { + return createMockResponse(VISUALIZATIONS_2_JSON); } else if (path.startsWith("/api/organisationUnits?")) { return createMockResponse(ORGANISATION_UNITS_JSON); } else if (path.startsWith("/api/organisationUnitLevels?")) { @@ -359,7 +362,8 @@ public void enqueueMetadataResponses() { enqueueMockResponse(CATEGORIES_JSON); enqueueMockResponse(CATEGORY_OPTIONS_JSON); enqueueMockResponse(CATEGORY_OPTION_ORGUNITS_JSON); - enqueueMockResponse(VISUALIZATIONS_JSON); + enqueueMockResponse(VISUALIZATIONS_1_JSON); + enqueueMockResponse(VISUALIZATIONS_2_JSON); enqueueMockResponse(PROGRAMS_INDICATORS_JSON); enqueueMockResponse(PROGRAMS_INDICATORS_JSON); enqueueMockResponse(INDICATORS_JSON); diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCall.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCall.kt index 6e33a8c8fc..59ce24031a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCall.kt @@ -31,8 +31,10 @@ import dagger.Reusable import io.reactivex.Single import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.APIDownloader +import org.hisp.dhis.android.core.arch.api.payload.internal.Payload import org.hisp.dhis.android.core.arch.call.factories.internal.UidsCall import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.common.internal.AccessFields import org.hisp.dhis.android.core.systeminfo.DHISVersion import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.visualization.Visualization @@ -46,10 +48,13 @@ internal class VisualizationCall @Inject constructor( ) : UidsCall { companion object { - private const val MAX_UID_LIST_SIZE = 90 + // Workaround for DHIS2-15322. Force queries to entity endpoint instead of list endpoint. + private const val MAX_UID_LIST_SIZE = 1 } override fun download(uids: Set): Single> { + val accessFilter = "access." + AccessFields.read.eq(true).generateString() + return if (dhis2VersionManager.isGreaterOrEqualThan(DHISVersion.V2_34)) { if (dhis2VersionManager.isGreaterOrEqualThan(DHISVersion.V2_37)) { apiDownloader.downloadPartitioned( @@ -57,26 +62,30 @@ internal class VisualizationCall @Inject constructor( MAX_UID_LIST_SIZE, handler ) { partitionUids: Set -> - service.getVisualizations( + service.getSingleVisualization( + partitionUids.first(), VisualizationFields.allFields, - VisualizationFields.uid.`in`(partitionUids), + accessFilter = accessFilter, paging = false ) + .map { Payload(listOf(it)) } + .onErrorReturnItem(Payload()) } } else { apiDownloader.downloadPartitioned( uids, MAX_UID_LIST_SIZE, - handler, - { partitionUids: Set -> - service.getVisualizations36( - VisualizationFields.allFieldsAPI36, - VisualizationFields.uid.`in`(partitionUids), - paging = false - ) - }, - { it.toVisualization() } - ) + handler + ) { partitionUids: Set -> + service.getSingleVisualizations36( + partitionUids.first(), + VisualizationFields.allFieldsAPI36, + accessFilter = accessFilter, + paging = false + ) + .map { Payload(listOf(it.toVisualization())) } + .onErrorReturnItem(Payload()) + } } } else { Single.just(listOf()) diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationService.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationService.kt index 9bc75f3aad..e90c78e9d4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationService.kt @@ -29,28 +29,33 @@ package org.hisp.dhis.android.core.visualization.internal import io.reactivex.Single import org.hisp.dhis.android.core.arch.api.fields.internal.Fields -import org.hisp.dhis.android.core.arch.api.filters.internal.Filter -import org.hisp.dhis.android.core.arch.api.filters.internal.Where import org.hisp.dhis.android.core.arch.api.filters.internal.Which -import org.hisp.dhis.android.core.arch.api.payload.internal.Payload import org.hisp.dhis.android.core.visualization.Visualization import org.hisp.dhis.android.core.visualization.VisualizationAPI36 import retrofit2.http.GET +import retrofit2.http.Path import retrofit2.http.Query internal interface VisualizationService { - @GET("visualizations") - fun getVisualizations( + @GET("$VISUALIZATIONS/{$VISUALIZATION_UID}") + fun getSingleVisualization( + @Path(VISUALIZATION_UID) uid: String, @Query("fields") @Which fields: Fields, - @Query("filter") @Where uids: Filter, + @Query("filter") accessFilter: String, @Query("paging") paging: Boolean - ): Single> + ): Single - @GET("visualizations") - fun getVisualizations36( + @GET("$VISUALIZATIONS/{$VISUALIZATION_UID}") + fun getSingleVisualizations36( + @Path(VISUALIZATION_UID) uid: String, @Query("fields") @Which fields: Fields, - @Query("filter") @Where uids: Filter, + @Query("filter") accessFilter: String, @Query("paging") paging: Boolean - ): Single> + ): Single + + companion object { + const val VISUALIZATIONS = "visualizations" + const val VISUALIZATION_UID = "visualizationUid" + } } diff --git a/core/src/sharedTest/resources/visualization/visualizations.json b/core/src/sharedTest/resources/visualization/visualizations.json deleted file mode 100644 index 9e63505a68..0000000000 --- a/core/src/sharedTest/resources/visualization/visualizations.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "visualizations": [ - { - "id": "PYBH8ZaAQnC", - "name": "Android SDK Visualization sample", - "displayName": "Android SDK Visualization sample", - "description": "Sample visualization for the Android SDK", - "displayDescription": "Sample visualization for the Android SDK", - "displayFormName": "Android SDK Visualization sample", - "title": "Sample title", - "displayTitle": "Sample title", - "subtitle": "Sample subtitle", - "displaySubtitle": "Sample subtitle", - "lastUpdated": "2021-06-16T14:26:50.195", - "created": "2021-06-16T14:26:50.195", - "type": "PIVOT_TABLE", - "hideTitle": false, - "hideSubtitle": false, - "hideEmptyColumns": false, - "hideEmptyRows": false, - "hideEmptyRowItems": "AFTER_LAST", - "hideLegend": false, - "showHierarchy": false, - "rowTotals": true, - "rowSubTotals": false, - "colTotals": false, - "colSubTotals": false, - "showDimensionLabels": false, - "percentStackedValues": false, - "noSpaceBetweenColumns": false, - "skipRounding": false, - "displayDensity": "NORMAL", - "digitGroupSeparator": "COMMA", - "legend": { - "showKey": false, - "style": "FILL", - "strategy": "FIXED", - "set": { - "id": "Yf6UHoPkd57" - } - }, - "filters": [ - { - "id": "ou", - "items": [ - { - "dimensionItem": "YuQRtpLP10I", - "dimensionItemType": "ORGANISATION_UNIT" - }, - { - "dimensionItem": "vWbkYPRmKyS", - "dimensionItemType": "ORGANISATION_UNIT" - }, - { - "dimensionItem": "LEVEL-3" - } - ] - } - ], - "rows": [ - { - "id": "pe", - "items": [ - { - "dimensionItem": "202102", - "dimensionItemType": "PERIOD" - }, - { - "dimensionItem": "202103", - "dimensionItemType": "PERIOD" - }, - { - "dimensionItem": "2021S2", - "dimensionItemType": "PERIOD" - }, - { - "dimensionItem": "LAST_12_MONTHS", - "dimensionItemType": "PERIOD" - } - ] - } - ], - "columns": [ - { - "id": "dx", - "items": [ - { - "dimensionItem": "Uvn6LCg7dVU", - "dimensionItemType": "INDICATOR" - }, - { - "dimensionItem": "cYeuwXTCPkU", - "dimensionItemType": "DATA_ELEMENT" - }, - { - "dimensionItem": "p2Zxg0wcPQ3", - "dimensionItemType": "PROGRAM_INDICATOR" - } - ] - }, - { - "id": "fMZEcRHuamy", - "items": [] - }, - { - "id": "fkAkrdC7eJF", - "items": [] - } - ] - }, - { - "id": "FAFa11yFeFe", - "name": "Android SDK Visualization sample 2", - "displayName": "Android SDK Visualization sample 2", - "description": "Sample visualization for the Android SDK 2", - "displayDescription": "Sample visualization for the Android SDK 2", - "displayFormName": "Android SDK Visualization sample 2", - "lastUpdated": "2021-06-16T14:26:50.195", - "created": "2021-06-16T14:26:50.195", - "type": "COLUMN", - "hideTitle": true, - "hideSubtitle": false, - "hideEmptyColumns": false, - "hideEmptyRows": false, - "hideEmptyRowItems": "NONE", - "hideLegend": false, - "showHierarchy": false, - "rowTotals": true, - "rowSubTotals": false, - "colTotals": false, - "colSubTotals": false, - "showDimensionLabels": false, - "percentStackedValues": false, - "noSpaceBetweenColumns": false, - "skipRounding": false, - "displayDensity": "NORMAL", - "digitGroupSeparator": "COMMA", - "legend": { - "showKey": false, - "style": "TEXT", - "strategy": "FIXED", - "set": { - "id": "TiOkbpGEud4" - } - }, - "filters": [ - { - "id": "ou", - "items": [ - { - "dimensionItem": "DiszpKrYNg8", - "dimensionItemType": "ORGANISATION_UNIT" - } - ] - } - ], - "rows": [ - { - "id": "pe", - "items": [ - { - "dimensionItem": "2016", - "dimensionItemType": "PERIOD" - }, - { - "dimensionItem": "2017", - "dimensionItemType": "PERIOD" - }, - { - "dimensionItem": "2018", - "dimensionItemType": "PERIOD" - } - ] - } - ], - "columns": [ - { - "id": "dx", - "items": [ - { - "dimensionItem": "g9eOBujte1U", - "dimensionItemType": "DATA_ELEMENT" - }, - { - "dimensionItem": "GSae40Fyppf", - "dimensionItemType": "PROGRAM_INDICATOR" - }, - { - "dimensionItem": "yYo5Gy4sZa0", - "dimensionItemType": "EXPRESSION_DIMENSION_ITEM" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/core/src/sharedTest/resources/visualization/visualizations_1.json b/core/src/sharedTest/resources/visualization/visualizations_1.json new file mode 100644 index 0000000000..f15862970f --- /dev/null +++ b/core/src/sharedTest/resources/visualization/visualizations_1.json @@ -0,0 +1,108 @@ +{ + "id": "PYBH8ZaAQnC", + "name": "Android SDK Visualization sample", + "displayName": "Android SDK Visualization sample", + "description": "Sample visualization for the Android SDK", + "displayDescription": "Sample visualization for the Android SDK", + "displayFormName": "Android SDK Visualization sample", + "title": "Sample title", + "displayTitle": "Sample title", + "subtitle": "Sample subtitle", + "displaySubtitle": "Sample subtitle", + "lastUpdated": "2021-06-16T14:26:50.195", + "created": "2021-06-16T14:26:50.195", + "type": "PIVOT_TABLE", + "hideTitle": false, + "hideSubtitle": false, + "hideEmptyColumns": false, + "hideEmptyRows": false, + "hideEmptyRowItems": "AFTER_LAST", + "hideLegend": false, + "showHierarchy": false, + "rowTotals": true, + "rowSubTotals": false, + "colTotals": false, + "colSubTotals": false, + "showDimensionLabels": false, + "percentStackedValues": false, + "noSpaceBetweenColumns": false, + "skipRounding": false, + "displayDensity": "NORMAL", + "digitGroupSeparator": "COMMA", + "legend": { + "showKey": false, + "style": "FILL", + "strategy": "FIXED", + "set": { + "id": "Yf6UHoPkd57" + } + }, + "filters": [ + { + "id": "ou", + "items": [ + { + "dimensionItem": "YuQRtpLP10I", + "dimensionItemType": "ORGANISATION_UNIT" + }, + { + "dimensionItem": "vWbkYPRmKyS", + "dimensionItemType": "ORGANISATION_UNIT" + }, + { + "dimensionItem": "LEVEL-3" + } + ] + } + ], + "rows": [ + { + "id": "pe", + "items": [ + { + "dimensionItem": "202102", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "202103", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "2021S2", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "LAST_12_MONTHS", + "dimensionItemType": "PERIOD" + } + ] + } + ], + "columns": [ + { + "id": "dx", + "items": [ + { + "dimensionItem": "Uvn6LCg7dVU", + "dimensionItemType": "INDICATOR" + }, + { + "dimensionItem": "cYeuwXTCPkU", + "dimensionItemType": "DATA_ELEMENT" + }, + { + "dimensionItem": "p2Zxg0wcPQ3", + "dimensionItemType": "PROGRAM_INDICATOR" + } + ] + }, + { + "id": "fMZEcRHuamy", + "items": [] + }, + { + "id": "fkAkrdC7eJF", + "items": [] + } + ] +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/visualization/visualizations_2.json b/core/src/sharedTest/resources/visualization/visualizations_2.json new file mode 100644 index 0000000000..055ff1f536 --- /dev/null +++ b/core/src/sharedTest/resources/visualization/visualizations_2.json @@ -0,0 +1,85 @@ +{ + "id": "FAFa11yFeFe", + "name": "Android SDK Visualization sample 2", + "displayName": "Android SDK Visualization sample 2", + "description": "Sample visualization for the Android SDK 2", + "displayDescription": "Sample visualization for the Android SDK 2", + "displayFormName": "Android SDK Visualization sample 2", + "lastUpdated": "2021-06-16T14:26:50.195", + "created": "2021-06-16T14:26:50.195", + "type": "COLUMN", + "hideTitle": true, + "hideSubtitle": false, + "hideEmptyColumns": false, + "hideEmptyRows": false, + "hideEmptyRowItems": "NONE", + "hideLegend": false, + "showHierarchy": false, + "rowTotals": true, + "rowSubTotals": false, + "colTotals": false, + "colSubTotals": false, + "showDimensionLabels": false, + "percentStackedValues": false, + "noSpaceBetweenColumns": false, + "skipRounding": false, + "displayDensity": "NORMAL", + "digitGroupSeparator": "COMMA", + "legend": { + "showKey": false, + "style": "TEXT", + "strategy": "FIXED", + "set": { + "id": "TiOkbpGEud4" + } + }, + "filters": [ + { + "id": "ou", + "items": [ + { + "dimensionItem": "DiszpKrYNg8", + "dimensionItemType": "ORGANISATION_UNIT" + } + ] + } + ], + "rows": [ + { + "id": "pe", + "items": [ + { + "dimensionItem": "2016", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "2017", + "dimensionItemType": "PERIOD" + }, + { + "dimensionItem": "2018", + "dimensionItemType": "PERIOD" + } + ] + } + ], + "columns": [ + { + "id": "dx", + "items": [ + { + "dimensionItem": "g9eOBujte1U", + "dimensionItemType": "DATA_ELEMENT" + }, + { + "dimensionItem": "GSae40Fyppf", + "dimensionItemType": "PROGRAM_INDICATOR" + }, + { + "dimensionItem": "yYo5Gy4sZa0", + "dimensionItemType": "EXPRESSION_DIMENSION_ITEM" + } + ] + } + ] +} From b70250273cabf6038a5742fd031e67b880d74c8e Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 30 May 2023 12:51:30 +1000 Subject: [PATCH 17/23] [ANDROSDK-1690] Add network timeout parameter to MapLayerDownloader --- .../hisp/dhis/android/core/map/MapModule.kt | 4 +- .../core/map/internal/MapModuleImpl.kt | 7 ++- .../core/map/layer/MapLayerDownloader.kt | 50 +++++++++++++++++++ .../map/layer/internal/MapLayerCallFactory.kt | 4 +- ...ownloader.kt => MapLayerDownloadParams.kt} | 17 ++----- .../layer/internal/MapLayerEntityDIModule.kt | 6 +++ .../layer/internal/bing/BingCallFactory.kt | 44 ++++++++++++---- 7 files changed, 102 insertions(+), 30 deletions(-) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/map/layer/MapLayerDownloader.kt rename core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/{MapLayerModuleDownloader.kt => MapLayerDownloadParams.kt} (78%) diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/MapModule.kt b/core/src/main/java/org/hisp/dhis/android/core/map/MapModule.kt index 5c44be2013..b4d9dfa26a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/MapModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/MapModule.kt @@ -27,12 +27,12 @@ */ package org.hisp.dhis.android.core.map -import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader import org.hisp.dhis.android.core.map.layer.MapLayerCollectionRepository +import org.hisp.dhis.android.core.map.layer.MapLayerDownloader interface MapModule { fun mapLayers(): MapLayerCollectionRepository - fun mapLayersDownloader(): UntypedModuleDownloader + fun mapLayersDownloader(): MapLayerDownloader } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/internal/MapModuleImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/map/internal/MapModuleImpl.kt index 4b861b1bf5..8177718b62 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/internal/MapModuleImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/internal/MapModuleImpl.kt @@ -29,22 +29,21 @@ package org.hisp.dhis.android.core.map.internal import dagger.Reusable import javax.inject.Inject -import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader import org.hisp.dhis.android.core.map.MapModule import org.hisp.dhis.android.core.map.layer.MapLayerCollectionRepository -import org.hisp.dhis.android.core.map.layer.internal.MapLayerModuleDownloader +import org.hisp.dhis.android.core.map.layer.MapLayerDownloader @Reusable internal class MapModuleImpl @Inject internal constructor( private val mapLayerCollectionRepository: MapLayerCollectionRepository, - private val mapLayerModuleDownloader: MapLayerModuleDownloader + private val mapLayerModuleDownloader: MapLayerDownloader ) : MapModule { override fun mapLayers(): MapLayerCollectionRepository { return mapLayerCollectionRepository } - override fun mapLayersDownloader(): UntypedModuleDownloader { + override fun mapLayersDownloader(): MapLayerDownloader { return mapLayerModuleDownloader } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/MapLayerDownloader.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/MapLayerDownloader.kt new file mode 100644 index 0000000000..a7d0948ea9 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/MapLayerDownloader.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.map.layer + +import dagger.Reusable +import io.reactivex.Completable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader +import org.hisp.dhis.android.core.map.layer.internal.MapLayerCallFactory +import org.hisp.dhis.android.core.map.layer.internal.MapLayerDownloadParams + +@Reusable +class MapLayerDownloader @Inject internal constructor( + private val mapLayerCallFactory: MapLayerCallFactory, + private val params: MapLayerDownloadParams +) : UntypedModuleDownloader { + + override fun downloadMetadata(): Completable { + return Completable.fromSingle(mapLayerCallFactory.downloadMetadata(params)) + } + + fun withNetworkTimeoutInSeconds(timeout: Int): MapLayerDownloader { + return MapLayerDownloader(mapLayerCallFactory, params.copy(networkTimeoutInSeconds = timeout)) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerCallFactory.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerCallFactory.kt index 9ae319be68..5fbe4e07b0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerCallFactory.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerCallFactory.kt @@ -40,10 +40,10 @@ internal class MapLayerCallFactory @Inject constructor( private val bingCallFactory: BingCallFactory ) { - fun downloadMetadata(): Single> { + fun downloadMetadata(params: MapLayerDownloadParams): Single> { return Single.merge( osmCallFactory.download(), - bingCallFactory.download() + bingCallFactory.download(params) ).toList().map { it.flatten() } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerModuleDownloader.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerDownloadParams.kt similarity index 78% rename from core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerModuleDownloader.kt rename to core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerDownloadParams.kt index 6db3639656..bdf95ce640 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerModuleDownloader.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerDownloadParams.kt @@ -27,17 +27,8 @@ */ package org.hisp.dhis.android.core.map.layer.internal -import dagger.Reusable -import io.reactivex.Completable -import javax.inject.Inject -import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader +import org.hisp.dhis.android.core.arch.repositories.scope.BaseScope -@Reusable -class MapLayerModuleDownloader @Inject internal constructor( - private val mapLayerCallFactory: MapLayerCallFactory -) : UntypedModuleDownloader { - - override fun downloadMetadata(): Completable { - return Completable.fromSingle(mapLayerCallFactory.downloadMetadata()) - } -} +internal data class MapLayerDownloadParams( + val networkTimeoutInSeconds: Int? = null +) : BaseScope diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerEntityDIModule.kt index 49fc83374b..fbc8b5b6d5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerEntityDIModule.kt @@ -66,4 +66,10 @@ internal class MapLayerEntityDIModule { MapLayer.IMAGERY_PROVIDERS to MapLayerImagerProviderChildrenAppender(store) ) } + + @Provides + @Reusable + fun downloadParams(): MapLayerDownloadParams { + return MapLayerDownloadParams() + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt index dffc356fe4..6594654c87 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt @@ -29,7 +29,10 @@ package org.hisp.dhis.android.core.map.layer.internal.bing import dagger.Reusable +import io.reactivex.Flowable import io.reactivex.Single +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException import javax.inject.Inject import org.hisp.dhis.android.core.D2Manager import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor @@ -38,6 +41,7 @@ import org.hisp.dhis.android.core.map.layer.MapLayer import org.hisp.dhis.android.core.map.layer.MapLayerImageryProvider import org.hisp.dhis.android.core.map.layer.MapLayerImageryProviderArea import org.hisp.dhis.android.core.map.layer.MapLayerPosition +import org.hisp.dhis.android.core.map.layer.internal.MapLayerDownloadParams import org.hisp.dhis.android.core.settings.internal.SettingService import org.hisp.dhis.android.core.settings.internal.SystemSettingsFields import org.hisp.dhis.android.core.systeminfo.DHISVersion @@ -52,13 +56,13 @@ internal class BingCallFactory @Inject constructor( private val bingService: BingService ) { - fun download(): Single> { + fun download(params: MapLayerDownloadParams): Single> { return Single.defer { if (versionManager.isGreaterOrEqualThan(DHISVersion.V2_34)) { rxAPICallExecutor.wrapSingle(settingsService.getSystemSettings(SystemSettingsFields.bingApiKey), true) .flatMap { settings -> settings.keyBingMapsApiKey - ?.let { downloadBingBasemaps(it) } + ?.let { downloadBingBasemaps(it, params) } ?: Single.just(emptyList()) } .map { mapLayers -> @@ -72,14 +76,36 @@ internal class BingCallFactory @Inject constructor( } } - private fun downloadBingBasemaps(bingKey: String): Single> { - return Single.merge( - BingBasemaps.list.map { b -> downloadBasemap(bingKey, b) } - ).toList().map { it.flatten() } + private fun downloadBingBasemaps( + bingKey: String, + params: MapLayerDownloadParams + ): Single> { + return Flowable.fromIterable(BingBasemaps.list) + .flatMapSingle { b -> + downloadBasemap(bingKey, b, params).onErrorReturn { t -> + when (t) { + is TimeoutException -> throw t + else -> emptyList() + } + } + } + .toList() + .map { it.flatten() } + .onErrorReturnItem(emptyList()) } - private fun downloadBasemap(bingkey: String, basemap: BingBasemap): Single> { - return bingService.getBaseMap(getUrl(basemap.style, bingkey)) + private fun downloadBasemap( + bingkey: String, + basemap: BingBasemap, + params: MapLayerDownloadParams + ): Single> { + return bingService.getBaseMap(getUrl(basemap.style, bingkey)).run { + if (params.networkTimeoutInSeconds != null) { + this.timeout(params.networkTimeoutInSeconds.toLong(), TimeUnit.SECONDS) + } else { + this + } + } .map { m -> m.resourceSets.firstOrNull()?.resources?.firstOrNull()?.let { resource -> listOf( @@ -113,7 +139,7 @@ internal class BingCallFactory @Inject constructor( .build() ) } ?: emptyList() - }.onErrorReturnItem(emptyList()) + } } private fun getUrl(style: String, bingKey: String): String { From 56f2e3a07ab9530dfd6643a24d26fb32fb8ada71 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 30 May 2023 15:33:05 +1000 Subject: [PATCH 18/23] [ANDROSDK-1690] Refactor using coroutines --- .../layer/internal/bing/BingCallFactory.kt | 151 ++++++++++-------- .../map/layer/internal/bing/BingService.kt | 5 +- .../core/settings/internal/SettingService.kt | 5 +- .../settings/internal/SystemSettingCall.kt | 2 +- .../settings/internal/SystemSettingsFields.kt | 2 - 5 files changed, 88 insertions(+), 77 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt index 6594654c87..d9f7cf5e22 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt @@ -29,13 +29,14 @@ package org.hisp.dhis.android.core.map.layer.internal.bing import dagger.Reusable -import io.reactivex.Flowable import io.reactivex.Single -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.rx2.rxSingle +import kotlinx.coroutines.withTimeout import org.hisp.dhis.android.core.D2Manager -import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor +import org.hisp.dhis.android.core.arch.api.executors.internal.CoroutineAPICallExecutor import org.hisp.dhis.android.core.arch.handlers.internal.Handler import org.hisp.dhis.android.core.map.layer.MapLayer import org.hisp.dhis.android.core.map.layer.MapLayerImageryProvider @@ -49,97 +50,107 @@ import org.hisp.dhis.android.core.systeminfo.DHISVersionManager @Reusable internal class BingCallFactory @Inject constructor( - private val rxAPICallExecutor: RxAPICallExecutor, + private val coroutineAPICallExecutor: CoroutineAPICallExecutor, private val mapLayerHandler: Handler, private val versionManager: DHISVersionManager, private val settingsService: SettingService, private val bingService: BingService ) { + @Suppress("TooGenericExceptionCaught") fun download(params: MapLayerDownloadParams): Single> { - return Single.defer { + return rxSingle { if (versionManager.isGreaterOrEqualThan(DHISVersion.V2_34)) { - rxAPICallExecutor.wrapSingle(settingsService.getSystemSettings(SystemSettingsFields.bingApiKey), true) - .flatMap { settings -> - settings.keyBingMapsApiKey - ?.let { downloadBingBasemaps(it, params) } - ?: Single.just(emptyList()) + try { + val settings = coroutineAPICallExecutor.wrap(storeError = true) { + settingsService.getSystemSettings(SystemSettingsFields.bingApiKey) } - .map { mapLayers -> - mapLayerHandler.handleMany(mapLayers) - mapLayers + + val mapLayers = settings.getOrNull()?.keyBingMapsApiKey + ?.let { key -> downloadBingBasemaps(key, params) } + ?: emptyList() + + mapLayers.also { + mapLayerHandler.handleMany(it) } - .onErrorReturnItem(emptyList()) + } catch (e: Exception) { + emptyList() + } } else { - Single.just(emptyList()) + emptyList() } } } - private fun downloadBingBasemaps( + @Suppress("TooGenericExceptionCaught") + private suspend fun downloadBingBasemaps( bingKey: String, params: MapLayerDownloadParams - ): Single> { - return Flowable.fromIterable(BingBasemaps.list) - .flatMapSingle { b -> - downloadBasemap(bingKey, b, params).onErrorReturn { t -> - when (t) { - is TimeoutException -> throw t + ): List { + return try { + BingBasemaps.list.map { b -> + try { + if (params.networkTimeoutInSeconds != null) { + withTimeout(params.networkTimeoutInSeconds.seconds) { + downloadBasemap(bingKey, b) + } + } else { + downloadBasemap(bingKey, b) + } + } catch (e: Exception) { + when (e) { + is TimeoutCancellationException -> throw e else -> emptyList() } } - } - .toList() - .map { it.flatten() } - .onErrorReturnItem(emptyList()) + }.flatten() + } catch (e: Exception) { + emptyList() + } } - private fun downloadBasemap( + private suspend fun downloadBasemap( bingkey: String, - basemap: BingBasemap, - params: MapLayerDownloadParams - ): Single> { - return bingService.getBaseMap(getUrl(basemap.style, bingkey)).run { - if (params.networkTimeoutInSeconds != null) { - this.timeout(params.networkTimeoutInSeconds.toLong(), TimeUnit.SECONDS) - } else { - this - } + basemap: BingBasemap + ): List { + val bingResponseResult = coroutineAPICallExecutor.wrap(storeError = false) { + bingService.getBaseMap(getUrl(basemap.style, bingkey)) } - .map { m -> - m.resourceSets.firstOrNull()?.resources?.firstOrNull()?.let { resource -> - listOf( - MapLayer.builder() - .uid(basemap.id) - .name(basemap.name) - .displayName(basemap.name) - .style(basemap.style) - .mapLayerPosition(MapLayerPosition.BASEMAP) - .external(false) - .imageUrl(resource.imageUrl) - .subdomains(resource.imageUrlSubdomains) - .subdomainPlaceholder("{subdomain}") - .imageryProviders( - resource.imageryProviders.map { i -> - MapLayerImageryProvider.builder() - .mapLayer(basemap.id) - .attribution(i.attribution) - .coverageAreas( - i.coverageAreas.map { ca -> - MapLayerImageryProviderArea.builder() - .bbox(ca.bbox) - .zoomMax(ca.zoomMax) - .zoomMin(ca.zoomMin) - .build() - } - ) - .build() - } - ) - .build() - ) - } ?: emptyList() + + return bingResponseResult.map { bingResponse -> + bingResponse.resourceSets.firstOrNull()?.resources?.firstOrNull()?.let { resource -> + listOf( + MapLayer.builder() + .uid(basemap.id) + .name(basemap.name) + .displayName(basemap.name) + .style(basemap.style) + .mapLayerPosition(MapLayerPosition.BASEMAP) + .external(false) + .imageUrl(resource.imageUrl) + .subdomains(resource.imageUrlSubdomains) + .subdomainPlaceholder("{subdomain}") + .imageryProviders( + resource.imageryProviders.map { i -> + MapLayerImageryProvider.builder() + .mapLayer(basemap.id) + .attribution(i.attribution) + .coverageAreas( + i.coverageAreas.map { ca -> + MapLayerImageryProviderArea.builder() + .bbox(ca.bbox) + .zoomMax(ca.zoomMax) + .zoomMin(ca.zoomMin) + .build() + } + ) + .build() + } + ) + .build() + ) } + }.getOrNull() ?: emptyList() } private fun getUrl(style: String, bingKey: String): String { diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingService.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingService.kt index e2b3e4f1ee..8638a61f8c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingService.kt @@ -28,14 +28,13 @@ package org.hisp.dhis.android.core.map.layer.internal.bing -import io.reactivex.Single import retrofit2.http.GET import retrofit2.http.Url internal interface BingService { @GET - fun getBaseMap( + suspend fun getBaseMap( @Url url: String - ): Single + ): BingServerResponse } diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingService.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingService.kt index 28587be5fb..54a5f11b22 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingService.kt @@ -39,7 +39,10 @@ import retrofit2.http.Url internal interface SettingService { @GET("systemSettings") - fun getSystemSettings(@Query("key") @Which fields: Fields): Single + fun getSystemSettingsSingle(@Query("key") @Which fields: Fields): Single + + @GET("systemSettings") + suspend fun getSystemSettings(@Query("key") @Which fields: Fields): SystemSettings @GET("userSettings") fun getUserSettings(@Query("key") @Which fields: Fields): Single diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SystemSettingCall.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SystemSettingCall.kt index 3bd6227cb8..ab7dc5ddec 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SystemSettingCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SystemSettingCall.kt @@ -47,7 +47,7 @@ internal class SystemSettingCall @Inject constructor( override fun download(): Single> { return apiDownloader.downloadList( handler = handler, - downloader = service.getSystemSettings(allFields).map(settingsSplitter::splitSettings) + downloader = service.getSystemSettingsSingle(allFields).map(settingsSplitter::splitSettings) ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SystemSettingsFields.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SystemSettingsFields.kt index 37dbf6ff99..a74cd0fd89 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SystemSettingsFields.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SystemSettingsFields.kt @@ -38,14 +38,12 @@ internal object SystemSettingsFields { private val fh = FieldsHelper() - @JvmStatic val allFields: Fields = Fields.builder() .fields( fh.field(KEY_FLAG), fh.field(KEY_STYLE) ).build() - @JvmStatic val bingApiKey: Fields = Fields.builder() .fields( fh.field(KEY_BING_MAPS_API_KEY) From 47aec2fdc8ec19c48a34eb8bd6e2d1b65d88e787 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 1 Jun 2023 08:59:54 +1000 Subject: [PATCH 19/23] [ANDROSDK-1690] Bing map download: set internal timeout to 30 seconds --- .../hisp/dhis/android/core/map/MapModule.kt | 4 +-- .../core/map/internal/MapModuleImpl.kt | 3 +- .../core/map/layer/MapLayerDownloader.kt | 10 ++---- .../map/layer/internal/MapLayerCallFactory.kt | 4 +-- .../layer/internal/MapLayerDownloadParams.kt | 34 ------------------- .../layer/internal/MapLayerEntityDIModule.kt | 6 ---- .../layer/internal/bing/BingCallFactory.kt | 18 +++++----- 7 files changed, 16 insertions(+), 63 deletions(-) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerDownloadParams.kt diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/MapModule.kt b/core/src/main/java/org/hisp/dhis/android/core/map/MapModule.kt index b4d9dfa26a..5c44be2013 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/MapModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/MapModule.kt @@ -27,12 +27,12 @@ */ package org.hisp.dhis.android.core.map +import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader import org.hisp.dhis.android.core.map.layer.MapLayerCollectionRepository -import org.hisp.dhis.android.core.map.layer.MapLayerDownloader interface MapModule { fun mapLayers(): MapLayerCollectionRepository - fun mapLayersDownloader(): MapLayerDownloader + fun mapLayersDownloader(): UntypedModuleDownloader } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/internal/MapModuleImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/map/internal/MapModuleImpl.kt index 8177718b62..4aafe3c2e9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/internal/MapModuleImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/internal/MapModuleImpl.kt @@ -29,6 +29,7 @@ package org.hisp.dhis.android.core.map.internal import dagger.Reusable import javax.inject.Inject +import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader import org.hisp.dhis.android.core.map.MapModule import org.hisp.dhis.android.core.map.layer.MapLayerCollectionRepository import org.hisp.dhis.android.core.map.layer.MapLayerDownloader @@ -43,7 +44,7 @@ internal class MapModuleImpl @Inject internal constructor( return mapLayerCollectionRepository } - override fun mapLayersDownloader(): MapLayerDownloader { + override fun mapLayersDownloader(): UntypedModuleDownloader { return mapLayerModuleDownloader } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/MapLayerDownloader.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/MapLayerDownloader.kt index a7d0948ea9..1e7a66ca82 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/MapLayerDownloader.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/MapLayerDownloader.kt @@ -32,19 +32,13 @@ import io.reactivex.Completable import javax.inject.Inject import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader import org.hisp.dhis.android.core.map.layer.internal.MapLayerCallFactory -import org.hisp.dhis.android.core.map.layer.internal.MapLayerDownloadParams @Reusable class MapLayerDownloader @Inject internal constructor( - private val mapLayerCallFactory: MapLayerCallFactory, - private val params: MapLayerDownloadParams + private val mapLayerCallFactory: MapLayerCallFactory ) : UntypedModuleDownloader { override fun downloadMetadata(): Completable { - return Completable.fromSingle(mapLayerCallFactory.downloadMetadata(params)) - } - - fun withNetworkTimeoutInSeconds(timeout: Int): MapLayerDownloader { - return MapLayerDownloader(mapLayerCallFactory, params.copy(networkTimeoutInSeconds = timeout)) + return Completable.fromSingle(mapLayerCallFactory.downloadMetadata()) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerCallFactory.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerCallFactory.kt index 5fbe4e07b0..9ae319be68 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerCallFactory.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerCallFactory.kt @@ -40,10 +40,10 @@ internal class MapLayerCallFactory @Inject constructor( private val bingCallFactory: BingCallFactory ) { - fun downloadMetadata(params: MapLayerDownloadParams): Single> { + fun downloadMetadata(): Single> { return Single.merge( osmCallFactory.download(), - bingCallFactory.download(params) + bingCallFactory.download() ).toList().map { it.flatten() } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerDownloadParams.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerDownloadParams.kt deleted file mode 100644 index bdf95ce640..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerDownloadParams.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2004-2023, 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.android.core.map.layer.internal - -import org.hisp.dhis.android.core.arch.repositories.scope.BaseScope - -internal data class MapLayerDownloadParams( - val networkTimeoutInSeconds: Int? = null -) : BaseScope diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerEntityDIModule.kt index fbc8b5b6d5..49fc83374b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/MapLayerEntityDIModule.kt @@ -66,10 +66,4 @@ internal class MapLayerEntityDIModule { MapLayer.IMAGERY_PROVIDERS to MapLayerImagerProviderChildrenAppender(store) ) } - - @Provides - @Reusable - fun downloadParams(): MapLayerDownloadParams { - return MapLayerDownloadParams() - } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt index d9f7cf5e22..f7f14339d1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/layer/internal/bing/BingCallFactory.kt @@ -42,7 +42,6 @@ import org.hisp.dhis.android.core.map.layer.MapLayer import org.hisp.dhis.android.core.map.layer.MapLayerImageryProvider import org.hisp.dhis.android.core.map.layer.MapLayerImageryProviderArea import org.hisp.dhis.android.core.map.layer.MapLayerPosition -import org.hisp.dhis.android.core.map.layer.internal.MapLayerDownloadParams import org.hisp.dhis.android.core.settings.internal.SettingService import org.hisp.dhis.android.core.settings.internal.SystemSettingsFields import org.hisp.dhis.android.core.systeminfo.DHISVersion @@ -58,7 +57,7 @@ internal class BingCallFactory @Inject constructor( ) { @Suppress("TooGenericExceptionCaught") - fun download(params: MapLayerDownloadParams): Single> { + fun download(): Single> { return rxSingle { if (versionManager.isGreaterOrEqualThan(DHISVersion.V2_34)) { try { @@ -67,7 +66,7 @@ internal class BingCallFactory @Inject constructor( } val mapLayers = settings.getOrNull()?.keyBingMapsApiKey - ?.let { key -> downloadBingBasemaps(key, params) } + ?.let { key -> downloadBingBasemaps(key) } ?: emptyList() mapLayers.also { @@ -84,17 +83,12 @@ internal class BingCallFactory @Inject constructor( @Suppress("TooGenericExceptionCaught") private suspend fun downloadBingBasemaps( - bingKey: String, - params: MapLayerDownloadParams + bingKey: String ): List { return try { BingBasemaps.list.map { b -> try { - if (params.networkTimeoutInSeconds != null) { - withTimeout(params.networkTimeoutInSeconds.seconds) { - downloadBasemap(bingKey, b) - } - } else { + withTimeout(TIMEOUT_SECONDS.seconds) { downloadBasemap(bingKey, b) } } catch (e: Exception) { @@ -161,4 +155,8 @@ internal class BingCallFactory @Inject constructor( "output=json&include=ImageryProviders&culture=en-GB&uriScheme=https&key=$bingKey" } } + + companion object { + private const val TIMEOUT_SECONDS = 30 + } } From 63e91e0f5c0e320ff2855ea2bb1952e2beb662f0 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 5 Jun 2023 08:39:11 +1000 Subject: [PATCH 20/23] [ANDROSDK-1687] Use correct dimension type in IndicatorUidSeeker --- ...ndicatorUidsSeekerMockIntegrationShould.kt | 46 +++++++++++++++++++ .../indicator/internal/IndicatorUidsSeeker.kt | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeekerMockIntegrationShould.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeekerMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeekerMockIntegrationShould.kt new file mode 100644 index 0000000000..9bd2b776a4 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeekerMockIntegrationShould.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-2023, 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.android.core.indicator.internal + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class IndicatorUidsSeekerMockIntegrationShould : BaseMockIntegrationTestFullDispatcher() { + @Test + fun seek_programIndicator_uids() { + val indicatorsUids = IndicatorUidsSeeker(databaseAdapter).seekUids() + assertThat(indicatorsUids.size).isEqualTo(2) + assertThat(indicatorsUids.contains("ReUHfIn0pTQ")).isTrue() + assertThat(indicatorsUids.contains("Uvn6LCg7dVU")).isTrue() + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt b/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt index 3bbabdb625..806dc1bdc6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/indicator/internal/IndicatorUidsSeeker.kt @@ -53,7 +53,7 @@ internal class IndicatorUidsSeeker @Inject constructor( val visualizationQuery = "SELECT ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM} " + "FROM ${VisualizationDimensionItemTableInfo.TABLE_INFO.name()} " + "WHERE ${VisualizationDimensionItemTableInfo.Columns.DIMENSION_ITEM_TYPE} = " + - "'${DimensionItemType.PROGRAM_INDICATOR.name}'" + "'${DimensionItemType.INDICATOR.name}'" return readSingleColumnResults(tablesQuery) + readSingleColumnResults(visualizationQuery) } From bd864be12cfcaaa2357875eab9305a3822a47775 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 5 Jun 2023 08:58:44 +1000 Subject: [PATCH 21/23] [ANDROSDK-1687] Catch NaN evaluations in expressionService --- ...nsionItemEvaluatorIntegrationBaseShould.kt | 41 ++++++++----------- ...IndicatorEvaluatorIntegrationBaseShould.kt | 7 ++++ .../aggregated/internal/AnalyticsService.kt | 4 +- .../indicatorengine/IndicatorSQLEngine.kt | 2 +- .../internal/service/ExpressionService.kt | 8 +++- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt index 860f940e61..38697ff550 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ExpressionDimensionItemEvaluatorIntegrationBaseShould.kt @@ -64,10 +64,8 @@ internal class ExpressionDimensionItemEvaluatorIntegrationBaseShould : BaseEvalu @Test fun should_evaluate_mathematical_expressions() { - val item = createExpression(expression = "4 * 5 / 2") - + val item = createExpression(item = "4 * 5 / 2") val value = evaluateForThisMonth(item) - assertThat(value).isEqualTo("10.0") } @@ -76,10 +74,8 @@ internal class ExpressionDimensionItemEvaluatorIntegrationBaseShould : BaseEvalu createDataValue("2", dataElementUid = dataElement1.uid()) createDataValue("3", dataElementUid = dataElement2.uid()) - val item = createExpression(expression = "${de(dataElement1.uid())} + ${de(dataElement2.uid())}") - + val item = createExpression(item = "${de(dataElement1.uid())} + ${de(dataElement2.uid())}") val value = evaluateForThisMonth(item) - assertThat(value).isEqualTo("5.0") } @@ -87,10 +83,8 @@ internal class ExpressionDimensionItemEvaluatorIntegrationBaseShould : BaseEvalu fun should_evaluate_days_variable() { createDataValue("62", dataElementUid = dataElement1.uid()) - val item = createExpression(expression = "${de(dataElement1.uid())} + [days]") - + val item = createExpression(item = "${de(dataElement1.uid())} + [days]") val value = evaluateForThisMonth(item) - assertThat(value).isEqualTo("93.0") } @@ -98,10 +92,8 @@ internal class ExpressionDimensionItemEvaluatorIntegrationBaseShould : BaseEvalu fun should_evaluate_constants() { createDataValue("10", dataElementUid = dataElement1.uid()) - val item = createExpression(expression = cons(constant1.uid())) - + val item = createExpression(item = cons(constant1.uid())) val value = evaluateForThisMonth(item) - assertThat(value).isEqualTo("5.0") } @@ -110,12 +102,8 @@ internal class ExpressionDimensionItemEvaluatorIntegrationBaseShould : BaseEvalu createEventAndValue("5", dataElement1.uid()) createEventAndValue("15", dataElement1.uid()) - val indicator = createExpression( - expression = eventDE(program.uid(), dataElement1.uid()) - ) - - val value = evaluateForThisMonth(indicator) - + val item = createExpression(item = eventDE(program.uid(), dataElement1.uid())) + val value = evaluateForThisMonth(item) assertThat(value).isEqualTo("20.0") } @@ -124,22 +112,25 @@ internal class ExpressionDimensionItemEvaluatorIntegrationBaseShould : BaseEvalu createTEIAndAttribute("10", attribute1.uid()) createTEIAndAttribute("5", attribute1.uid()) - val indicator = createExpression( - expression = eventAtt(program.uid(), attribute1.uid()) - ) + val item = createExpression(item = eventAtt(program.uid(), attribute1.uid())) + val value = evaluateForThisMonth(item) + assertThat(value).isEqualTo("15.0") + } + @Test + fun should_evaluate_missing_values() { + val indicator = createExpression(item = "${de(dataElement1.uid())} / ${de(dataElement2.uid())}") val value = evaluateForThisMonth(indicator) - - assertThat(value).isEqualTo("15.0") + assertThat(value).isEqualTo("0.0") } private fun createExpression( - expression: String + item: String ): ExpressionDimensionItem { val expressionDimensionItem = ExpressionDimensionItem.builder() .uid(generator.generate()) .displayName("Expression Dimension Item") - .expression(expression) + .expression(item) .build() expressionDimensionItemStore.insert(expressionDimensionItem) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt index 0d12239b10..55d31d5ffd 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt @@ -279,6 +279,13 @@ internal abstract class IndicatorEvaluatorIntegrationBaseShould : BaseEvaluatorI assertThat(result).isEqualTo("4.0") } + @Test + fun should_evaluate_missing_values() { + val indicator = createIndicator(numerator = "${de(dataElement1.uid())} / ${de(dataElement2.uid())}") + val result = evaluateForThisMonth(indicator) + assertThat(result).isEqualTo("0.0") + } + private fun evaluateForThisMonth( indicator: Indicator, aggregationType: AggregationType = AggregationType.DEFAULT diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsService.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsService.kt index 0b0747a8cd..efd6abad91 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsService.kt @@ -35,7 +35,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.Dimension import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem import org.hisp.dhis.android.core.analytics.aggregated.DimensionalResponse import org.hisp.dhis.android.core.arch.helpers.Result -import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.antlr.ParserException internal class AnalyticsService @Inject constructor( private val analyticsServiceDimensionHelper: AnalyticsServiceDimensionHelper, @@ -85,7 +85,7 @@ internal class AnalyticsService @Inject constructor( ) } catch (e: AnalyticsException) { Result.Failure(e) - } catch (e: ParserExceptionWithoutContext) { + } catch (e: ParserException) { Result.Failure(AnalyticsException.ParserException(e.message ?: "Unknown")) } catch (e: IllegalArgumentException) { Result.Failure(AnalyticsException.InvalidArguments(e.message ?: "Unknown")) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt index ae967c1900..be9595469c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt @@ -78,6 +78,6 @@ internal class IndicatorSQLEngine @Inject constructor( val denominator = engine.evaluate(indicator.denominator()!!) val factor = indicatorType?.factor() ?: 1 - return "SELECT $factor * ($numerator) / ($denominator)" + return "SELECT COALESCE($factor * ($numerator) / ($denominator), '0.0')" } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt index f629c05b72..b573192cfb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt @@ -50,6 +50,7 @@ import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimItemDataEl import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemId import org.hisp.dhis.android.core.program.ProgramStage import org.hisp.dhis.android.core.validation.MissingValueStrategy +import org.hisp.dhis.antlr.ParserException import org.hisp.dhis.parser.expression.antlr.ExpressionParser internal class ExpressionService @Inject constructor( @@ -124,7 +125,12 @@ internal class ExpressionService @Inject constructor( visitor.orgUnitCountMap = context.orgUnitCountMap visitor.days = context.days?.toDouble() - val value = visit(expression, visitor) + val value = try { + visit(expression, visitor) + } catch (e: ParserException) { + null + } + val itemsFound = visitor.state.itemsFound val itemValuesFound = visitor.state.itemValuesFound From bb2fd149d2a1dee49df4289e2409511261da257c Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 7 Jun 2023 11:10:36 +0200 Subject: [PATCH 22/23] Add PendingIntent.FLAG_IMMUTABLE for send SMS in versions greater than 31 --- .../core/sms/data/smsrepository/internal/SmsRepositoryImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/data/smsrepository/internal/SmsRepositoryImpl.java b/core/src/main/java/org/hisp/dhis/android/core/sms/data/smsrepository/internal/SmsRepositoryImpl.java index 1933d3f097..435884e3dd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/data/smsrepository/internal/SmsRepositoryImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/data/smsrepository/internal/SmsRepositoryImpl.java @@ -174,7 +174,7 @@ private void sendSmsToOS(SendingStateReceiver stateReceiver, String number, List context, uniqueIntentId, new Intent(sendSmsAction).putExtra(SMS_KEY, smsKey), - PendingIntent.FLAG_ONE_SHOT); + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE); sentMessagePIs.add(sentPI); uniqueIntentId++; } From fe6513aa7713437bac2acc7d6512ae86de27aebd Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 7 Jun 2023 19:43:48 +1000 Subject: [PATCH 23/23] [1.8.1-RC] Version 1.8.1 --- core/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/gradle.properties b/core/gradle.properties index 1c9437d1c8..a57420d63d 100644 --- a/core/gradle.properties +++ b/core/gradle.properties @@ -29,7 +29,7 @@ # Properties which are consumed by plugins/gradle-mvn-push.gradle plugin. # They are used for publishing artifact to snapshot repository. -VERSION_NAME=1.8.1-SNAPSHOT +VERSION_NAME=1.8.1 VERSION_CODE=281 GROUP=org.hisp.dhis