diff --git a/core/build.gradle b/core/build.gradle index 88538b6ac8..6beae70e86 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -43,8 +43,8 @@ ext { buildToolsVersion: "29.0.3", minSdkVersion : 19, targetSdkVersion : 29, - versionCode : 242, - versionName : "1.4.2" + versionCode : 243, + versionName : "1.4.3" ] libraries = [ diff --git a/core/config/detekt.yml b/core/config/detekt.yml index e8b670d0e1..59cf0ede2b 100644 --- a/core/config/detekt.yml +++ b/core/config/detekt.yml @@ -68,7 +68,7 @@ complexity: ComplexMethod: active: true threshold: 15 - ignoreSingleWhenExpression: false + ignoreSingleWhenExpression: true ignoreSimpleWhenEntries: false ignoreNestingFunctions: false nestingFunctions: [run, let, apply, with, also, use, forEach, isNotNull, ifNull] diff --git a/core/gradle.properties b/core/gradle.properties index f0e060f276..1cd5fd5b8a 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.4.2 -VERSION_CODE=242 +VERSION_NAME=1.4.3 +VERSION_CODE=243 GROUP=org.hisp.dhis diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/EventWithLimitCallMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/EventWithLimitCallMockIntegrationShould.kt index a28c654219..ace96396b3 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/EventWithLimitCallMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/EventWithLimitCallMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.java index c7411c5248..580f59ad43 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/MetadataCallRealIntegrationShould.java @@ -75,7 +75,7 @@ public void response_successful_on_sync_meta_data_once() throws Exception { d2.userModule().logIn(username, password, url).blockingGet(); d2.metadataModule().blockingDownload(); - + //TODO: add additional sync + break point. //when debugger stops at the new break point manually change metadata online & resume. 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 new file mode 100644 index 0000000000..ce3ccfe243 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsVisualizationRepositoryIntegrationShould.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2004-2021, 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.Dimension +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.common.RelativeOrganisationUnit +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher +import org.junit.Test + +class AnalyticsVisualizationRepositoryIntegrationShould : BaseMockIntegrationTestFullDispatcher() { + + private val visualizationUid = "FAFa11yFeFe" + + @Test + fun evaluate_visualization() { + val result = d2.analyticsModule().visualizations() + .withVisualization(visualizationUid) + .blockingEvaluate() + + assertThat(result.dimensions.columns.size).isEqualTo(1) + assertThat(result.dimensions.rows.size).isEqualTo(1) + assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(1) + assertThat(result.dimensionItems[Dimension.OrganisationUnit]!!.size).isEqualTo(1) + assertThat(result.dimensionItems[Dimension.Period]!!.size).isEqualTo(3) + assertThat(result.metadata).isNotEmpty() + assertThat(result.values.size).isEqualTo(3) + } + + @Test + fun evaluate_visualization_wit_periods() { + val result = d2.analyticsModule().visualizations() + .withVisualization(visualizationUid) + .withPeriods(listOf(DimensionItem.PeriodItem.Absolute("2018"))) + .blockingEvaluate() + + assertThat(result.dimensions.columns.size).isEqualTo(1) + assertThat(result.dimensions.rows.size).isEqualTo(1) + assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(1) + assertThat(result.dimensionItems[Dimension.OrganisationUnit]!!.size).isEqualTo(1) + assertThat(result.dimensionItems[Dimension.Period]).isEqualTo( + listOf( + DimensionItem.PeriodItem.Absolute("2018") + ) + ) + assertThat(result.metadata).isNotEmpty() + assertThat(result.values.size).isEqualTo(1) + } + + @Test + fun evaluate_visualization_wit_organisation_units() { + val result = d2.analyticsModule().visualizations() + .withVisualization(visualizationUid) + .withOrganisationUnits( + listOf( + DimensionItem.OrganisationUnitItem.Relative(RelativeOrganisationUnit.USER_ORGUNIT) + ) + ) + .blockingEvaluate() + + assertThat(result.dimensions.columns.size).isEqualTo(1) + assertThat(result.dimensions.rows.size).isEqualTo(1) + assertThat(result.dimensionItems[Dimension.Data]!!.size).isEqualTo(1) + assertThat(result.dimensionItems[Dimension.OrganisationUnit]).isEqualTo( + listOf( + DimensionItem.OrganisationUnitItem.Relative(RelativeOrganisationUnit.USER_ORGUNIT) + ) + ) + assertThat(result.dimensionItems[Dimension.Period]!!.size).isEqualTo(3) + assertThat(result.metadata).isNotEmpty() + assertThat(result.values.size).isEqualTo(3) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluatorIntegrationShould.kt new file mode 100644 index 0000000000..f0bac9912a --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluatorIntegrationShould.kt @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2004-2021, 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.DataElementEvaluatorSamples.category +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.categoryCategoryComboLink +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.categoryCategoryOptionLink +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.categoryCombo +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.categoryOption +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.categoryOptionCombo +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.categoryOptionComboCategoryOptionLink +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.dataElement1 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.dataElement2 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.dataElementOperand +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.orgunitChild1 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.orgunitChild2 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.orgunitParent +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.periodDec +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.periodNov +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.DataElementEvaluatorSamples.periodQ4 +import org.hisp.dhis.android.core.category.internal.* +import org.hisp.dhis.android.core.common.RelativeOrganisationUnit +import org.hisp.dhis.android.core.common.RelativePeriod +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.organisationunit.internal.OrganisationUnitStore +import org.hisp.dhis.android.core.period.internal.PeriodStoreImpl +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyDispatcher +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class DataElementEvaluatorIntegrationShould : BaseMockIntegrationTestEmptyDispatcher() { + + private val dataElementEvaluator = DataElementEvaluator(databaseAdapter) + + // Stores + private val dataValueStore = DataValueStore.create(databaseAdapter) + private val categoryStore = CategoryStore.create(databaseAdapter) + private val categoryOptionStore = CategoryOptionStore.create(databaseAdapter) + private val categoryCategoryOptionStore = CategoryCategoryOptionLinkStore.create(databaseAdapter) + private val categoryComboStore = CategoryComboStore.create(databaseAdapter) + private val categoryOptionComboStore = CategoryOptionComboStoreImpl.create(databaseAdapter) + private val categoryCategoryComboLinkStore = CategoryCategoryComboLinkStore.create(databaseAdapter) + private val categoryOptionComboCategoryOptionLinkStore = CategoryOptionComboCategoryOptionLinkStore.create( + databaseAdapter + ) + private val dataElementStore = DataElementStore.create(databaseAdapter) + private val organisationUnitStore = OrganisationUnitStore.create(databaseAdapter) + private val periodStore = PeriodStoreImpl.create(databaseAdapter) + + val metadata: Map = mapOf( + orgunitParent.uid() to MetadataItem.OrganisationUnitItem(orgunitParent), + orgunitChild1.uid() to MetadataItem.OrganisationUnitItem(orgunitChild1), + orgunitChild2.uid() to MetadataItem.OrganisationUnitItem(orgunitChild2), + dataElement1.uid() to MetadataItem.DataElementItem(dataElement1), + dataElement2.uid() to MetadataItem.DataElementItem(dataElement2), + dataElementOperand.uid()!! to MetadataItem.DataElementOperandItem(dataElementOperand), + periodNov.periodId()!! to MetadataItem.PeriodItem(periodNov), + periodDec.periodId()!! to MetadataItem.PeriodItem(periodDec), + periodQ4.periodId()!! to MetadataItem.PeriodItem(periodQ4), + RelativeOrganisationUnit.USER_ORGUNIT.name to MetadataItem.OrganisationUnitRelativeItem( + RelativeOrganisationUnit.USER_ORGUNIT, + listOf(orgunitParent) + ), + RelativeOrganisationUnit.USER_ORGUNIT_CHILDREN.name to MetadataItem.OrganisationUnitRelativeItem( + RelativeOrganisationUnit.USER_ORGUNIT_CHILDREN, + listOf(orgunitChild1, orgunitChild2) + ), + RelativePeriod.THIS_MONTH.name to MetadataItem.RelativePeriodItem( + RelativePeriod.THIS_MONTH, + listOf(periodDec) + ), + RelativePeriod.LAST_MONTH.name to MetadataItem.RelativePeriodItem( + RelativePeriod.LAST_MONTH, + listOf(periodNov) + ) + ) + + @Before + fun setUp() { + setUpClass() + + organisationUnitStore.insert(orgunitParent) + organisationUnitStore.insert(orgunitChild1) + organisationUnitStore.insert(orgunitChild2) + + categoryStore.insert(category) + categoryOptionStore.insert(categoryOption) + categoryCategoryOptionStore.insert(categoryCategoryOptionLink) + categoryComboStore.insert(categoryCombo) + categoryOptionComboStore.insert(categoryOptionCombo) + categoryCategoryComboLinkStore.insert(categoryCategoryComboLink) + categoryOptionComboCategoryOptionLinkStore.insert(categoryOptionComboCategoryOptionLink) + + dataElementStore.insert(dataElement1) + dataElementStore.insert(dataElement2) + + periodStore.insert(periodNov) + periodStore.insert(periodDec) + periodStore.insert(periodQ4) + } + + @After + fun tearDown() { + organisationUnitStore.delete() + categoryStore.delete() + categoryOptionStore.delete() + categoryCategoryOptionStore.delete() + categoryComboStore.delete() + categoryOptionComboStore.delete() + categoryCategoryComboLinkStore.delete() + categoryOptionComboCategoryOptionLinkStore.delete() + dataElementStore.delete() + periodStore.delete() + dataValueStore.delete() + } + + @Test + fun should_aggregate_value_in_hierarchy() { + createDataValue("2", orgunitUid = orgunitParent.uid()) + createDataValue("3", orgunitUid = orgunitChild1.uid()) + createDataValue("4", orgunitUid = orgunitChild2.uid()) + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()), + DimensionItem.PeriodItem.Absolute(periodDec.periodId()!!) + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("9") + } + + @Test + fun should_aggregate_value_in_time() { + createDataValue("2", periodId = periodNov.periodId()!!) + createDataValue("3", periodId = periodDec.periodId()!!) + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()), + DimensionItem.PeriodItem.Absolute(periodQ4.periodId()!!) + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("5") + } + + @Test + fun should_aggregate_relative_periods() { + createDataValue("2", periodId = periodNov.periodId()!!) + createDataValue("3", periodId = periodDec.periodId()!!) + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()) + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()), + DimensionItem.PeriodItem.Relative(RelativePeriod.LAST_MONTH), + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("5") + } + + @Test + fun should_aggregate_data_elements_if_defined_as_filter() { + createDataValue("2", dataElementUid = dataElement1.uid()) + createDataValue("3", dataElementUid = dataElement2.uid()) + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()) + ), + filters = listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()), + DimensionItem.DataItem.DataElementItem(dataElement2.uid()), + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("5") + } + + @Test + fun should_use_data_element_aggregation_type() { + createDataValue("2", orgunitUid = orgunitChild1.uid(), dataElementUid = dataElement2.uid()) + createDataValue("3", orgunitUid = orgunitChild2.uid(), dataElementUid = dataElement2.uid()) + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementItem(dataElement2.uid()) + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()), + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("2.5") + } + + @Test + fun should_disaggregate_by_category_option() { + createDataValue("2") + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()), + DimensionItem.CategoryItem(category.uid(), categoryOption.uid()) + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()), + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("2") + } + + @Test + fun should_ignore_missing_category_option() { + createDataValue("2") + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()), + DimensionItem.CategoryItem(category.uid(), categoryOption = "non-existing-co") + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()), + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isNull() + } + + @Test + fun should_evaluate_data_element_operand() { + createDataValue("2") + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementOperandItem(dataElement1.uid(), categoryOptionCombo.uid()) + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunitParent.uid()), + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("2") + } + + @Test + fun should_evaluate_relative_user_orgunit() { + createDataValue("2", orgunitUid = orgunitChild1.uid()) + createDataValue("3", orgunitUid = orgunitChild2.uid()) + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()) + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Relative(RelativeOrganisationUnit.USER_ORGUNIT), + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("5") + } + + @Test + fun should_aggregate_relative_user_children_as_filter() { + createDataValue("2", orgunitUid = orgunitChild1.uid()) + createDataValue("3", orgunitUid = orgunitChild2.uid()) + + val evaluationItem = AnalyticsServiceEvaluationItem( + dimensionItems = listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()) + ), + filters = listOf( + DimensionItem.OrganisationUnitItem.Relative(RelativeOrganisationUnit.USER_ORGUNIT_CHILDREN), + DimensionItem.PeriodItem.Relative(RelativePeriod.THIS_MONTH) + ) + ) + + val value = dataElementEvaluator.evaluate(evaluationItem, metadata) + + assertThat(value).isEqualTo("5") + } + + private fun createDataValue( + value: String, + dataElementUid: String = dataElement1.uid(), + orgunitUid: String = orgunitParent.uid(), + periodId: String = periodDec.periodId()!! + ) { + val dataValue = DataValue.builder() + .value(value) + .dataElement(dataElementUid) + .period(periodId) + .organisationUnit(orgunitUid) + .categoryOptionCombo(categoryOptionCombo.uid()) + .attributeOptionCombo(categoryOptionCombo.uid()) + .build() + + dataValueStore.insert(dataValue) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluatorSamples.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluatorSamples.kt new file mode 100644 index 0000000000..1c3dfbdfda --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluatorSamples.kt @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2004-2021, 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.arch.helpers.DateUtils +import org.hisp.dhis.android.core.arch.helpers.UidGeneratorImpl +import org.hisp.dhis.android.core.category.* +import org.hisp.dhis.android.core.common.AggregationType +import org.hisp.dhis.android.core.common.ObjectWithUid +import org.hisp.dhis.android.core.common.ValueType +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.dataelement.DataElementOperand +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.PeriodType + +object DataElementEvaluatorSamples { + + val generator = UidGeneratorImpl() + + val orgunitParent: OrganisationUnit by lazy { + val uid = generator.generate() + OrganisationUnit.builder() + .uid(uid) + .displayName("Child 1") + .path("/$uid") + .build() + } + + val orgunitChild1: OrganisationUnit by lazy { + val uid = generator.generate() + OrganisationUnit.builder() + .uid(uid) + .displayName("Child 1") + .parent(ObjectWithUid.create(orgunitParent.uid())) + .path("/${orgunitParent.uid()}/$uid") + .build() + } + + val orgunitChild2: OrganisationUnit by lazy { + val uid = generator.generate() + OrganisationUnit.builder() + .uid(uid) + .displayName("Child 2") + .parent(ObjectWithUid.create(orgunitParent.uid())) + .path("/${orgunitParent.uid()}/$uid") + .build() + } + + val category: Category = Category.builder() + .uid(generator.generate()) + .build() + + val categoryOption: CategoryOption = CategoryOption.builder() + .uid(generator.generate()) + .build() + + val categoryCategoryOptionLink: CategoryCategoryOptionLink = CategoryCategoryOptionLink.builder() + .category(category.uid()) + .categoryOption(categoryOption.uid()) + .build() + + val categoryCombo: CategoryCombo = CategoryCombo.builder() + .uid(generator.generate()) + .build() + + val categoryOptionCombo: CategoryOptionCombo = CategoryOptionCombo.builder() + .uid(generator.generate()) + .categoryCombo(ObjectWithUid.fromIdentifiable(categoryCombo)) + .build() + + val categoryCategoryComboLink: CategoryCategoryComboLink = CategoryCategoryComboLink.builder() + .category(category.uid()) + .categoryCombo(categoryCombo.uid()) + .build() + + val categoryOptionComboCategoryOptionLink = CategoryOptionComboCategoryOptionLink.builder() + .categoryOption(categoryOption.uid()) + .categoryOptionCombo(categoryOptionCombo.uid()) + .build() + + val dataElement1 = DataElement.builder() + .uid(generator.generate()) + .displayName("Data element 1") + .valueType(ValueType.NUMBER) + .aggregationType(AggregationType.SUM.name) + .categoryCombo(ObjectWithUid.fromIdentifiable(categoryCombo)) + .build() + + val dataElement2 = DataElement.builder() + .uid(generator.generate()) + .displayName("Data element 2") + .valueType(ValueType.INTEGER) + .aggregationType(AggregationType.AVERAGE.name) + .categoryCombo(ObjectWithUid.fromIdentifiable(categoryCombo)) + .build() + + val dataElementOperand = DataElementOperand.builder() + .uid("${dataElement1.uid()}.${categoryOptionCombo.uid()}") + .dataElement(ObjectWithUid.create(dataElement1.uid())) + .categoryOptionCombo(ObjectWithUid.create(categoryOptionCombo.uid())) + .build() + + val periodNov: Period = Period.builder() + .periodId("201911") + .periodType(PeriodType.Monthly) + .startDate(DateUtils.DATE_FORMAT.parse("2019-11-01T00:00:00.000")) + .endDate(DateUtils.DATE_FORMAT.parse("2019-11-30T23:59:59.999")) + .build() + + val periodDec: Period = Period.builder() + .periodId("201912") + .periodType(PeriodType.Monthly) + .startDate(DateUtils.DATE_FORMAT.parse("2019-12-01T00:00:00.000")) + .endDate(DateUtils.DATE_FORMAT.parse("2019-12-31T23:59:59.999")) + .build() + + val periodQ4: Period = Period.builder() + .periodId("2019Q4") + .periodType(PeriodType.Quarterly) + .startDate(DateUtils.DATE_FORMAT.parse("2019-10-01T00:00:00.000")) + .endDate(DateUtils.DATE_FORMAT.parse("2021-12-31T23:59:59.999")) + .build() +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorIntegrationShould.java deleted file mode 100644 index c2d1e6e51c..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorIntegrationShould.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.common.internal; - -import androidx.test.runner.AndroidJUnit4; - -import org.hisp.dhis.android.core.common.BaseIdentifiableObject; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.enrollment.Enrollment; -import org.hisp.dhis.android.core.enrollment.EnrollmentCreateProjection; -import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore; -import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStoreImpl; -import org.hisp.dhis.android.core.event.Event; -import org.hisp.dhis.android.core.event.EventCreateProjection; -import org.hisp.dhis.android.core.event.internal.EventStore; -import org.hisp.dhis.android.core.event.internal.EventStoreImpl; -import org.hisp.dhis.android.core.maintenance.D2Error; -import org.hisp.dhis.android.core.relationship.RelationshipHelper; -import org.hisp.dhis.android.core.relationship.RelationshipItem; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceCreateProjection; -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStore; -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStoreImpl; -import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.IOException; -import java.text.ParseException; -import java.util.Date; - -import static com.google.common.truth.Truth.assertThat; - -@RunWith(AndroidJUnit4.class) -public class DataStatePropagatorIntegrationShould extends BaseMockIntegrationTestFullDispatcher { - - private DataStatePropagator propagator; - private TrackedEntityInstanceStore trackedEntityInstanceStore; - private EnrollmentStore enrollmentStore; - private EventStore eventStore; - - @Before - public void setUp() throws IOException { - this.trackedEntityInstanceStore = TrackedEntityInstanceStoreImpl.create(d2.databaseAdapter()); - this.enrollmentStore = EnrollmentStoreImpl.create(d2.databaseAdapter()); - this.eventStore = EventStoreImpl.create(d2.databaseAdapter()); - this.propagator = new DataStatePropagatorImpl( - trackedEntityInstanceStore, - enrollmentStore, - eventStore); - } - - @Test - public void set_parent_state_to_update_if_has_synced_state() throws D2Error { - assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.SYNCED); - assertThatSetTeiToUpdateWhenEventPropagation(State.SYNCED); - assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.SYNCED); - } - - @Test - public void set_parent_state_to_update_if_has_to_update_state() throws D2Error { - assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.TO_UPDATE); - assertThatSetTeiToUpdateWhenEventPropagation(State.TO_UPDATE); - assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.TO_UPDATE); - } - - @Test - public void set_parent_state_to_update_if_has_error_state() throws D2Error { - assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.ERROR); - assertThatSetTeiToUpdateWhenEventPropagation(State.ERROR); - assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.ERROR); - } - - @Test - public void set_parent_state_to_update_if_has_warning_state() throws D2Error { - assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.WARNING); - assertThatSetTeiToUpdateWhenEventPropagation(State.WARNING); - assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.WARNING); - } - - @Test - public void do_not_set_parent_state_to_update_if_has_to_post_state() throws D2Error { - assertThatDoNotSetTeiToUpdateWhenEnrollmentPropagation(State.TO_POST); - assertThatDoNotSetTeiToUpdateWhenEventPropagation(State.TO_POST); - assertThatDoNotSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.TO_POST); - } - - @Test - public void do_not_fail_with_events_without_registration() throws D2Error { - String eventUid = d2.eventModule().events().blockingAdd(sampleEventProjection(null)); - - assertThat(eventStore.selectByUid(eventUid).state()).isEqualTo(State.TO_POST); - eventStore.delete(eventUid); - } - - @Test - public void reset_enrollment_and_event_states_if_uploading() throws D2Error { - String teiUid = d2.trackedEntityModule().trackedEntityInstances().blockingAdd(sampleTEIProjection()); - String enrolmentUid1 = d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)); - String enrolmentUid2 = d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)); - - String eventUid1 = d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid1)); - String eventUid2 = d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid1)); - String eventUid3 = d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid2)); - - enrollmentStore.setState(enrolmentUid1, State.UPLOADING); - eventStore.setState(eventUid1, State.UPLOADING); - - propagator.resetUploadingEnrollmentAndEventStates(teiUid); - - assertThat(enrollmentStore.getState(enrolmentUid1)).isEqualTo(State.TO_UPDATE); - assertThat(enrollmentStore.getState(enrolmentUid2)).isEqualTo(State.TO_POST); - - assertThat(eventStore.getState(eventUid1)).isEqualTo(State.TO_UPDATE); - assertThat(eventStore.getState(eventUid2)).isEqualTo(State.TO_POST); - assertThat(eventStore.getState(eventUid3)).isEqualTo(State.TO_POST); - - trackedEntityInstanceStore.delete(teiUid); - } - - @Test - public void propagate_last_updated_if_previous_is_older() throws D2Error, ParseException { - Date oldDate = BaseIdentifiableObject.DATE_FORMAT.parse("1990-09-20T08:36:46.552"); - String teiUid = createTEIWithLastUpdated(oldDate); - - propagator.propagateEnrollmentUpdate(Enrollment.builder().uid("uid").trackedEntityInstance(teiUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).lastUpdated()).isGreaterThan(oldDate); - trackedEntityInstanceStore.delete(teiUid); - } - - @Test - public void do_not_propagate_last_updated_if_previous_is_newer() throws D2Error, ParseException { - Date newerDate = BaseIdentifiableObject.DATE_FORMAT.parse("2990-09-20T08:36:46.552"); - String teiUid = createTEIWithLastUpdated(newerDate); - - propagator.propagateEnrollmentUpdate(Enrollment.builder().uid("uid").trackedEntityInstance(teiUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).lastUpdated()).isEqualTo(newerDate); - trackedEntityInstanceStore.delete(teiUid); - } - - @Test - public void propagate_last_updated_if_previous_is_null() throws D2Error { - String teiUid = createTEIWithLastUpdated(null); - - propagator.propagateEnrollmentUpdate(Enrollment.builder().uid("uid").trackedEntityInstance(teiUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).lastUpdated()).isNotNull(); - trackedEntityInstanceStore.delete(teiUid); - } - - @Test - public void propagate_tei_relationship_update() throws D2Error { - String teiUid = createTEIWithState(State.SYNCED); - RelationshipItem fromItem = RelationshipHelper.teiItem(teiUid); - - propagator.propagateRelationshipUpdate(fromItem); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(State.TO_UPDATE); - - trackedEntityInstanceStore.delete(teiUid); - } - - @Test - public void propagate_enrollment_relationship_update() throws D2Error { - String teiUid = createTEIWithState(State.SYNCED); - String enrollmentUid = createEnrollmentWithState(State.SYNCED, teiUid); - RelationshipItem fromItem = RelationshipHelper.enrollmentItem(enrollmentUid); - - propagator.propagateRelationshipUpdate(fromItem); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(State.TO_UPDATE); - assertThat(enrollmentStore.selectByUid(enrollmentUid).state()).isEqualTo(State.TO_UPDATE); - - trackedEntityInstanceStore.delete(teiUid); - } - - @Test - public void propagate_event_relationship_update() throws D2Error { - String teiUid = createTEIWithState(State.SYNCED); - String enrollmentUid = createEnrollmentWithState(State.SYNCED, teiUid); - String eventUid = createEventWithState(State.SYNCED, enrollmentUid); - RelationshipItem fromItem = RelationshipHelper.eventItem(eventUid); - - propagator.propagateRelationshipUpdate(fromItem); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(State.TO_UPDATE); - assertThat(enrollmentStore.selectByUid(enrollmentUid).state()).isEqualTo(State.TO_UPDATE); - assertThat(eventStore.selectByUid(eventUid).state()).isEqualTo(State.TO_UPDATE); - - trackedEntityInstanceStore.delete(teiUid); - } - - private void assertThatSetTeiToUpdateWhenEnrollmentPropagation(State state) throws D2Error { - String teiUid = createTEIWithState(state); - - propagator.propagateEnrollmentUpdate(Enrollment.builder().uid("uid").trackedEntityInstance(teiUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(State.TO_UPDATE); - trackedEntityInstanceStore.delete(teiUid); - } - - private void assertThatDoNotSetTeiToUpdateWhenEnrollmentPropagation(State state) throws D2Error { - String teiUid = createTEIWithState(state); - - propagator.propagateEnrollmentUpdate(Enrollment.builder().uid("uid").trackedEntityInstance(teiUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(state); - trackedEntityInstanceStore.delete(teiUid); - } - - private void assertThatSetTeiToUpdateWhenEventPropagation(State state) throws D2Error { - String teiUid = createTEIWithState(state); - - String enrolmentUid = d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)); - enrollmentStore.setState(enrolmentUid, state); - - propagator.propagateEventUpdate(Event.builder().uid("uid").enrollment(enrolmentUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(State.TO_UPDATE); - assertThat(enrollmentStore.selectByUid(enrolmentUid).state()).isEqualTo(State.TO_UPDATE); - trackedEntityInstanceStore.delete(teiUid); - } - - private void assertThatDoNotSetTeiToUpdateWhenEventPropagation(State state) throws D2Error { - String teiUid = createTEIWithState(state); - String enrolmentUid = createEnrollmentWithState(state, teiUid); - - propagator.propagateEventUpdate(Event.builder().uid("uid").enrollment(enrolmentUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(state); - assertThat(enrollmentStore.selectByUid(enrolmentUid).state()).isEqualTo(state); - trackedEntityInstanceStore.delete(teiUid); - } - - private void assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State state) throws D2Error { - String teiUid = createTEIWithState(state); - String enrolmentUid = createEnrollmentWithState(state, teiUid); - String eventUid = createEventWithState(state, enrolmentUid); - - propagator.propagateTrackedEntityDataValueUpdate(TrackedEntityDataValue.builder().event(eventUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(State.TO_UPDATE); - assertThat(enrollmentStore.selectByUid(enrolmentUid).state()).isEqualTo(State.TO_UPDATE); - assertThat(eventStore.selectByUid(eventUid).state()).isEqualTo(State.TO_UPDATE); - trackedEntityInstanceStore.delete(teiUid); - } - - private void assertThatDoNotSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State state) throws D2Error { - String teiUid = createTEIWithState(state); - String enrolmentUid = createEnrollmentWithState(state, teiUid); - String eventUid = createEventWithState(state, enrolmentUid); - - propagator.propagateTrackedEntityDataValueUpdate(TrackedEntityDataValue.builder().event(eventUid).build()); - - assertThat(trackedEntityInstanceStore.selectByUid(teiUid).state()).isEqualTo(state); - assertThat(enrollmentStore.selectByUid(enrolmentUid).state()).isEqualTo(state); - assertThat(eventStore.selectByUid(eventUid).state()).isEqualTo(state); - trackedEntityInstanceStore.delete(teiUid); - } - - private String createTEIWithState(State state) throws D2Error { - String teiUid = d2.trackedEntityModule().trackedEntityInstances().blockingAdd(sampleTEIProjection()); - trackedEntityInstanceStore.setState(teiUid, state); - return teiUid; - } - - private String createEnrollmentWithState(State state, String teiUid) throws D2Error { - String enrolmentUid = d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)); - enrollmentStore.setState(enrolmentUid, state); - return enrolmentUid; - } - - private String createEventWithState(State state, String enrolmentUid) throws D2Error { - String eventUid = d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid)); - eventStore.setState(eventUid, state); - return eventUid; - } - - private String createTEIWithLastUpdated(Date lastUpdated) throws D2Error { - String teiUid = d2.trackedEntityModule().trackedEntityInstances().blockingAdd(sampleTEIProjection()); - - TrackedEntityInstance existingTEI = trackedEntityInstanceStore.selectByUid(teiUid); - trackedEntityInstanceStore.update(existingTEI.toBuilder().lastUpdated(lastUpdated).build()); - - return teiUid; - } - - private TrackedEntityInstanceCreateProjection sampleTEIProjection() { - return TrackedEntityInstanceCreateProjection.create("DiszpKrYNg8", "nEenWmSyUEp"); - } - - private EnrollmentCreateProjection sampleEnrollmentProjection(String teiUid) { - return EnrollmentCreateProjection.create("DiszpKrYNg8", "lxAQ7Zs9VYR", teiUid); - } - - private EventCreateProjection sampleEventProjection(String enrollmentUid) { - return EventCreateProjection.create(enrollmentUid, "lxAQ7Zs9VYR", "dBwrot7S420", - "DiszpKrYNg8", "bRowv6yZOF2"); - } -} \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorIntegrationShould.kt new file mode 100644 index 0000000000..a58e57d75c --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorIntegrationShould.kt @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2004-2021, 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.common.internal + +import androidx.test.runner.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import java.io.IOException +import java.text.ParseException +import java.util.* +import org.hisp.dhis.android.core.common.BaseIdentifiableObject +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.EnrollmentCreateProjection +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStoreImpl.Companion.create +import org.hisp.dhis.android.core.event.EventCreateProjection +import org.hisp.dhis.android.core.event.internal.EventStore +import org.hisp.dhis.android.core.event.internal.EventStoreImpl +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.relationship.internal.RelationshipItemStore +import org.hisp.dhis.android.core.relationship.internal.RelationshipItemStoreImpl +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore +import org.hisp.dhis.android.core.relationship.internal.RelationshipStoreImpl +import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceCreateProjection +import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStore +import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStoreImpl +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class DataStatePropagatorIntegrationShould : BaseMockIntegrationTestFullDispatcher() { + private lateinit var propagator: DataStatePropagator + private lateinit var trackedEntityInstanceStore: TrackedEntityInstanceStore + private lateinit var enrollmentStore: EnrollmentStore + private lateinit var eventStore: EventStore + private lateinit var relationshipStore: RelationshipStore + private lateinit var relationshipItemStore: RelationshipItemStore + + private val relationshipType = "WiH6923nMtb" + + @Before + @Throws(IOException::class) + fun setUp() { + trackedEntityInstanceStore = TrackedEntityInstanceStoreImpl.create(d2.databaseAdapter()) + enrollmentStore = create(d2.databaseAdapter()) + eventStore = EventStoreImpl.create(d2.databaseAdapter()) + relationshipStore = RelationshipStoreImpl.create(d2.databaseAdapter()) + relationshipItemStore = RelationshipItemStoreImpl.create(d2.databaseAdapter()) + + propagator = DataStatePropagatorImpl( + trackedEntityInstanceStore, enrollmentStore, + eventStore, relationshipStore, relationshipItemStore + ) + } + + @Test + @Throws(D2Error::class) + fun set_parent_state_to_update_if_has_synced_state() { + assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.SYNCED) + assertThatSetTeiToUpdateWhenEventPropagation(State.SYNCED) + assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.SYNCED) + } + + @Test + @Throws(D2Error::class) + fun set_parent_state_to_update_if_has_to_update_state() { + assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.TO_UPDATE) + assertThatSetTeiToUpdateWhenEventPropagation(State.TO_UPDATE) + assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.TO_UPDATE) + } + + @Test + @Throws(D2Error::class) + fun set_parent_state_to_update_if_has_to_post_state() { + assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.TO_POST) + assertThatSetTeiToUpdateWhenEventPropagation(State.TO_POST) + assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.TO_POST) + } + + @Test + @Throws(D2Error::class) + fun set_parent_state_to_update_if_has_to_update_synced_via_sms() { + assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.SYNCED_VIA_SMS) + assertThatSetTeiToUpdateWhenEventPropagation(State.SYNCED_VIA_SMS) + assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.SYNCED_VIA_SMS) + } + + @Test + @Throws(D2Error::class) + fun set_parent_state_to_update_if_has_to_update_sent_via_sms() { + assertThatSetTeiToUpdateWhenEnrollmentPropagation(State.SENT_VIA_SMS) + assertThatSetTeiToUpdateWhenEventPropagation(State.SENT_VIA_SMS) + assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.SENT_VIA_SMS) + } + + @Test + @Throws(D2Error::class) + fun do_not_set_parent_state_to_update_if_has_error_state() { + assertThatDoNotSetTeiToUpdateWhenEnrollmentPropagation(State.ERROR) + assertThatDoNotSetTeiToUpdateWhenEventPropagation(State.ERROR) + assertThatDoNotSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.ERROR) + } + + @Test + @Throws(D2Error::class) + fun do_not_set_parent_state_to_update_if_has_warning_state() { + assertThatDoNotSetTeiToUpdateWhenEnrollmentPropagation(State.WARNING) + assertThatDoNotSetTeiToUpdateWhenEventPropagation(State.WARNING) + assertThatDoNotSetTeiToUpdateWhenTrackedEntityDataValuePropagation(State.WARNING) + } + + @Test + @Throws(D2Error::class) + fun do_not_fail_with_events_without_registration() { + val eventUid = d2.eventModule().events().blockingAdd(sampleEventProjection(null)) + + assertThat(eventStore.selectByUid(eventUid)!!.syncState()).isEqualTo(State.TO_POST) + + eventStore.delete(eventUid) + } + + @Test + @Throws(D2Error::class) + fun reset_enrollment_and_event_states_if_uploading() { + val teiUid = d2.trackedEntityModule().trackedEntityInstances().blockingAdd(sampleTEIProjection()) + val enrolmentUid1 = d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)) + val enrolmentUid2 = d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)) + val eventUid1 = d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid1)) + val eventUid2 = d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid1)) + val eventUid3 = d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid2)) + + enrollmentStore.setSyncState(enrolmentUid1, State.UPLOADING) + eventStore.setSyncState(eventUid1, State.UPLOADING) + propagator.resetUploadingEnrollmentAndEventStates(teiUid) + + assertThat(enrollmentStore.getSyncState(enrolmentUid1)).isEqualTo(State.TO_UPDATE) + assertThat(enrollmentStore.getSyncState(enrolmentUid2)).isEqualTo(State.TO_POST) + + assertThat(eventStore.getSyncState(eventUid1)).isEqualTo(State.TO_UPDATE) + assertThat(eventStore.getSyncState(eventUid2)).isEqualTo(State.TO_POST) + assertThat(eventStore.getSyncState(eventUid3)).isEqualTo(State.TO_POST) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Test + @Throws(D2Error::class, ParseException::class) + fun propagate_last_updated_if_previous_is_older() { + val oldDate = BaseIdentifiableObject.DATE_FORMAT.parse("1990-09-20T08:36:46.552") + val teiUid = createTEIWithLastUpdated(oldDate) + + propagator.propagateEnrollmentUpdate(Enrollment.builder().uid("uid").trackedEntityInstance(teiUid).build()) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.lastUpdated()).isGreaterThan(oldDate) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Test + @Throws(D2Error::class, ParseException::class) + fun do_not_propagate_last_updated_if_previous_is_newer() { + val newerDate = BaseIdentifiableObject.DATE_FORMAT.parse("2990-09-20T08:36:46.552") + val teiUid = createTEIWithLastUpdated(newerDate) + + propagator.propagateEnrollmentUpdate(Enrollment.builder().uid("uid").trackedEntityInstance(teiUid).build()) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.lastUpdated()).isEqualTo(newerDate) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Test + @Throws(D2Error::class) + fun propagate_last_updated_if_previous_is_null() { + val teiUid = createTEIWithLastUpdated(null) + d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.lastUpdated()).isNotNull() + + trackedEntityInstanceStore.delete(teiUid) + } + + @Test + @Throws(D2Error::class) + fun propagate_tei_relationship_update() { + val teiUid = createTEIWithState(State.SYNCED) + + val relationshipUid = d2.relationshipModule().relationships().blockingAdd( + Relationship.builder() + .relationshipType(relationshipType) + .from(RelationshipHelper.teiItem(teiUid)) + .to(RelationshipHelper.teiItem(teiUid)) + .build() + ) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(State.SYNCED) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + + trackedEntityInstanceStore.delete(teiUid) + relationshipStore.delete(relationshipUid) + } + + @Test + @Throws(D2Error::class) + fun propagate_enrollment_relationship_update() { + val teiUid = createTEIWithState(State.SYNCED) + val enrollmentUid = createEnrollmentWithState(State.SYNCED, teiUid) + + val relationshipUid = d2.relationshipModule().relationships().blockingAdd( + Relationship.builder() + .relationshipType(relationshipType) + .from(RelationshipHelper.enrollmentItem(enrollmentUid)) + .to(RelationshipHelper.enrollmentItem(enrollmentUid)) + .build() + ) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(State.SYNCED) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + assertThat(enrollmentStore.selectByUid(enrollmentUid)!!.syncState()).isEqualTo(State.SYNCED) + assertThat(enrollmentStore.selectByUid(enrollmentUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + + trackedEntityInstanceStore.delete(teiUid) + relationshipStore.delete(relationshipUid) + } + + @Test + @Throws(D2Error::class) + fun propagate_event_relationship_update() { + val teiUid = createTEIWithState(State.SYNCED) + val enrollmentUid = createEnrollmentWithState(State.SYNCED, teiUid) + val eventUid = createEventWithState(State.SYNCED, enrollmentUid) + + val relationshipUid = d2.relationshipModule().relationships().blockingAdd( + Relationship.builder() + .relationshipType(relationshipType) + .from(RelationshipHelper.eventItem(eventUid)) + .to(RelationshipHelper.eventItem(eventUid)) + .build() + ) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(State.SYNCED) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + assertThat(enrollmentStore.selectByUid(enrollmentUid)!!.syncState()).isEqualTo(State.SYNCED) + assertThat(enrollmentStore.selectByUid(enrollmentUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + assertThat(eventStore.selectByUid(eventUid)!!.syncState()).isEqualTo(State.SYNCED) + assertThat(eventStore.selectByUid(eventUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + + trackedEntityInstanceStore.delete(teiUid) + relationshipStore.delete(relationshipUid) + } + + @Throws(D2Error::class) + private fun assertThatSetTeiToUpdateWhenEnrollmentPropagation(state: State) { + val teiUid = createTEIWithState(state) + d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(state) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Throws(D2Error::class) + private fun assertThatDoNotSetTeiToUpdateWhenEnrollmentPropagation(state: State) { + val teiUid = createTEIWithState(state) + d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(state) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(state) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Throws(D2Error::class) + private fun assertThatSetTeiToUpdateWhenEventPropagation(state: State) { + val teiUid = createTEIWithState(state) + val enrolmentUid = d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)) + + enrollmentStore.setSyncState(enrolmentUid, state) + enrollmentStore.setAggregatedSyncState(enrolmentUid, state) + + d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid)) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(state) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + assertThat(enrollmentStore.selectByUid(enrolmentUid)!!.syncState()).isEqualTo(state) + assertThat(enrollmentStore.selectByUid(enrolmentUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Throws(D2Error::class) + private fun assertThatDoNotSetTeiToUpdateWhenEventPropagation(state: State) { + val teiUid = createTEIWithState(state) + val enrolmentUid = createEnrollmentWithState(state, teiUid) + + d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid)) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(state) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(state) + assertThat(enrollmentStore.selectByUid(enrolmentUid)!!.syncState()).isEqualTo(state) + assertThat(enrollmentStore.selectByUid(enrolmentUid)!!.aggregatedSyncState()).isEqualTo(state) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Throws(D2Error::class) + private fun assertThatSetTeiToUpdateWhenTrackedEntityDataValuePropagation(state: State) { + val teiUid = createTEIWithState(state) + val enrolmentUid = createEnrollmentWithState(state, teiUid) + val eventUid = createEventWithState(state, enrolmentUid) + + propagator.propagateTrackedEntityDataValueUpdate(TrackedEntityDataValue.builder().event(eventUid).build()) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(state) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + assertThat(enrollmentStore.selectByUid(enrolmentUid)!!.syncState()).isEqualTo(state) + assertThat(enrollmentStore.selectByUid(enrolmentUid)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + assertThat(eventStore.selectByUid(eventUid)!!.syncState()).isIn(listOf(State.TO_POST, State.TO_UPDATE)) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Throws(D2Error::class) + private fun assertThatDoNotSetTeiToUpdateWhenTrackedEntityDataValuePropagation(state: State) { + val teiUid = createTEIWithState(state) + val enrolmentUid = createEnrollmentWithState(state, teiUid) + val eventUid = createEventWithState(state, enrolmentUid) + + propagator.propagateTrackedEntityDataValueUpdate(TrackedEntityDataValue.builder().event(eventUid).build()) + + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.syncState()).isEqualTo(state) + assertThat(trackedEntityInstanceStore.selectByUid(teiUid)!!.aggregatedSyncState()).isEqualTo(state) + assertThat(enrollmentStore.selectByUid(enrolmentUid)!!.syncState()).isEqualTo(state) + assertThat(enrollmentStore.selectByUid(enrolmentUid)!!.aggregatedSyncState()).isEqualTo(state) + assertThat(eventStore.selectByUid(eventUid)!!.syncState()).isIn(listOf(State.TO_POST, State.TO_UPDATE)) + + trackedEntityInstanceStore.delete(teiUid) + } + + @Throws(D2Error::class) + private fun createTEIWithState(state: State): String { + val teiUid = d2.trackedEntityModule().trackedEntityInstances().blockingAdd(sampleTEIProjection()) + + trackedEntityInstanceStore.setSyncState(teiUid, state) + trackedEntityInstanceStore.setAggregatedSyncState(teiUid, state) + + return teiUid + } + + @Throws(D2Error::class) + private fun createEnrollmentWithState(state: State, teiUid: String): String { + val enrolmentUid = d2.enrollmentModule().enrollments().blockingAdd(sampleEnrollmentProjection(teiUid)) + + enrollmentStore.setSyncState(enrolmentUid, state) + enrollmentStore.setAggregatedSyncState(enrolmentUid, state) + + return enrolmentUid + } + + @Throws(D2Error::class) + private fun createEventWithState(state: State, enrolmentUid: String): String { + val eventUid = d2.eventModule().events().blockingAdd(sampleEventProjection(enrolmentUid)) + + eventStore.setSyncState(eventUid, state) + eventStore.setAggregatedSyncState(eventUid, state) + + return eventUid + } + + @Throws(D2Error::class) + private fun createTEIWithLastUpdated(lastUpdated: Date?): String { + val teiUid = d2.trackedEntityModule().trackedEntityInstances().blockingAdd(sampleTEIProjection()) + val existingTEI = trackedEntityInstanceStore.selectByUid(teiUid) + + trackedEntityInstanceStore.update(existingTEI!!.toBuilder().lastUpdated(lastUpdated).build()) + + return teiUid + } + + private fun sampleTEIProjection(): TrackedEntityInstanceCreateProjection { + return TrackedEntityInstanceCreateProjection.create("DiszpKrYNg8", "nEenWmSyUEp") + } + + private fun sampleEnrollmentProjection(teiUid: String): EnrollmentCreateProjection { + return EnrollmentCreateProjection.create("DiszpKrYNg8", "lxAQ7Zs9VYR", teiUid) + } + + private fun sampleEventProjection(enrollmentUid: String?): EventCreateProjection { + return EventCreateProjection.create( + enrollmentUid, "lxAQ7Zs9VYR", "dBwrot7S420", + "DiszpKrYNg8", "bRowv6yZOF2" + ) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/IdentifiableObjectStoreAbstractIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/IdentifiableObjectStoreAbstractIntegrationShould.java index f215023702..5d7483cbb2 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/IdentifiableObjectStoreAbstractIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/IdentifiableObjectStoreAbstractIntegrationShould.java @@ -38,6 +38,8 @@ import org.junit.Test; import java.io.IOException; +import java.util.Collections; +import java.util.List; import static com.google.common.truth.Truth.assertThat; @@ -67,6 +69,14 @@ public void insert_and_select_by_uid() { assertEqualsIgnoreId(objectFromDb); } + @Test + public void insert_and_select_by_uid_list() { + store.insert(object); + List listFromDb = store.selectByUids(Collections.singletonList(object.uid())); + assertThat(listFromDb.size()).isEqualTo(1); + assertEqualsIgnoreId(listFromDb.get(0)); + } + @Test public void select_inserted_object_uid() { store.insert(object); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/LinkStoreAbstractIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/LinkStoreAbstractIntegrationShould.java index 32716b4db6..d9b616b5d3 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/LinkStoreAbstractIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/LinkStoreAbstractIntegrationShould.java @@ -73,8 +73,8 @@ public void delete_link_for_master_uid() { @Test public void delete_links_for_master_should_delete_only_objects_with_the_master_key() { - store.insert(objectWithOtherMasterUid); store.insert(object); + store.insert(objectWithOtherMasterUid); store.deleteLinksForMasterUid(masterUid); M objectFromDb = store.selectFirst(); assertEqualsIgnoreId(objectFromDb, objectWithOtherMasterUid); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.java index 30687bce89..aa071723b8 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.java @@ -74,8 +74,7 @@ public void insert_and_select_first_object() { @Test public void insert_as_content_values_and_select_first_object() { - long rowsInserted = databaseAdapter.insert(tableInfo.name(), null, object.toContentValues()); - assertThat(rowsInserted).isEqualTo(1); + databaseAdapter.insert(tableInfo.name(), null, object.toContentValues()); M objectFromDb = store.selectFirst(); assertEqualsIgnoreId(objectFromDb); } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/data/server/RealServerMother.java b/core/src/androidTest/java/org/hisp/dhis/android/core/data/server/RealServerMother.java index 20e95a4f24..7af48ea08e 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/data/server/RealServerMother.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/data/server/RealServerMother.java @@ -31,11 +31,12 @@ public class RealServerMother { public static String url2_29 = "https://play.dhis2.org/2.29/"; public static String url2_30 = "https://play.dhis2.org/2.30/"; - public static String url2_31 = "https://play.dhis2.org/2.31.9/"; - public static String url2_32 = "https://play.dhis2.org/2.32.6/"; - public static String url2_33 = "https://play.dhis2.org/2.33.6/"; - public static String url2_34 = "https://play.dhis2.org/2.34.1/"; + public static String url2_31 = "https://play.dhis2.org/2.31/"; + public static String url2_32 = "https://play.dhis2.org/2.32/"; + public static String url2_33 = "https://play.dhis2.org/2.33/"; + public static String url2_34 = "https://play.dhis2.org/2.34/"; public static String url2_35 = "https://play.dhis2.org/2.35/"; + public static String url2_36 = "https://play.dhis2.org/2.36/"; public static String url_dev = "https://play.dhis2.org/dev/"; public static String android_current = "https://play.dhis2.org/android-current/"; public static String android_previous1 = "https://play.dhis2.org/android-previous1/"; diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/dataset/internal/DataSetCompleteRegistrationPostCallRealIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/dataset/internal/DataSetCompleteRegistrationPostCallRealIntegrationShould.java index a4dfea2c65..e83c6398ce 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/dataset/internal/DataSetCompleteRegistrationPostCallRealIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/dataset/internal/DataSetCompleteRegistrationPostCallRealIntegrationShould.java @@ -177,7 +177,7 @@ private DataSetCompleteRegistration getTestDataSetCompleteRegistrationWith(State .organisationUnit("DiszpKrYNg8") .date(dateFormat.parse("2010-03-02")) .storedBy("android") - .state(state) + .syncState(state) .build(); } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/datastore/LocalDataStoreStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/datastore/LocalDataStoreStoreIntegrationShould.kt new file mode 100644 index 0000000000..be6dd95bda --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/datastore/LocalDataStoreStoreIntegrationShould.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2004-2021, 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.datastore + +import org.hisp.dhis.android.core.data.database.ObjectWithoutUidStoreAbstractIntegrationShould +import org.hisp.dhis.android.core.data.datastore.KeyValuePairSamples.keyValuePairSample +import org.hisp.dhis.android.core.datastore.internal.LocalDataStoreStore.create +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 LocalDataStoreStoreIntegrationShould : ObjectWithoutUidStoreAbstractIntegrationShould( + create(TestDatabaseAdapterFactory.get()), + LocalDataStoreTableInfo.TABLE_INFO, + TestDatabaseAdapterFactory.get() +) { + override fun buildObject(): KeyValuePair { + return keyValuePairSample + } + + override fun buildObjectToUpdate(): KeyValuePair { + return keyValuePairSample + .toBuilder() + .value("value2") + .build() + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictStoreIntegrationShould.kt new file mode 100644 index 0000000000..e40b85fdd9 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictStoreIntegrationShould.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal + +import org.hisp.dhis.android.core.data.database.ObjectStoreAbstractIntegrationShould +import org.hisp.dhis.android.core.data.datavalue.DataValueConflictSamples +import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.datavalue.DataValueConflictTableInfo +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 DataValueConflictStoreIntegrationShould : ObjectStoreAbstractIntegrationShould( + DataValueConflictStore.create(TestDatabaseAdapterFactory.get()), + DataValueConflictTableInfo.TABLE_INFO, + TestDatabaseAdapterFactory.get() +) { + override fun buildObject(): DataValueConflict { + return DataValueConflictSamples.get() + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallMockIntegrationShould.kt index bad0bd18ba..dfa70a8428 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallMockIntegrationShould.kt @@ -28,8 +28,8 @@ class DataValuePostCallMockIntegrationShould : BaseMockIntegrationTestMetadataEn d2.dataValueModule().dataValues().blockingUpload() // Then all data set should be properly synced - val warnings = d2.dataValueModule().dataValues().byState().eq(State.SYNCED).blockingGet() - assertThat(warnings.size).isEqualTo(2) + val synced = d2.dataValueModule().dataValues().bySyncState().eq(State.SYNCED).blockingGet() + assertThat(synced.size).isEqualTo(2) } @Test @@ -42,7 +42,7 @@ class DataValuePostCallMockIntegrationShould : BaseMockIntegrationTestMetadataEn d2.dataValueModule().dataValues().blockingUpload() // Then one data set should marked as WARNING - val warnings = d2.dataValueModule().dataValues().byState().eq(State.WARNING).blockingGet() + val warnings = d2.dataValueModule().dataValues().bySyncState().eq(State.WARNING).blockingGet() assertThat(warnings.size).isEqualTo(1) } @@ -56,7 +56,7 @@ class DataValuePostCallMockIntegrationShould : BaseMockIntegrationTestMetadataEn d2.dataValueModule().dataValues().blockingUpload() // Then all data values should be marked as WARNING - val warnings = d2.dataValueModule().dataValues().byState().eq(State.WARNING).blockingGet() + val warnings = d2.dataValueModule().dataValues().bySyncState().eq(State.WARNING).blockingGet() assertThat(warnings.size).isEqualTo(2) } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallRealIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallRealIntegrationShould.java index ed56f105d3..37fbe55ed9 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallRealIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallRealIntegrationShould.java @@ -109,7 +109,7 @@ private DataValue getTestDataValueWith(State state, int value) { .period(d2.periodModule().periods().one().blockingGet().periodId()) .organisationUnit(d2.organisationUnitModule().organisationUnits().one().blockingGet().uid()) .value(String.valueOf(value)) - .state(state) + .syncState(state) .build(); } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/enrollment/CreateEnrollmentUtils.java b/core/src/androidTest/java/org/hisp/dhis/android/core/enrollment/CreateEnrollmentUtils.java index b74664a236..d33baeee84 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/enrollment/CreateEnrollmentUtils.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/enrollment/CreateEnrollmentUtils.java @@ -61,7 +61,7 @@ public static ContentValues create(@NonNull String uid, @NonNull String programU enrollment.put(Columns.FOLLOW_UP, FOLLOW_UP); enrollment.put(Columns.GEOMETRY_TYPE, GEOMETRY_TYPE.getFeatureType()); enrollment.put(Columns.GEOMETRY_COORDINATES, GEOMETRY_COORDINATES); - enrollment.put(Columns.STATE, STATE.name()); + enrollment.put(Columns.SYNC_STATE, STATE.name()); enrollment.put(Columns.CREATED, DATE); enrollment.put(Columns.LAST_UPDATED, DATE); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreIntegrationShould.java index 693e963927..40bb44f7bf 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreIntegrationShould.java @@ -60,7 +60,8 @@ protected Enrollment buildObjectToUpdate() { @Override protected Enrollment buildObjectWithToDeleteState() { return EnrollmentSamples.get().toBuilder() - .state(State.TO_UPDATE) + .syncState(State.TO_UPDATE) + .aggregatedSyncState(State.TO_UPDATE) .deleted(true) .build(); } @@ -68,7 +69,8 @@ protected Enrollment buildObjectWithToDeleteState() { @Override protected Enrollment buildObjectWithSyncedState() { return EnrollmentSamples.get().toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) + .aggregatedSyncState(State.SYNCED) .build(); } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/event/CreateEventUtils.java b/core/src/androidTest/java/org/hisp/dhis/android/core/event/CreateEventUtils.java index d64fc9693e..721fe14299 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/event/CreateEventUtils.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/event/CreateEventUtils.java @@ -71,7 +71,7 @@ public static ContentValues create(@NonNull String uid, event.put(Columns.EVENT_DATE, DATE); event.put(Columns.COMPLETE_DATE, DATE); event.put(Columns.DUE_DATE, DATE); - event.put(Columns.STATE, State.TO_POST.name()); + event.put(Columns.SYNC_STATE, State.TO_POST.name()); return event; } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventEndpointCallMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventEndpointCallMockIntegrationShould.kt index 325e4267cc..c0765343a8 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventEndpointCallMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventEndpointCallMockIntegrationShould.kt @@ -83,7 +83,9 @@ class EventEndpointCallMockIntegrationShould : BaseMockIntegrationTestMetadataEn assertThat(events.size).isEqualTo(1) EventStoreImpl.create(d2.databaseAdapter()).update( event.toBuilder() - .state(state).status(EventStatus.SKIPPED).build() + .syncState(state) + .aggregatedSyncState(state) + .status(EventStatus.SKIPPED).build() ) enqueue("event/events_1.json") diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventPostCallRealIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventPostCallRealIntegrationShould.java index 23e810c7bc..70fddc289f 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventPostCallRealIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventPostCallRealIntegrationShould.java @@ -134,6 +134,19 @@ public void pull_event_with_correct_category_combo_after_be_pushed() throws Exce assertThatEventPushedIsDownloaded(pushedEvent); } + //@Test + public void pull_events_delete_with_repository_and_post() throws Exception { + downloadMetadata(); + + d2.eventModule().eventDownloader().limit(10).blockingDownload(); + + String uid = d2.eventModule().events().one().blockingGet().uid(); + + d2.eventModule().events().uid(uid).blockingDelete(); + + d2.eventModule().events().blockingUpload(); + } + // commented out since it is a flaky test that works against a real server. //@Test public void pull_two_events_with_correct_category_combo_after_be_pushed() throws Exception { @@ -160,7 +173,7 @@ private void createDummyDataToPost(String eventUid) { eventStore.insert(Event.builder().uid(eventUid).created(new Date()).lastUpdated(new Date()) .status(EventStatus.ACTIVE).program(programUid) .programStage(programStageUid).organisationUnit(orgUnitUid).eventDate(new Date()) - .completedDate(new Date()).dueDate(new Date()).state(State.TO_POST) + .completedDate(new Date()).dueDate(new Date()).syncState(State.TO_POST) .attributeOptionCombo(attributeOptionCombo).build()); TrackedEntityDataValue trackedEntityDataValue = TrackedEntityDataValue.builder() diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGeneratorMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGeneratorMockIntegrationShould.java index 82c702d52f..2c8580db34 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGeneratorMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGeneratorMockIntegrationShould.java @@ -110,9 +110,12 @@ public void delete_old_import_conflicts() { d2.eventModule().events().blockingUpload(); assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(3); - eventStore.setState(event1Id, State.TO_POST); - eventStore.setState(event2Id, State.TO_POST); - eventStore.setState(event3Id, State.TO_POST); + eventStore.setSyncState(event1Id, State.TO_POST); + eventStore.setSyncState(event2Id, State.TO_POST); + eventStore.setSyncState(event3Id, State.TO_POST); + eventStore.setAggregatedSyncState(event1Id, State.TO_POST); + eventStore.setAggregatedSyncState(event2Id, State.TO_POST); + eventStore.setAggregatedSyncState(event3Id, State.TO_POST); dhis2MockServer.enqueueMockResponse("imports/web_response_with_event_import_conflicts2.json"); d2.eventModule().events().blockingUpload(); @@ -149,7 +152,7 @@ public void recreate_events_with_filters() { List events = payloadGenerator.getEvents( d2.eventModule().events().byProgramUid().eq(program.uid()) - .byState().in(State.uploadableStates()).blockingGet()); + .bySyncState().in(State.uploadableStates()).blockingGet()); assertThat(events.size()).isEqualTo(3); assertThat(UidsHelper.getUidsList(events).containsAll(Lists.newArrayList(event1, event2, event3))) @@ -189,7 +192,8 @@ private void storeEvents() { .organisationUnit(orgUnit.uid()) .program(program.uid()) .programStage(programStage.uid()) - .state(State.TO_POST) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) .trackedEntityDataValues(Collections.singletonList(dataValue1)) .build(); @@ -200,7 +204,8 @@ private void storeEvents() { .organisationUnit(orgUnit.uid()) .program(program.uid()) .programStage(programStage.uid()) - .state(State.TO_POST) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) .trackedEntityDataValues(Collections.singletonList(dataValue2)) .build(); @@ -211,7 +216,8 @@ private void storeEvents() { .organisationUnit(orgUnit.uid()) .program(program.uid()) .programStage(programStage.uid()) - .state(State.TO_POST) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) .trackedEntityDataValues(Collections.singletonList(dataValue3)) .build(); @@ -222,7 +228,8 @@ private void storeEvents() { .organisationUnit(orgUnit.uid()) .program(program.uid()) .programStage(programStage.uid()) - .state(State.ERROR) + .syncState(State.ERROR) + .aggregatedSyncState(State.ERROR) .trackedEntityDataValues(Collections.singletonList(dataValue4)) .build(); @@ -250,7 +257,8 @@ private void storeSingleEvent(String eventUid, Program program, State state, Boo .organisationUnit(orgUnit.uid()) .program(program.uid()) .programStage(programStage.uid()) - .state(state) + .syncState(state) + .aggregatedSyncState(state) .deleted(deleted) .build()); } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventStoreIntegrationShould.java index b834ca1a4c..1ee55ca73f 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventStoreIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/event/internal/EventStoreIntegrationShould.java @@ -61,7 +61,8 @@ protected Event buildObjectToUpdate() { @Override protected Event buildObjectWithToDeleteState() { return EventSamples.get().toBuilder() - .state(State.TO_UPDATE) + .syncState(State.TO_UPDATE) + .aggregatedSyncState(State.TO_UPDATE) .deleted(true) .build(); } @@ -69,7 +70,8 @@ protected Event buildObjectWithToDeleteState() { @Override protected Event buildObjectWithSyncedState() { return EventSamples.get().toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) + .aggregatedSyncState(State.SYNCED) .build(); } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceCallRealIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceCallRealIntegrationShould.java index d45c8ff02c..0b23cb5b25 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceCallRealIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceCallRealIntegrationShould.java @@ -110,7 +110,7 @@ public void write_tracked_entity_attribute_related_files_and_upload() throws Exc TrackedEntityInstance trackedEntityInstance2 = d2.trackedEntityModule().trackedEntityInstances().blockingGet().get(0); - assertThat(trackedEntityInstance2.state()).isEqualTo(State.SYNCED); + assertThat(trackedEntityInstance2.syncState()).isEqualTo(State.SYNCED); } //@Test diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStoreIntegrationShould.java index c5ac7e419d..ed91f02d30 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStoreIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStoreIntegrationShould.java @@ -41,7 +41,7 @@ public class TrackerImportConflictStoreIntegrationShould extends ObjectStoreAbstractIntegrationShould { public TrackerImportConflictStoreIntegrationShould() { - super(TrackerImportConflictStore.create(TestDatabaseAdapterFactory.get()), + super(TrackerImportConflictStoreImpl.create(TestDatabaseAdapterFactory.get()), TrackerImportConflictTableInfo.TABLE_INFO, TestDatabaseAdapterFactory.get()); } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/note/internal/NoteStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/note/internal/NoteStoreIntegrationShould.java index 054a300d7f..59606ef528 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/note/internal/NoteStoreIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/note/internal/NoteStoreIntegrationShould.java @@ -53,7 +53,7 @@ protected Note buildObject() { @Override protected Note buildObjectToUpdate() { return NoteSamples.getNote().toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) .build(); } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCallMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCallMockIntegrationShould.java index b0b539cb06..2d7ecee670 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCallMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCallMockIntegrationShould.java @@ -30,6 +30,8 @@ import android.content.ContentValues; +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner; +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleanerImpl; import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; import org.hisp.dhis.android.core.arch.db.tableinfos.TableInfo; import org.hisp.dhis.android.core.category.CategoryComboTableInfo; @@ -37,6 +39,7 @@ import org.hisp.dhis.android.core.data.organisationunit.OrganisationUnitSamples; import org.hisp.dhis.android.core.dataset.DataSetTableInfo; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitTableInfo; import org.hisp.dhis.android.core.program.ProgramTableInfo; import org.hisp.dhis.android.core.user.User; import org.hisp.dhis.android.core.user.UserInternalAccessor; @@ -104,10 +107,13 @@ public void setUp() throws IOException { OrganisationUnitHandler organisationUnitHandler = OrganisationUnitHandlerImpl.create(databaseAdapter); + CollectionCleaner organisationUnitCollectionCleaner = + new CollectionCleanerImpl<>(OrganisationUnitTableInfo.TABLE_INFO.name(), databaseAdapter); + OrganisationUnitDisplayPathTransformer pathTransformer = new OrganisationUnitDisplayPathTransformer(); organisationUnitCall = new OrganisationUnitCall(organisationUnitService, - organisationUnitHandler, pathTransformer) + organisationUnitHandler, pathTransformer, organisationUnitCollectionCleaner) .download(user); } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/relationship/RelationshipCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/relationship/RelationshipCollectionRepositoryMockIntegrationShould.java index a7548b18f3..66a810a937 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/relationship/RelationshipCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/relationship/RelationshipCollectionRepositoryMockIntegrationShould.java @@ -29,6 +29,7 @@ package org.hisp.dhis.android.core.relationship; import org.hisp.dhis.android.core.common.BaseNameableObject; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; import org.hisp.dhis.android.core.utils.runner.D2JunitRunner; import org.junit.Test; @@ -101,6 +102,16 @@ public void filter_by_relationship_type() { assertThat(relationships.size()).isEqualTo(1); } + @Test + public void filter_by_sync_state() { + List relationships = + d2.relationshipModule().relationships() + .bySyncState().eq(State.SYNCED) + .blockingGet(); + + assertThat(relationships.size()).isEqualTo(3); + } + @Test public void get_by_item() { RelationshipItem item = RelationshipItem.builder().trackedEntityInstance( diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepositoryMockIntegrationShould.java new file mode 100644 index 0000000000..9f9d5b7cbc --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepositoryMockIntegrationShould.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2004-2021, 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.relationship; + +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.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +@RunWith(D2JunitRunner.class) +public class RelationshipTypeCollectionRepositoryMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { + + @Test + public void find_all() { + List relationshipTypes = + d2.relationshipModule().relationshipTypes() + .blockingGet(); + + assertThat(relationshipTypes.size()).isEqualTo(4); + } + + @Test + public void filter_by_bidirectional() { + List relationshipTypes = + d2.relationshipModule().relationshipTypes() + .byBidirectional().eq(true) + .blockingGet(); + + assertThat(relationshipTypes.size()).isEqualTo(1); + } + + @Test + public void find_with_constraints() { + List relationshipTypes = + d2.relationshipModule().relationshipTypes() + .byUid().eq("WiH6923nMtb") + .withConstraints() + .blockingGet(); + + assertThat(relationshipTypes.size()).isEqualTo(1); + for (RelationshipType type : relationshipTypes) { + assertThat(type.fromConstraint()).isNotNull(); + assertThat(type.toConstraint()).isNotNull(); + } + } + + @Test + public void by_tracked_entity_instance() { + List relationshipTypes = + d2.relationshipModule().relationshipTypes() + .byAvailableForTrackedEntityInstance("nWrB0TfWlvh") + .blockingGet(); + + assertThat(relationshipTypes.size()).isEqualTo(2); + } + + @Test + public void by_enrollment() { + List relationshipTypes = + d2.relationshipModule().relationshipTypes() + .byAvailableForEnrollment("enroll1") + .blockingGet(); + + assertThat(relationshipTypes.size()).isEqualTo(1); + } + + @Test + public void by_event() { + List relationshipTypes = + d2.relationshipModule().relationshipTypes() + .byAvailableForEvent("single1") + .blockingGet(); + + assertThat(relationshipTypes.size()).isEqualTo(1); + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationStoreIntegrationShould.kt new file mode 100644 index 0000000000..ccac7f9772 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationStoreIntegrationShould.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2021, 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.settings.internal + +import org.hisp.dhis.android.core.data.database.ObjectStoreAbstractIntegrationShould +import org.hisp.dhis.android.core.data.settings.AnalyticsSettingsSamples +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationTableInfo +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 AnalyticsDhisVisualizationStoreIntegrationShould : + ObjectStoreAbstractIntegrationShould( + AnalyticsDhisVisualizationStore.create(TestDatabaseAdapterFactory.get()), + AnalyticsDhisVisualizationTableInfo.TABLE_INFO, + TestDatabaseAdapterFactory.get() + ) { + override fun buildObject(): AnalyticsDhisVisualization { + return AnalyticsSettingsSamples.analyticsDhisVisualization + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/CreateTrackedEntityInstanceUtils.java b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/CreateTrackedEntityInstanceUtils.java index 0e8e499a51..5b81f8223a 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/CreateTrackedEntityInstanceUtils.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/CreateTrackedEntityInstanceUtils.java @@ -50,7 +50,7 @@ public static ContentValues create(@NonNull String uid, trackedEntityInstance.put(Columns.LAST_UPDATED, DATE); trackedEntityInstance.put(Columns.ORGANISATION_UNIT, organisationUnit); trackedEntityInstance.put(Columns.TRACKED_ENTITY_TYPE, trackedEntityType); - trackedEntityInstance.put(Columns.STATE, STATE.name()); + trackedEntityInstance.put(Columns.SYNC_STATE, STATE.name()); return trackedEntityInstance; } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/BasePayloadGeneratorMockIntegration.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/BasePayloadGeneratorMockIntegration.kt new file mode 100644 index 0000000000..cd4ac2fb2a --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/BasePayloadGeneratorMockIntegration.kt @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import org.hisp.dhis.android.core.arch.call.executors.internal.D2CallExecutor +import org.hisp.dhis.android.core.common.ObjectWithUid +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.data.relationship.RelationshipSamples +import org.hisp.dhis.android.core.data.trackedentity.TrackedEntityDataValueSamples +import org.hisp.dhis.android.core.data.trackedentity.TrackedEntityInstanceSamples +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.EnrollmentInternalAccessor +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore +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.EventStore +import org.hisp.dhis.android.core.event.internal.EventStoreImpl +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.maintenance.internal.ForeignKeyCleanerImpl +import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitStore.create +import org.hisp.dhis.android.core.program.internal.ProgramStageStore +import org.hisp.dhis.android.core.relationship.RelationshipConstraintType +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.relationship.RelationshipItem +import org.hisp.dhis.android.core.relationship.internal.RelationshipItemStoreImpl +import org.hisp.dhis.android.core.relationship.internal.RelationshipStoreImpl +import org.hisp.dhis.android.core.relationship.internal.RelationshipTypeStore +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestMetadataEnqueable +import org.junit.After +import org.junit.BeforeClass + +open class BasePayloadGeneratorMockIntegration : BaseMockIntegrationTestMetadataEnqueable() { + + protected val teiId = "teiId" + protected val enrollment1Id = "enrollment1Id" + protected val enrollment2Id = "enrollment2Id" + protected val enrollment3Id = "enrollment3Id" + protected val event1Id = "event1Id" + protected val event2Id = "event2Id" + protected val event3Id = "event3Id" + protected val singleEventId = "singleEventId" + + @After + @Throws(D2Error::class) + fun tearDown() { + d2.wipeModule().wipeData() + } + + protected fun storeTrackerData() { + val orgUnit = create(databaseAdapter).selectFirst() + val teiType = TrackedEntityTypeStore.create(databaseAdapter).selectFirst() + val program = d2.programModule().programs().one().blockingGet() + val programStage = ProgramStageStore.create(databaseAdapter).selectFirst() + + val dataValue1 = TrackedEntityDataValueSamples.get().toBuilder().event(event1Id).build() + + val event1 = Event.builder() + .uid(event1Id) + .enrollment(enrollment1Id) + .organisationUnit(orgUnit!!.uid()) + .program(program.uid()) + .programStage(programStage!!.uid()) + .syncState(State.TO_UPDATE) + .aggregatedSyncState(State.TO_UPDATE) + .trackedEntityDataValues(listOf(dataValue1)) + .build() + + val enrollment1 = EnrollmentInternalAccessor.insertEvents(Enrollment.builder(), listOf(event1)) + .uid(enrollment1Id) + .program(program.uid()) + .organisationUnit(orgUnit.uid()) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) + .trackedEntityInstance(teiId) + .build() + val dataValue2 = TrackedEntityDataValueSamples.get().toBuilder().event(event2Id).build() + + val event2 = Event.builder() + .uid(event2Id) + .enrollment(enrollment2Id) + .organisationUnit(orgUnit.uid()) + .program(program.uid()) + .programStage(programStage.uid()) + .syncState(State.SYNCED_VIA_SMS) + .aggregatedSyncState(State.SYNCED_VIA_SMS) + .trackedEntityDataValues(listOf(dataValue2)) + .build() + + val enrollment2 = EnrollmentInternalAccessor.insertEvents(Enrollment.builder(), listOf(event2)) + .uid(enrollment2Id) + .program(program.uid()) + .organisationUnit(orgUnit.uid()) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) + .trackedEntityInstance(teiId) + .build() + + val dataValue3 = TrackedEntityDataValueSamples.get().toBuilder().event(event3Id).build() + + val event3 = Event.builder() + .uid(event3Id) + .enrollment(enrollment3Id) + .organisationUnit(orgUnit.uid()) + .program(program.uid()) + .programStage(programStage.uid()) + .syncState(State.ERROR) + .aggregatedSyncState(State.ERROR) + .trackedEntityDataValues(listOf(dataValue3)) + .build() + + val enrollment3 = EnrollmentInternalAccessor.insertEvents(Enrollment.builder(), listOf(event3)) + .uid(enrollment3Id) + .program(program.uid()) + .organisationUnit(orgUnit.uid()) + .syncState(State.TO_POST) + .aggregatedSyncState(State.SYNCED) + .trackedEntityInstance(teiId) + .build() + + val tei = TrackedEntityInstanceInternalAccessor.insertEnrollments( + TrackedEntityInstance.builder(), listOf(enrollment1, enrollment2, enrollment3) + ) + .uid(teiId) + .trackedEntityType(teiType!!.uid()) + .organisationUnit(orgUnit.uid()) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) + .build() + + val singleEventDataValue = TrackedEntityDataValueSamples.get().toBuilder().event(singleEventId).build() + + val singleEvent = Event.builder() + .uid(singleEventId) + .organisationUnit(orgUnit.uid()) + .program(program.uid()) + .programStage(programStage.uid()) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) + .trackedEntityDataValues(listOf(singleEventDataValue)) + .build() + + teiStore.insert(tei) + enrollmentStore.insert(enrollment1) + enrollmentStore.insert(enrollment2) + enrollmentStore.insert(enrollment3) + eventStore.insert(event1) + eventStore.insert(event2) + eventStore.insert(event3) + eventStore.insert(singleEvent) + teiDataValueStore.insert(dataValue1) + teiDataValueStore.insert(dataValue2) + teiDataValueStore.insert(dataValue3) + teiDataValueStore.insert(singleEventDataValue) + } + + protected fun storeSimpleTrackedEntityInstance(teiUid: String, state: State) { + val orgUnit = create(databaseAdapter).selectFirst() + val teiType = TrackedEntityTypeStore.create(databaseAdapter).selectFirst() + TrackedEntityInstanceStoreImpl.create(databaseAdapter).insert( + TrackedEntityInstanceSamples.get().toBuilder() + .uid(teiUid) + .trackedEntityType(teiType!!.uid()) + .organisationUnit(orgUnit!!.uid()) + .syncState(state) + .aggregatedSyncState(state) + .build() + ) + } + + @Throws(D2Error::class) + protected fun storeRelationship( + relationshipUid: String, + from: String, + to: String + ) { + storeRelationship(relationshipUid, RelationshipHelper.teiItem(from), RelationshipHelper.teiItem(to)) + } + + @Throws(D2Error::class) + protected fun storeRelationship( + relationshipUid: String, + from: RelationshipItem, + to: RelationshipItem + ) { + val relationshipType = RelationshipTypeStore.create(databaseAdapter).selectFirst() + val executor = D2CallExecutor.create(databaseAdapter) + executor.executeD2CallTransactionally { + RelationshipStoreImpl.create(databaseAdapter).insert( + RelationshipSamples.get230(relationshipUid, from, to).toBuilder() + .relationshipType(relationshipType!!.uid()) + .syncState(State.TO_POST) + .build() + ) + RelationshipItemStoreImpl.create(databaseAdapter).insert( + from.toBuilder() + .relationship(ObjectWithUid.create(relationshipUid)) + .relationshipItemType(RelationshipConstraintType.FROM) + .build() + ) + RelationshipItemStoreImpl.create(databaseAdapter).insert( + to.toBuilder() + .relationship(ObjectWithUid.create(relationshipUid)) + .relationshipItemType(RelationshipConstraintType.TO) + .build() + ) + ForeignKeyCleanerImpl.create(databaseAdapter).cleanForeignKeyErrors() + null + } + } + + protected fun getEnrollments(trackedEntityInstance: TrackedEntityInstance): List { + return TrackedEntityInstanceInternalAccessor.accessEnrollments(trackedEntityInstance) + } + + protected fun getEvents(enrollment: Enrollment): List { + return EnrollmentInternalAccessor.accessEvents(enrollment) + } + + protected companion object { + internal lateinit var payloadGenerator29: TrackedEntityInstancePostPayloadGenerator29 + internal lateinit var oldTrackerPayloadGenerator: OldTrackerImporterPayloadGenerator + internal lateinit var teiStore: TrackedEntityInstanceStore + internal lateinit var teiDataValueStore: TrackedEntityDataValueStore + internal lateinit var eventStore: EventStore + internal lateinit var enrollmentStore: EnrollmentStore + + @BeforeClass + @JvmStatic + @Throws(Exception::class) + fun setUp() { + setUpClass() + payloadGenerator29 = objects.d2DIComponent.trackedEntityInstancePostPayloadGenerator() + oldTrackerPayloadGenerator = objects.d2DIComponent.oldTrackerImporterPayloadGenerator() + teiStore = TrackedEntityInstanceStoreImpl.create(databaseAdapter) + teiDataValueStore = TrackedEntityDataValueStoreImpl.create(databaseAdapter) + eventStore = EventStoreImpl.create(databaseAdapter) + enrollmentStore = EnrollmentStoreImpl.create(databaseAdapter) + } + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt new file mode 100644 index 0000000000..4e2a103daf --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGeneratorMockIntegrationShould.kt @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Assert.fail +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class OldTrackerImporterPayloadGeneratorMockIntegrationShould : BasePayloadGeneratorMockIntegration() { + + @Test + fun build_tracked_entity_instance_payload_with_nested_elements() { + storeTrackerData() + val trackedEntityInstance = teiStore.selectByUid(teiId)!! + + val payload = oldTrackerPayloadGenerator.getTrackedEntityInstancePayload(listOf(trackedEntityInstance)) + + assertThat(payload.trackedEntityInstances.size).isEqualTo(1) + assertThat(payload.events.size).isEqualTo(0) + assertThat(payload.relationships.size).isEqualTo(0) + + val payloadInstance = payload.trackedEntityInstances.first() + + assertThat(getEnrollments(payloadInstance).size).isEqualTo(2) + for (enrollment in getEnrollments(payloadInstance)) { + assertThat(getEvents(enrollment).size).isEqualTo(1) + for (event in getEvents(enrollment)) { + assertThat(event.trackedEntityDataValues()!!.size).isEqualTo(1) + } + } + } + + @Test + fun build_single_event_with_nested_elements() { + storeTrackerData() + val event = eventStore.selectByUid(singleEventId)!! + + val payload = oldTrackerPayloadGenerator.getEventPayload(listOf(event)) + + assertThat(payload.trackedEntityInstances.size).isEqualTo(0) + assertThat(payload.events.size).isEqualTo(1) + assertThat(payload.relationships.size).isEqualTo(0) + + val payloadEvent = payload.events.first() + + assertThat(payloadEvent.trackedEntityDataValues()!!.size).isEqualTo(1) + } + + @Test + fun build_payload_from_tei_with_related_single_events() { + storeTrackerData() + storeRelationship( + relationshipUid = "relationship1", + from = RelationshipHelper.teiItem(teiId), + to = RelationshipHelper.eventItem(singleEventId) + ) + val instance = teiStore.selectByUid(teiId)!! + + val payload = oldTrackerPayloadGenerator.getTrackedEntityInstancePayload(listOf(instance)) + + assertThat(payload.trackedEntityInstances.size).isEqualTo(1) + assertThat(payload.events.size).isEqualTo(1) + assertThat(payload.relationships.size).isEqualTo(1) + + assertThat(payload.events.first().uid()).isEqualTo(singleEventId) + } + + @Test + fun build_payload_from_tei_with_related_tracker_event() { + val fromTeiUid = "fromTei" + storeTrackerData() + storeSimpleTrackedEntityInstance(fromTeiUid, State.TO_UPDATE) + + storeRelationship( + relationshipUid = "relationship1", + from = RelationshipHelper.teiItem(fromTeiUid), + to = RelationshipHelper.eventItem(event1Id) + ) + val instance = teiStore.selectByUid(fromTeiUid)!! + + val payload = oldTrackerPayloadGenerator.getTrackedEntityInstancePayload(listOf(instance)) + + checkTeisInPayload(payload, fromTeiUid, teiId) + } + + @Test + fun build_payload_from_tei_with_related_enrollments() { + val fromTeiUid = "fromTei" + storeTrackerData() + storeSimpleTrackedEntityInstance(fromTeiUid, State.TO_UPDATE) + + storeRelationship( + relationshipUid = "relationship1", + from = RelationshipHelper.teiItem(fromTeiUid), + to = RelationshipHelper.enrollmentItem(enrollment1Id) + ) + val instance = teiStore.selectByUid(fromTeiUid)!! + + val payload = oldTrackerPayloadGenerator.getTrackedEntityInstancePayload(listOf(instance)) + + checkTeisInPayload(payload, fromTeiUid, teiId) + } + + @Test + fun build_payload_from_tei_with_related_trackedEntityInstances() { + val fromTeiUid = "fromTei" + storeTrackerData() + storeSimpleTrackedEntityInstance(fromTeiUid, State.TO_UPDATE) + + storeRelationship( + relationshipUid = "relationship1", + from = RelationshipHelper.teiItem(fromTeiUid), + to = RelationshipHelper.teiItem(teiId) + ) + val instance = teiStore.selectByUid(fromTeiUid)!! + + val payload = oldTrackerPayloadGenerator.getTrackedEntityInstancePayload(listOf(instance)) + + checkTeisInPayload(payload, fromTeiUid, teiId) + } + + private fun checkTeisInPayload(payload: OldTrackerImporterPayload, fromTei: String, toTei: String) { + assertThat(payload.trackedEntityInstances.size).isEqualTo(2) + assertThat(payload.events.size).isEqualTo(0) + assertThat(payload.relationships.size).isEqualTo(1) + + payload.trackedEntityInstances.forEach { + when (it.uid()) { + fromTei -> assertThat(TrackedEntityInstanceInternalAccessor.accessEnrollments(it)).isEmpty() + toTei -> assertThat(TrackedEntityInstanceInternalAccessor.accessEnrollments(it)).isNotEmpty() + else -> fail("Unexpected trackedEntityInstance uid: " + it.uid()) + } + } + } + + @Test + fun build_payload_from_single_event_with_related_tei() { + storeTrackerData() + + storeRelationship( + relationshipUid = "relationship1", + from = RelationshipHelper.eventItem(singleEventId), + to = RelationshipHelper.teiItem(teiId) + ) + val event = eventStore.selectByUid(singleEventId)!! + + val payload = oldTrackerPayloadGenerator.getEventPayload(listOf(event)) + + assertThat(payload.trackedEntityInstances.size).isEqualTo(1) + assertThat(payload.events.size).isEqualTo(1) + assertThat(payload.relationships.size).isEqualTo(1) + + assertThat(payload.trackedEntityInstances.first().uid()).isEqualTo(teiId) + assertThat(payload.events.first().uid()).isEqualTo(singleEventId) + } + + @Test + fun build_recursive_relationship() { + storeTrackerData() + + storeRelationship( + relationshipUid = "relationship1", + from = RelationshipHelper.enrollmentItem(enrollment1Id), + to = RelationshipHelper.enrollmentItem(enrollment1Id) + ) + val instance = teiStore.selectByUid(teiId)!! + + val payload = oldTrackerPayloadGenerator.getTrackedEntityInstancePayload(listOf(instance)) + + assertThat(payload.trackedEntityInstances.size).isEqualTo(1) + assertThat(payload.events.size).isEqualTo(0) + assertThat(payload.relationships.size).isEqualTo(1) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java index bd5d6115e4..89aaa6d03c 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreIntegrationShould.java @@ -125,8 +125,8 @@ public void select_data_values_by_event_uid() { @Test public void select_single_events_data_values() { EventStore eventStore = EventStoreImpl.create(TestDatabaseAdapterFactory.get()); - eventStore.insert(EventSamples.get().toBuilder().uid("event_1").enrollment(null).state(State.TO_POST).build()); - eventStore.insert(EventSamples.get().toBuilder().uid("event_2").state(State.TO_POST).build()); + eventStore.insert(EventSamples.get().toBuilder().uid("event_1").enrollment(null).syncState(State.TO_POST).build()); + eventStore.insert(EventSamples.get().toBuilder().uid("event_2").syncState(State.TO_POST).build()); assertThat(eventStore.count()).isEqualTo(2); store.insert(TrackedEntityDataValueSamples.get() @@ -150,17 +150,18 @@ public void select_tracker_data_values() { TrackedEntityInstanceStore trackedEntityInstanceStore = TrackedEntityInstanceStoreImpl .create(TestDatabaseAdapterFactory.get()); TrackedEntityInstance trackedEntityInstance = TrackedEntityInstance.builder().uid("tei_uid") - .organisationUnit("organisation_unit_uid").trackedEntityType("tei_type").state(State.TO_POST).build(); + .organisationUnit("organisation_unit_uid").trackedEntityType("tei_type").syncState(State.TO_POST).build(); trackedEntityInstanceStore.insert(trackedEntityInstance); EnrollmentStore enrollmentStore = EnrollmentStoreImpl.create(TestDatabaseAdapterFactory.get()); Enrollment enrollment = Enrollment.builder().uid("enrollment").organisationUnit("organisation_unit") - .program("program").trackedEntityInstance("tei_uid").state(State.TO_POST).build(); + .program("program").trackedEntityInstance("tei_uid") + .aggregatedSyncState(State.TO_POST).syncState(State.TO_POST).build(); enrollmentStore.insert(enrollment); EventStore eventStore = EventStoreImpl.create(TestDatabaseAdapterFactory.get()); - eventStore.insert(EventSamples.get().toBuilder().uid("event_1").state(State.TO_POST).build()); - eventStore.insert(EventSamples.get().toBuilder().uid("event_2").enrollment(null).state(State.TO_POST).build()); + eventStore.insert(EventSamples.get().toBuilder().uid("event_1").syncState(State.TO_POST).build()); + eventStore.insert(EventSamples.get().toBuilder().uid("event_2").enrollment(null).syncState(State.TO_POST).build()); assertThat(eventStore.count()).isEqualTo(2); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceCallMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceCallMockIntegrationShould.java index 930755a420..df1bb7cfbf 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceCallMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceCallMockIntegrationShould.java @@ -210,7 +210,7 @@ private TrackedEntityInstance getDownloadedTei(String teiUid) { List downloadedEnrollmentsWithoutIdAndDeleteFalse = new ArrayList<>(); for (Enrollment enrollment : downloadedEnrollments) { downloadedEnrollmentsWithoutIdAndDeleteFalse.add( - enrollment.toBuilder().id(null).deleted(false).state(null).notes(new ArrayList<>()).build()); + enrollment.toBuilder().id(null).deleted(false).notes(new ArrayList<>()).build()); } EventStore eventStore = EventStoreImpl.create(databaseAdapter); @@ -219,7 +219,7 @@ private TrackedEntityInstance getDownloadedTei(String teiUid) { List downloadedEventsWithoutValuesAndDeleteFalse = new ArrayList<>(); for (Event event : downloadedEventsWithoutValues) { downloadedEventsWithoutValuesAndDeleteFalse.add( - event.toBuilder().id(null).deleted(false).state(null).build()); + event.toBuilder().id(null).deleted(false).build()); } List dataValueList = TrackedEntityDataValueStoreImpl.create(databaseAdapter).selectAll(); @@ -282,7 +282,6 @@ private TrackedEntityInstance createTei(TrackedEntityInstance downloadedTei, downloadedTei.toBuilder(), relationships), downloadedEnrollments) .id(null) - .state(null) .deleted(false) .trackedEntityAttributeValues(attValuesWithoutIdAndTEI) .build(); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostCallRealIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostCallRealIntegrationShould.java index 66a29c5d22..ff8c68cff1 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostCallRealIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostCallRealIntegrationShould.java @@ -184,7 +184,7 @@ public void add_and_post_tei_using_repositories() throws Exception { .build()); // Enrollment module -> enroll the tracked entity instance to the program - d2.enrollmentModule().enrollments().add( + d2.enrollmentModule().enrollments().blockingAdd( EnrollmentCreateProjection.builder() .organisationUnit(organisationUnit.uid()) .program(program.uid()) @@ -382,7 +382,7 @@ public void post_new_relationship_to_client_created_tei() throws Exception { String teiBUid = uidGenerator.generate(); insertATei(teiBUid, teiA, geometry); - trackedEntityInstanceStore.setState(teiA.uid(), State.TO_POST); + trackedEntityInstanceStore.setSyncState(teiA.uid(), State.TO_POST); Relationship newRelationship = RelationshipHelper.teiToTeiRelationship(teiA.uid(), teiBUid, relationshipType.uid()); @@ -474,15 +474,9 @@ public void post_a_tei_and_delete_one_event() throws Exception { downloadMetadata(); d2.trackedEntityModule().trackedEntityInstanceDownloader().byUid().eq("LxMVYhJm3Jp").blockingDownload(); - TrackedEntityInstance tei = trackedEntityInstanceStore.selectFirst(); - - Enrollment enrollment = enrollmentStore.selectFirst(); - Event event = eventStore.selectFirst(); String eventUid = event.uid(); - trackedEntityInstanceStore.setState(tei.uid(), State.TO_UPDATE); - enrollmentStore.setState(enrollment.uid(), State.TO_UPDATE); d2.eventModule().events().uid(eventUid).blockingDelete(); d2.trackedEntityModule().trackedEntityInstances().blockingUpload(); @@ -505,7 +499,8 @@ private void insertATei(String uid, TrackedEntityInstance tei, Geometry geometry TrackedEntityInstance trackedEntityInstance = tei.toBuilder() .uid(uid) .geometry(geometry) - .state(State.TO_POST) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) .build(); trackedEntityInstanceStore.insert(trackedEntityInstance); @@ -525,7 +520,8 @@ private void createDummyDataToPost(String orgUnitUid, String programUid, String .organisationUnit(orgUnitUid) .trackedEntityType(trackedEntityUid) .geometry(geometry) - .state(State.TO_POST) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) .build(); trackedEntityInstanceStore.insert(trackedEntityInstance); @@ -535,7 +531,7 @@ private void createDummyDataToPost(String orgUnitUid, String programUid, String .program(programUid).incidentDate(refDate).completedDate(refDate).enrollmentDate(refDate) .followUp(Boolean.FALSE).status(EnrollmentStatus.ACTIVE).trackedEntityInstance(trackedEntityInstanceUid) .geometry(Geometry.builder().type(FeatureType.POINT).coordinates("[10.33, 12.231]").build()) - .state(State.TO_POST).build(); + .syncState(State.TO_POST).aggregatedSyncState(State.TO_POST).build(); enrollmentStore.insert(enrollment); @@ -544,7 +540,7 @@ private void createDummyDataToPost(String orgUnitUid, String programUid, String .status(EventStatus.ACTIVE).program(programUid) .geometry(Geometry.builder().type(FeatureType.POINT).coordinates("[12.21, 13.21]").build()) .programStage(programStageUid).organisationUnit(orgUnitUid).eventDate(refDate).dueDate(refDate) - .completedDate(refDate).state(State.TO_POST).attributeOptionCombo(categoryComboOptionUid) + .completedDate(refDate).syncState(State.TO_POST).attributeOptionCombo(categoryComboOptionUid) .build(); eventStore.insert(event); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator29MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator29MockIntegrationShould.kt new file mode 100644 index 0000000000..52f3f5e1d0 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator29MockIntegrationShould.kt @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import com.google.common.collect.Lists +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.arch.helpers.UidsHelper.getUidsList +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.note.Note +import org.hisp.dhis.android.core.note.NoteCreateProjection +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 + +@RunWith(D2JunitRunner::class) +class TrackedEntityInstancePostPayloadGenerator29MockIntegrationShould : BasePayloadGeneratorMockIntegration() { + + @Test + fun build_payload_with_different_enrollments() { + storeTrackerData() + + val partitions = partitions + + assertThat(partitions.size).isEqualTo(1) + assertThat(partitions.first().size).isEqualTo(1) + + for (instance in partitions.first()) { + assertThat(getEnrollments(instance).size).isEqualTo(2) + for (enrollment in getEnrollments(instance)) { + assertThat(getEvents(enrollment).size).isEqualTo(1) + for (event in getEvents(enrollment)) { + assertThat(event.trackedEntityDataValues()!!.size).isEqualTo(1) + } + } + } + } + + private val partitions: List> + get() = payloadGenerator29.getTrackedEntityInstancesPartitions29( + teiStore.queryTrackedEntityInstancesToSync() + ) + + @Test + fun build_payload_without_events_marked_as_error() { + storeTrackerData() + + enrollmentStore.setAggregatedSyncState(enrollment3Id, State.TO_POST) + + val partitions = partitions + + assertThat(partitions.size).isEqualTo(1) + assertThat(partitions.first().size).isEqualTo(1) + + for (instance in partitions.first()) { + assertThat(getEnrollments(instance).size).isEqualTo(3) + for (enrollment in getEnrollments(instance)) { + if (enrollment.uid() == enrollment3Id) { + assertThat(getEvents(enrollment).size).isEqualTo(0) + } else { + assertThat(getEvents(enrollment).size).isEqualTo(1) + } + } + } + } + + @Test + fun handle_import_conflicts_correctly() { + storeTrackerData() + + dhis2MockServer.enqueueMockResponse("imports/web_response_with_import_conflicts_2.json") + + d2.trackedEntityModule().trackedEntityInstances().blockingUpload() + + assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(3) + } + + @Test + fun delete_old_import_conflicts() { + storeTrackerData() + + dhis2MockServer.enqueueMockResponse("imports/web_response_with_import_conflicts_2.json") + + d2.trackedEntityModule().trackedEntityInstances().blockingUpload() + + assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(3) + + teiStore.setAggregatedSyncState("teiId", State.TO_POST) + enrollmentStore.setAggregatedSyncState("enrollment1Id", State.TO_POST) + enrollmentStore.setAggregatedSyncState("enrollment2Id", State.TO_POST) + eventStore.setSyncStateOrDelete("event1Id", State.TO_POST) + eventStore.setSyncStateOrDelete("event2Id", State.TO_POST) + + dhis2MockServer.enqueueMockResponse("imports/web_response_with_import_conflicts_3.json") + + d2.trackedEntityModule().trackedEntityInstances().blockingUpload() + assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(1) + } + + @Test + @Throws(D2Error::class) + fun handle_tei_deletions() { + storeTrackerData() + + d2.trackedEntityModule().trackedEntityInstances().uid("teiId").blockingDelete() + + // There is no TEIs to upload, so there is no request to enqueue. + d2.trackedEntityModule().trackedEntityInstances().blockingUpload() + assertThat(d2.trackedEntityModule().trackedEntityInstances().blockingCount()).isEqualTo(0) + assertThat(d2.enrollmentModule().enrollments().blockingCount()).isEqualTo(0) + assertThat(d2.eventModule().events().byEnrollmentUid().isNotNull.blockingCount()).isEqualTo(0) + assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(0) + } + + @Test + @Throws(Exception::class) + fun recreate_teis_with_filters_and_relationships() { + val tei1 = "tei1" + val tei2 = "tei2" + val tei3 = "tei3" + val tei4 = "tei4" + val tei5 = "tei5" + + storeSimpleTrackedEntityInstance(tei1, State.TO_POST) + storeSimpleTrackedEntityInstance(tei2, State.TO_POST) + storeSimpleTrackedEntityInstance(tei3, State.TO_POST) + storeSimpleTrackedEntityInstance(tei4, State.TO_POST) + storeSimpleTrackedEntityInstance(tei5, State.SYNCED) + + storeRelationship("relationship1", tei1, tei2) + storeRelationship("relationship2", tei2, tei3) + storeRelationship("relationship3", tei1, tei5) + storeRelationship("relationship4", tei5, tei4) + + val partitions = payloadGenerator29.getTrackedEntityInstancesPartitions29( + d2.trackedEntityModule().trackedEntityInstances().byUid().eq(tei1) + .byAggregatedSyncState().`in`(*State.uploadableStates()).blockingGet() + ) + + assertThat(partitions.size).isEqualTo(1) + assertThat(partitions[0].size).isEqualTo(3) + assertThat(getUidsList(partitions[0]).containsAll(Lists.newArrayList(tei1, tei2, tei3))).isTrue() + } + + @Test + fun restore_payload_states_when_error_500() { + storeTrackerData() + + dhis2MockServer.enqueueMockResponse(500, "Internal Server Error") + d2.trackedEntityModule().trackedEntityInstances().blockingUpload() + + val instance = teiStore.selectFirst() + assertThat(instance!!.syncState()).isEqualTo(State.TO_POST) + + val enrollments = enrollmentStore.selectAll() + for (enrollment in enrollments) { + if (enrollment1Id == enrollment.uid() || enrollment2Id == enrollment.uid()) { + assertThat(enrollment.syncState()).isEqualTo(State.TO_POST) + } + } + + val events = eventStore.selectAll() + for (event in events) { + if (event1Id == event.uid()) { + assertThat(event.syncState()).isEqualTo(State.TO_UPDATE) + } + if (event2Id == event.uid()) { + assertThat(event.syncState()).isEqualTo(State.SYNCED_VIA_SMS) + } + } + } + + @Test + @Throws(D2Error::class) + fun build_payload_with_enrollment_notes() { + storeTrackerData() + d2.noteModule().notes().blockingAdd( + NoteCreateProjection.builder() + .enrollment(enrollment1Id) + .noteType(Note.NoteType.ENROLLMENT_NOTE) + .value("This is an enrollment note") + .build() + ) + val partitions = partitions + assertThat(partitions.size).isEqualTo(1) + assertThat(partitions.first().size).isEqualTo(1) + for (instance in partitions.first()) { + for (enrollment in getEnrollments(instance)) { + if (enrollment.uid() == enrollment1Id) { + assertThat(enrollment.notes()!!.size).isEqualTo(1) + } else { + assertThat(enrollment.notes()!!.size).isEqualTo(0) + } + } + } + } + + @Test + @Throws(D2Error::class) + fun build_payload_with_event_notes() { + storeTrackerData() + + d2.noteModule().notes().blockingAdd( + NoteCreateProjection.builder() + .event(event1Id) + .noteType(Note.NoteType.EVENT_NOTE) + .value("This is an event note") + .build() + ) + val partitions = partitions + + assertThat(partitions.size).isEqualTo(1) + assertThat(partitions.first().size).isEqualTo(1) + for (instance in partitions.first()) { + for (enrollment in getEnrollments(instance)) { + if (enrollment.uid() == enrollment1Id) { + for (event in getEvents(enrollment)) { + if (event.uid() == event1Id) { + assertThat(event.notes()!!.size).isEqualTo(1) + } else { + assertThat(enrollment.notes()!!.size).isEqualTo(0) + } + } + } + } + } + } + + @Test + fun do_not_ignore_elements_not_present_in_the_import_summary() { + storeTrackerData() + + // Only enrollment1 and event1 are TO_UPDATE + enrollmentStore.setAggregatedSyncState(enrollment2Id, State.SYNCED) + enrollmentStore.setSyncState(enrollment2Id, State.SYNCED) + eventStore.setAggregatedSyncState(event2Id, State.SYNCED) + eventStore.setSyncState(event2Id, State.SYNCED) + + dhis2MockServer.enqueueMockResponse("imports/web_response_with_empty_events.json") + d2.trackedEntityModule().trackedEntityInstances().blockingUpload() + + val trackedEntityInstance = teiStore.selectByUid(teiId) + assertThat(trackedEntityInstance!!.syncState()).isEqualTo(State.SYNCED) + assertThat(trackedEntityInstance.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + + assertThat(enrollmentStore.selectByUid(enrollment1Id)!!.syncState()).isEqualTo(State.SYNCED) + assertThat(enrollmentStore.selectByUid(enrollment1Id)!!.aggregatedSyncState()).isEqualTo(State.TO_UPDATE) + assertThat(enrollmentStore.selectByUid(enrollment2Id)!!.syncState()).isEqualTo(State.SYNCED) + assertThat(enrollmentStore.selectByUid(enrollment2Id)!!.aggregatedSyncState()).isEqualTo(State.SYNCED) + + assertThat(eventStore.selectByUid(event1Id)!!.syncState()).isEqualTo(State.TO_UPDATE) + assertThat(eventStore.selectByUid(event2Id)!!.syncState()).isEqualTo(State.SYNCED) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGeneratorMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGeneratorMockIntegrationShould.kt deleted file mode 100644 index b652e3c351..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGeneratorMockIntegrationShould.kt +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.trackedentity.internal - -import com.google.common.collect.Lists -import com.google.common.truth.Truth.assertThat -import org.hisp.dhis.android.core.arch.call.executors.internal.D2CallExecutor -import org.hisp.dhis.android.core.arch.helpers.UidsHelper.getUidsList -import org.hisp.dhis.android.core.common.ObjectWithUid -import org.hisp.dhis.android.core.common.State -import org.hisp.dhis.android.core.data.relationship.RelationshipSamples -import org.hisp.dhis.android.core.data.trackedentity.TrackedEntityDataValueSamples -import org.hisp.dhis.android.core.data.trackedentity.TrackedEntityInstanceSamples -import org.hisp.dhis.android.core.enrollment.Enrollment -import org.hisp.dhis.android.core.enrollment.EnrollmentInternalAccessor -import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore -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.EventStore -import org.hisp.dhis.android.core.event.internal.EventStoreImpl -import org.hisp.dhis.android.core.maintenance.D2Error -import org.hisp.dhis.android.core.maintenance.internal.ForeignKeyCleanerImpl -import org.hisp.dhis.android.core.note.Note -import org.hisp.dhis.android.core.note.NoteCreateProjection -import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitStore.create -import org.hisp.dhis.android.core.program.internal.ProgramStageStore -import org.hisp.dhis.android.core.relationship.RelationshipConstraintType -import org.hisp.dhis.android.core.relationship.RelationshipItem -import org.hisp.dhis.android.core.relationship.RelationshipItemTrackedEntityInstance -import org.hisp.dhis.android.core.relationship.internal.RelationshipItemStoreImpl -import org.hisp.dhis.android.core.relationship.internal.RelationshipStoreImpl -import org.hisp.dhis.android.core.relationship.internal.RelationshipTypeStore -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor -import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestMetadataEnqueable -import org.hisp.dhis.android.core.utils.runner.D2JunitRunner -import org.junit.After -import org.junit.BeforeClass -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(D2JunitRunner::class) -class TrackedEntityInstancePostPayloadGeneratorMockIntegrationShould : BaseMockIntegrationTestMetadataEnqueable() { - - private val teiId = "teiId" - private val enrollment1Id = "enrollment1Id" - private val enrollment2Id = "enrollment2Id" - private val enrollment3Id = "enrollment3Id" - private val event1Id = "event1Id" - private val event2Id = "event2Id" - private val event3Id = "event3Id" - - @After - @Throws(D2Error::class) - fun tearDown() { - d2.wipeModule().wipeData() - } - - @Test - fun build_payload_with_different_enrollments() { - storeTrackedEntityInstance() - - val partitions = partitions - - assertThat(partitions.size).isEqualTo(1) - assertThat(partitions.first().size).isEqualTo(1) - - for (instance in partitions.first()) { - assertThat(getEnrollments(instance).size).isEqualTo(2) - for (enrollment in getEnrollments(instance)) { - assertThat(getEvents(enrollment).size).isEqualTo(1) - for (event in getEvents(enrollment)) { - assertThat(event.trackedEntityDataValues()!!.size).isEqualTo(1) - } - } - } - } - - private val partitions: List> - get() = payloadGenerator.getTrackedEntityInstancesPartitions( - teiStore.queryTrackedEntityInstancesToSync() - ) - - @Test - fun build_payload_with_the_enrollments_events_and_values_set_for_upload() { - storeTrackedEntityInstance() - - val partitions = partitions - - assertThat(partitions.size).isEqualTo(1) - assertThat(partitions.first().size).isEqualTo(1) - - for (instance in partitions.first()) { - assertThat(getEnrollments(instance).size).isEqualTo(2) - for (enrollment in getEnrollments(instance)) { - assertThat(getEvents(enrollment).size).isEqualTo(1) - for (event in getEvents(enrollment)) { - assertThat(event.trackedEntityDataValues()!!.size).isEqualTo(1) - } - } - } - } - - @Test - fun build_payload_without_events_marked_as_error() { - storeTrackedEntityInstance() - - enrollmentStore.setState(enrollment3Id, State.TO_POST) - - val partitions = partitions - assertThat(partitions.size).isEqualTo(1) - assertThat(partitions.first().size).isEqualTo(1) - - for (instance in partitions.first()) { - assertThat(getEnrollments(instance).size).isEqualTo(3) - for (enrollment in getEnrollments(instance)) { - if (enrollment.uid() == enrollment3Id) { - assertThat(getEvents(enrollment).size).isEqualTo(0) - } else { - assertThat(getEvents(enrollment).size).isEqualTo(1) - } - } - } - } - - @Test - fun handle_import_conflicts_correctly() { - storeTrackedEntityInstance() - - dhis2MockServer.enqueueMockResponse("imports/web_response_with_import_conflicts_2.json") - - d2.trackedEntityModule().trackedEntityInstances().blockingUpload() - - assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(3) - } - - @Test - fun delete_old_import_conflicts() { - storeTrackedEntityInstance() - - dhis2MockServer.enqueueMockResponse("imports/web_response_with_import_conflicts_2.json") - - d2.trackedEntityModule().trackedEntityInstances().blockingUpload() - - assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(3) - - teiStore.setState("teiId", State.TO_POST) - enrollmentStore.setState("enrollment1Id", State.TO_POST) - enrollmentStore.setState("enrollment2Id", State.TO_POST) - eventStore.setState("event1Id", State.TO_POST) - eventStore.setState("event2Id", State.TO_POST) - - dhis2MockServer.enqueueMockResponse("imports/web_response_with_import_conflicts_3.json") - - d2.trackedEntityModule().trackedEntityInstances().blockingUpload() - assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(1) - } - - @Test - @Throws(D2Error::class) - fun handle_tei_deletions() { - storeTrackedEntityInstance() - - d2.trackedEntityModule().trackedEntityInstances().uid("teiId").blockingDelete() - - // There is no TEIs to upload, so there is no request to enqueue. - d2.trackedEntityModule().trackedEntityInstances().blockingUpload() - assertThat(d2.trackedEntityModule().trackedEntityInstances().blockingCount()).isEqualTo(0) - assertThat(d2.enrollmentModule().enrollments().blockingCount()).isEqualTo(0) - assertThat(d2.eventModule().events().blockingCount()).isEqualTo(0) - assertThat(d2.importModule().trackerImportConflicts().blockingCount()).isEqualTo(0) - } - - @Test - @Throws(Exception::class) - fun recreate_teis_with_filters_and_relationships() { - val tei1 = "tei1" - val tei2 = "tei2" - val tei3 = "tei3" - val tei4 = "tei4" - val tei5 = "tei5" - - storeSimpleTrackedEntityInstance(tei1, State.TO_POST) - storeSimpleTrackedEntityInstance(tei2, State.TO_POST) - storeSimpleTrackedEntityInstance(tei3, State.TO_POST) - storeSimpleTrackedEntityInstance(tei4, State.TO_POST) - storeSimpleTrackedEntityInstance(tei5, State.SYNCED) - - storeRelationship("relationship1", tei1, tei2) - storeRelationship("relationship2", tei2, tei3) - storeRelationship("relationship3", tei1, tei5) - storeRelationship("relationship4", tei5, tei4) - - val partitions = payloadGenerator.getTrackedEntityInstancesPartitions( - d2.trackedEntityModule().trackedEntityInstances().byUid().eq(tei1) - .byState().`in`(*State.uploadableStates()).blockingGet() - ) - - assertThat(partitions.size).isEqualTo(1) - assertThat(partitions[0].size).isEqualTo(3) - assertThat(getUidsList(partitions[0]).containsAll(Lists.newArrayList(tei1, tei2, tei3))).isTrue() - } - - @Test - fun mark_payload_as_uploading() { - storeTrackedEntityInstance() - - // Ignore result. Just interested in check that target TEIs are marked as UPLOADING - val partitions = partitions - - val instance = teiStore.selectFirst() - assertThat(instance!!.state()).isEqualTo(State.UPLOADING) - - val enrollments = enrollmentStore.selectAll() - for (enrollment in enrollments) { - if ("enrollment1Id" == enrollment.uid() || "enrollment2Id" == enrollment.uid()) { - assertThat(enrollment.state()).isEqualTo(State.UPLOADING) - } else { - assertThat(enrollment.state()).isNotEqualTo(State.UPLOADING) - } - } - - val events = eventStore.selectAll() - for (event in events) { - if (event1Id == event.uid() || event2Id == event.uid()) { - assertThat(event.state()).isEqualTo(State.UPLOADING) - } else { - assertThat(event.state()).isNotEqualTo(State.UPLOADING) - } - } - } - - @Test - fun restore_payload_states_when_error_500() { - storeTrackedEntityInstance() - - dhis2MockServer.enqueueMockResponse(500, "Internal Server Error") - d2.trackedEntityModule().trackedEntityInstances().blockingUpload() - - val instance = teiStore.selectFirst() - assertThat(instance!!.state()).isEqualTo(State.TO_POST) - - val enrollments = enrollmentStore.selectAll() - for (enrollment in enrollments) { - if (enrollment1Id == enrollment.uid() || enrollment2Id == enrollment.uid()) { - assertThat(enrollment.state()).isEqualTo(State.TO_POST) - } - } - - val events = eventStore.selectAll() - for (event in events) { - if (event1Id == event.uid()) { - assertThat(event.state()).isEqualTo(State.TO_UPDATE) - } - if (event2Id == event.uid()) { - assertThat(event.state()).isEqualTo(State.SYNCED_VIA_SMS) - } - } - } - - @Test - @Throws(D2Error::class) - fun build_payload_with_enrollment_notes() { - storeTrackedEntityInstance() - d2.noteModule().notes().blockingAdd( - NoteCreateProjection.builder() - .enrollment(enrollment1Id) - .noteType(Note.NoteType.ENROLLMENT_NOTE) - .value("This is an enrollment note") - .build() - ) - val partitions = partitions - assertThat(partitions.size).isEqualTo(1) - assertThat(partitions.first().size).isEqualTo(1) - for (instance in partitions.first()) { - for (enrollment in getEnrollments(instance)) { - if (enrollment.uid() == enrollment1Id) { - assertThat(enrollment.notes()!!.size).isEqualTo(1) - } else { - assertThat(enrollment.notes()!!.size).isEqualTo(0) - } - } - } - } - - @Test - @Throws(D2Error::class) - fun build_payload_with_event_notes() { - storeTrackedEntityInstance() - - d2.noteModule().notes().blockingAdd( - NoteCreateProjection.builder() - .event(event1Id) - .noteType(Note.NoteType.EVENT_NOTE) - .value("This is an event note") - .build() - ) - val partitions = partitions - - assertThat(partitions.size).isEqualTo(1) - assertThat(partitions.first().size).isEqualTo(1) - for (instance in partitions.first()) { - for (enrollment in getEnrollments(instance)) { - if (enrollment.uid() == enrollment1Id) { - for (event in getEvents(enrollment)) { - if (event.uid() == event1Id) { - assertThat(event.notes()!!.size).isEqualTo(1) - } else { - assertThat(enrollment.notes()!!.size).isEqualTo(0) - } - } - } - } - } - } - - @Test - fun do_not_ignore_elements_not_present_in_the_import_summary() { - storeTrackedEntityInstance() - - // Only enrollment1 and event1 are TO_UPDATE - enrollmentStore.setState(enrollment2Id, State.SYNCED) - eventStore.setState(event2Id, State.SYNCED) - - dhis2MockServer.enqueueMockResponse("imports/web_response_with_empty_events.json") - d2.trackedEntityModule().trackedEntityInstances().blockingUpload() - - val trackedEntityInstance = teiStore.selectByUid(teiId) - assertThat(trackedEntityInstance!!.state()).isEqualTo(State.TO_UPDATE) - - assertThat(enrollmentStore.selectByUid(enrollment1Id)!!.state()).isEqualTo(State.TO_UPDATE) - assertThat(enrollmentStore.selectByUid(enrollment2Id)!!.state()).isEqualTo(State.SYNCED) - - assertThat(eventStore.selectByUid(event1Id)!!.state()).isEqualTo(State.TO_UPDATE) - assertThat(eventStore.selectByUid(event2Id)!!.state()).isEqualTo(State.SYNCED) - } - - private fun storeTrackedEntityInstance() { - val orgUnit = create(databaseAdapter).selectFirst() - val teiType = TrackedEntityTypeStore.create(databaseAdapter).selectFirst() - val program = d2.programModule().programs().one().blockingGet() - val programStage = ProgramStageStore.create(databaseAdapter).selectFirst() - - val dataValue1 = TrackedEntityDataValueSamples.get().toBuilder().event(event1Id).build() - - val event1 = Event.builder() - .uid(event1Id) - .enrollment(enrollment1Id) - .organisationUnit(orgUnit!!.uid()) - .program(program.uid()) - .programStage(programStage!!.uid()) - .state(State.TO_UPDATE) - .trackedEntityDataValues(listOf(dataValue1)) - .build() - - val enrollment1 = EnrollmentInternalAccessor.insertEvents(Enrollment.builder(), listOf(event1)) - .uid(enrollment1Id) - .program(program.uid()) - .organisationUnit(orgUnit.uid()) - .state(State.TO_POST) - .trackedEntityInstance(teiId) - .build() - val dataValue2 = TrackedEntityDataValueSamples.get().toBuilder().event(event2Id).build() - - val event2 = Event.builder() - .uid(event2Id) - .enrollment(enrollment2Id) - .organisationUnit(orgUnit.uid()) - .program(program.uid()) - .programStage(programStage.uid()) - .state(State.SYNCED_VIA_SMS) - .trackedEntityDataValues(listOf(dataValue2)) - .build() - - val enrollment2 = EnrollmentInternalAccessor.insertEvents(Enrollment.builder(), listOf(event2)) - .uid(enrollment2Id) - .program(program.uid()) - .organisationUnit(orgUnit.uid()) - .state(State.TO_POST) - .trackedEntityInstance(teiId) - .build() - - val dataValue3 = TrackedEntityDataValueSamples.get().toBuilder().event(event3Id).build() - - val event3 = Event.builder() - .uid(event3Id) - .enrollment(enrollment3Id) - .organisationUnit(orgUnit.uid()) - .program(program.uid()) - .programStage(programStage.uid()) - .state(State.ERROR) - .trackedEntityDataValues(listOf(dataValue3)) - .build() - - val enrollment3 = EnrollmentInternalAccessor.insertEvents(Enrollment.builder(), listOf(event3)) - .uid(enrollment3Id) - .program(program.uid()) - .organisationUnit(orgUnit.uid()) - .state(State.SYNCED) - .trackedEntityInstance(teiId) - .build() - - val tei = TrackedEntityInstanceInternalAccessor.insertEnrollments( - TrackedEntityInstance.builder(), listOf(enrollment1, enrollment2, enrollment3) - ) - .uid(teiId) - .trackedEntityType(teiType!!.uid()) - .organisationUnit(orgUnit.uid()) - .state(State.TO_POST) - .build() - - teiStore.insert(tei) - enrollmentStore.insert(enrollment1) - enrollmentStore.insert(enrollment2) - enrollmentStore.insert(enrollment3) - eventStore.insert(event1) - eventStore.insert(event2) - eventStore.insert(event3) - teiDataValueStore.insert(dataValue1) - teiDataValueStore.insert(dataValue2) - teiDataValueStore.insert(dataValue3) - } - - private fun storeSimpleTrackedEntityInstance(teiUid: String, state: State) { - val orgUnit = create(databaseAdapter).selectFirst() - val teiType = TrackedEntityTypeStore.create(databaseAdapter).selectFirst() - TrackedEntityInstanceStoreImpl.create(databaseAdapter).insert( - TrackedEntityInstanceSamples.get().toBuilder() - .uid(teiUid) - .trackedEntityType(teiType!!.uid()) - .organisationUnit(orgUnit!!.uid()) - .state(state) - .build() - ) - } - - @Throws(D2Error::class) - private fun storeRelationship(relationshipUid: String, fromUid: String, toUid: String) { - val relationshipType = RelationshipTypeStore.create(databaseAdapter).selectFirst() - val executor = D2CallExecutor.create(databaseAdapter) - executor.executeD2CallTransactionally { - RelationshipStoreImpl.create(databaseAdapter).insert( - RelationshipSamples.get230(relationshipUid, fromUid, toUid).toBuilder() - .relationshipType(relationshipType!!.uid()).build() - ) - RelationshipItemStoreImpl.create(databaseAdapter).insert( - RelationshipItem.builder() - .relationship(ObjectWithUid.create(relationshipUid)) - .relationshipItemType(RelationshipConstraintType.FROM) - .trackedEntityInstance( - RelationshipItemTrackedEntityInstance.builder().trackedEntityInstance(fromUid).build() - ) - .build() - ) - RelationshipItemStoreImpl.create(databaseAdapter).insert( - RelationshipItem.builder() - .relationship(ObjectWithUid.create(relationshipUid)) - .relationshipItemType(RelationshipConstraintType.TO) - .trackedEntityInstance( - RelationshipItemTrackedEntityInstance.builder().trackedEntityInstance(toUid).build() - ) - .build() - ) - ForeignKeyCleanerImpl.create(databaseAdapter).cleanForeignKeyErrors() - null - } - } - - private fun getEnrollments(trackedEntityInstance: TrackedEntityInstance): List { - return TrackedEntityInstanceInternalAccessor.accessEnrollments(trackedEntityInstance) - } - - private fun getEvents(enrollment: Enrollment): List { - return EnrollmentInternalAccessor.accessEvents(enrollment) - } - - companion object { - private lateinit var payloadGenerator: TrackedEntityInstancePostPayloadGenerator - private lateinit var teiStore: TrackedEntityInstanceStore - private lateinit var teiDataValueStore: TrackedEntityDataValueStore - private lateinit var eventStore: EventStore - private lateinit var enrollmentStore: EnrollmentStore - - @BeforeClass - @JvmStatic - @Throws(Exception::class) - fun setUp() { - BaseMockIntegrationTestMetadataEnqueable.setUpClass() - payloadGenerator = objects.d2DIComponent.trackedEntityInstancePostPayloadGenerator() - teiStore = TrackedEntityInstanceStoreImpl.create(databaseAdapter) - teiDataValueStore = TrackedEntityDataValueStoreImpl.create(databaseAdapter) - eventStore = EventStoreImpl.create(databaseAdapter) - enrollmentStore = EnrollmentStoreImpl.create(databaseAdapter) - } - } -} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStoreIntegrationShould.java index 9021ead822..e886b0d2b2 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStoreIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStoreIntegrationShould.java @@ -61,7 +61,8 @@ protected TrackedEntityInstance buildObjectToUpdate() { @Override protected TrackedEntityInstance buildObjectWithToDeleteState() { return TrackedEntityInstanceSamples.get().toBuilder() - .state(State.TO_UPDATE) + .syncState(State.TO_UPDATE) + .aggregatedSyncState(State.TO_UPDATE) .deleted(true) .build(); } @@ -69,7 +70,8 @@ protected TrackedEntityInstance buildObjectWithToDeleteState() { @Override protected TrackedEntityInstance buildObjectWithSyncedState() { return TrackedEntityInstanceSamples.get().toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) + .aggregatedSyncState(State.SYNCED) .build(); } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectStoreIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectStoreIntegrationShould.java new file mode 100644 index 0000000000..721b0ed005 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectStoreIntegrationShould.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal; + +import org.hisp.dhis.android.core.data.database.ObjectWithoutUidStoreAbstractIntegrationShould; +import org.hisp.dhis.android.core.data.tracker.importer.internal.TrackerJobObjectSamples; +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) +public class TrackerJobObjectStoreIntegrationShould extends ObjectWithoutUidStoreAbstractIntegrationShould { + + public TrackerJobObjectStoreIntegrationShould() { + super(TrackerJobObjectStore.create(TestDatabaseAdapterFactory.get()), TrackerJobObjectTableInfo.TABLE_INFO, + TestDatabaseAdapterFactory.get()); + } + + @Override + protected TrackerJobObject buildObject() { + return TrackerJobObjectSamples.get1(); + } + + @Override + protected TrackerJobObject buildObjectToUpdate() { + return TrackerJobObjectSamples.get1() + .toBuilder() + .jobUid("anotherJobId") + .build(); + } +} \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.java b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.java index 380a4b8c1e..022fd24fce 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.java @@ -29,11 +29,14 @@ package org.hisp.dhis.android.core.utils.integration.mock; import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore; import org.hisp.dhis.android.core.data.imports.TrackerImportConflictSamples; import org.hisp.dhis.android.core.data.maintenance.D2ErrorSamples; +import org.hisp.dhis.android.core.datastore.KeyValuePair; +import org.hisp.dhis.android.core.datastore.internal.LocalDataStoreStore; import org.hisp.dhis.android.core.imports.ImportStatus; -import org.hisp.dhis.android.core.imports.TrackerImportConflict; import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore; +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStoreImpl; import org.hisp.dhis.android.core.maintenance.D2Error; import org.hisp.dhis.android.core.maintenance.D2ErrorCode; import org.hisp.dhis.android.core.maintenance.D2ErrorComponent; @@ -61,6 +64,7 @@ public static void setUpClass() throws Exception { downloadAggregatedData(); storeSomeD2Errors(); storeSomeConflicts(); + storeSomeKeyValuesInLocalDataStore(); } } @@ -109,8 +113,8 @@ private static void storeSomeD2Errors() { } private static void storeSomeConflicts() { - ObjectStore trackerImportConflictStore = - TrackerImportConflictStore.create(databaseAdapter); + TrackerImportConflictStore trackerImportConflictStore = + TrackerImportConflictStoreImpl.create(databaseAdapter); trackerImportConflictStore.insert(TrackerImportConflictSamples.get().toBuilder() .trackedEntityInstance(null) .enrollment(null) @@ -129,4 +133,16 @@ private static void storeSomeConflicts() { .build() ); } + + private static void storeSomeKeyValuesInLocalDataStore() { + ObjectWithoutUidStore dataStore = LocalDataStoreStore.create(databaseAdapter); + dataStore.insert(KeyValuePair.builder() + .key("key1") + .value("value1") + .build()); + dataStore.insert(KeyValuePair.builder() + .key("key2") + .value("value2") + .build()); + } } \ No newline at end of file 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 new file mode 100644 index 0000000000..8646efd433 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStoreIntegrationShould.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004-2021, 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 new file mode 100644 index 0000000000..7fdc5afd2c --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStoreIntegrationShould.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004-2021, 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/VisualizationEndpointCallShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEndpointCallShould.kt new file mode 100644 index 0000000000..880c6f8eaa --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEndpointCallShould.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2021, 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 com.google.common.collect.Lists +import com.google.common.truth.Truth +import io.reactivex.Single +import java.util.* +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyEnqueable +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.hisp.dhis.android.core.visualization.Visualization +import org.junit.BeforeClass +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class VisualizationEndpointCallShould : BaseMockIntegrationTestEmptyEnqueable() { + + companion object { + private lateinit var visualizationsSingle: Single> + + @BeforeClass + @JvmStatic + @Throws(Exception::class) + internal fun setUpClass() { + BaseMockIntegrationTestEmptyEnqueable.setUpClass() + visualizationsSingle = objects.d2DIComponent.internalModules().visualization.visualizationCall.download( + HashSet( + Lists.newArrayList("PYBH8ZaAQnC", "FAFa11yFeFe") + ) + ) + dhis2MockServer.enqueueMockResponse("visualization/visualizations.json") + d2.databaseAdapter().setForeignKeyConstraintsEnabled(false) + } + } + + @Test + fun download_persist_and_get_visualizations_successfully() { + var visualizations = visualizationsSingle.blockingGet() + Truth.assertThat(visualizations.isEmpty()).isFalse() + visualizations = d2.visualizationModule().visualizations().blockingGet() + Truth.assertThat(visualizations.isEmpty()).isFalse() + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationStoreIntegrationShould.kt new file mode 100644 index 0000000000..ba69683f9f --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/visualization/internal/VisualizationStoreIntegrationShould.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2004-2021, 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.IdentifiableObjectStoreAbstractIntegrationShould +import org.hisp.dhis.android.core.data.visualization.VisualizationSamples +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.HideEmptyItemStrategy +import org.hisp.dhis.android.core.visualization.Visualization +import org.hisp.dhis.android.core.visualization.VisualizationTableInfo +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class VisualizationStoreIntegrationShould : IdentifiableObjectStoreAbstractIntegrationShould( + VisualizationStore.create(TestDatabaseAdapterFactory.get()), + VisualizationTableInfo.TABLE_INFO, + TestDatabaseAdapterFactory.get() +) { + override fun buildObject(): Visualization { + return VisualizationSamples.visualization() + } + + override fun buildObjectToUpdate(): Visualization { + return VisualizationSamples.visualization().toBuilder() + .hideEmptyRowItems(HideEmptyItemStrategy.AFTER_LAST) + .build() + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.java index c4cb387808..e2a5f5cc36 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.java @@ -28,19 +28,26 @@ package org.hisp.dhis.android.core.wipe; -import org.hisp.dhis.android.core.common.StorableObjectWithUid; import org.hisp.dhis.android.core.data.database.DatabaseAssert; +import org.hisp.dhis.android.core.datastore.KeyValuePair; +import org.hisp.dhis.android.core.datastore.internal.LocalDataStoreStore; +import org.hisp.dhis.android.core.datavalue.DataValueConflict; +import org.hisp.dhis.android.core.datavalue.internal.DataValueConflictStore; import org.hisp.dhis.android.core.fileresource.FileResource; import org.hisp.dhis.android.core.fileresource.internal.FileResourceStoreImpl; import org.hisp.dhis.android.core.imports.TrackerImportConflict; -import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore; +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStoreImpl; import org.hisp.dhis.android.core.maintenance.D2Error; import org.hisp.dhis.android.core.maintenance.D2ErrorCode; import org.hisp.dhis.android.core.maintenance.internal.D2ErrorStore; -import org.hisp.dhis.android.core.tracker.importer.internal.TrackerJobStore; +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType; +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerJobObject; +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerJobObjectStore; import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyDispatcher; import org.junit.Test; +import java.util.Date; + public class WipeDBCallMockIntegrationShould extends BaseMockIntegrationTestEmptyDispatcher { @Test @@ -85,9 +92,29 @@ private void givenOthersInDatabase() { .errorDescription("Sample error") .build()); - TrackerImportConflictStore.create(databaseAdapter).insert(TrackerImportConflict.builder().build()); + TrackerImportConflictStoreImpl.create(databaseAdapter).insert(TrackerImportConflict.builder().build()); FileResourceStoreImpl.create(databaseAdapter).insert(FileResource.builder().uid("uid").build()); - TrackerJobStore.create(databaseAdapter).insert(StorableObjectWithUid.create("uid")); + TrackerJobObjectStore.create(databaseAdapter).insert( + TrackerJobObject.builder() + .jobUid("uid") + .trackerType(TrackerImporterObjectType.EVENT) + .objectUid("oUid") + .lastUpdated(new Date()) + .build() + ); + DataValueConflictStore.create(databaseAdapter).insert(DataValueConflict.builder().build()); + LocalDataStoreStore.create(databaseAdapter).insert( + KeyValuePair.builder() + .key("key1") + .value("value1") + .build() + ); + LocalDataStoreStore.create(databaseAdapter).insert( + KeyValuePair.builder() + .key("key2") + .value("value2") + .build() + ); } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsDataGenerator.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsDataGenerator.kt index ad48678312..d992ad0b09 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsDataGenerator.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsDataGenerator.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsDatabaseFiller.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsDatabaseFiller.kt index c15c4e1ed1..cc390cf8b0 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsDatabaseFiller.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsDatabaseFiller.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsMetadataGenerator.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsMetadataGenerator.kt index c907c7ef47..63d9c8d986 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsMetadataGenerator.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsMetadataGenerator.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsParams.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsParams.kt index a6303e5d10..1018f964a5 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsParams.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsParams.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/BaseLocalAnalyticsDatabaseSizeMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/BaseLocalAnalyticsDatabaseSizeMockIntegrationShould.kt index 930cfdd4c3..bc058d2453 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/BaseLocalAnalyticsDatabaseSizeMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/BaseLocalAnalyticsDatabaseSizeMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/LocalAnalyticsSuperLargeDatabaseMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/LocalAnalyticsSuperLargeDatabaseMockIntegrationShould.kt index e4cf23ccaf..b01a52cddf 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/LocalAnalyticsSuperLargeDatabaseMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/LocalAnalyticsSuperLargeDatabaseMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsAggregatedMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsAggregatedMockIntegrationShould.kt index 371824e0c8..3ff629db87 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsAggregatedMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsAggregatedMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTest.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTest.kt index 90cfb1022c..6769a46b1f 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTest.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTest.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTrackerMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTrackerMockIntegrationShould.kt index 190a1dc137..93523a9557 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTrackerMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTrackerMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedDefaultDataMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedDefaultDataMockIntegrationShould.kt index 72aef1a7b2..48979a49ce 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedDefaultDataMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedDefaultDataMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedLargeDataMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedLargeDataMockIntegrationShould.kt index e8c0e8f83f..5a27775958 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedLargeDataMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedLargeDataMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedSuperLargeDataMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedSuperLargeDataMockIntegrationShould.kt index b554574b00..e8ce6ed5f8 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedSuperLargeDataMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedSuperLargeDataMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerDefaultMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerDefaultMockIntegrationShould.kt index 9f13e98f4b..1c2e6a08cc 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerDefaultMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerDefaultMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerLargeMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerLargeMockIntegrationShould.kt index 136301a14d..0d78bf217d 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerLargeMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerLargeMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerSuperLargeMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerSuperLargeMockIntegrationShould.kt index 9ad98d25dd..579ba119ef 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerSuperLargeMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerSuperLargeMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/arch/helpers/FileResizerHelperShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/arch/helpers/FileResizerHelperShould.java index 520961ec61..8352464458 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/arch/helpers/FileResizerHelperShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/arch/helpers/FileResizerHelperShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryCollectionRepositoryMockIntegrationShould.java index a286fca2a3..ce36785f97 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryComboCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryComboCollectionRepositoryMockIntegrationShould.java index 08a4d370db..a67c83712c 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryComboCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryComboCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryModuleMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryModuleMockIntegrationShould.java index 3b1cf00142..ed66204187 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryModuleMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryModuleMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryOptionComboCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryOptionComboCollectionRepositoryMockIntegrationShould.java index cc3ade3933..ea94bcc637 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryOptionComboCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryOptionComboCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryOptionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryOptionRepositoryMockIntegrationShould.java index 0138db1c05..c3de878c85 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryOptionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/category/CategoryOptionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/constant/ConstantCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/constant/ConstantCollectionRepositoryMockIntegrationShould.java index 7b020c12ce..b84e4a394f 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/constant/ConstantCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/constant/ConstantCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/dataapproval/DataApprovalCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataapproval/DataApprovalCollectionRepositoryMockIntegrationShould.java index 0c9fe0f515..64790365dc 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataapproval/DataApprovalCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataapproval/DataApprovalCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/dataelement/DataElementCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataelement/DataElementCollectionRepositoryMockIntegrationShould.java index 18b0ed996f..afebb996c3 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataelement/DataElementCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataelement/DataElementCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCollectionRepositoryMockIntegrationShould.java index a5621fcd2b..40196ef8b2 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCompleteRegistrationCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCompleteRegistrationCollectionRepositoryMockIntegrationShould.java index dfc96a13e7..73c2451b02 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCompleteRegistrationCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCompleteRegistrationCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCompleteRegistrationObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCompleteRegistrationObjectRepositoryMockIntegrationShould.java index 5e29fa7250..5ef36bec2a 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCompleteRegistrationObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetCompleteRegistrationObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -57,7 +57,7 @@ public void delete() { DataSetCompleteRegistration dataSetCompleteRegistration = objectRepository().blockingGet(); assertThat(dataSetCompleteRegistration.deleted()).isTrue(); - assertThat(dataSetCompleteRegistration.state()).isEqualTo(State.TO_UPDATE); + assertThat(dataSetCompleteRegistration.syncState()).isEqualTo(State.TO_UPDATE); } @Test diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceCollectionRepositoryMockIntegrationShould.java index 74e7afb12e..f07df18a5b 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceSummaryCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceSummaryCollectionRepositoryMockIntegrationShould.java index a3fd8a0e84..ec5b1fcdfb 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceSummaryCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceSummaryCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetModuleMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetModuleMockIntegrationShould.java index 4e996bda8a..4c87247482 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetModuleMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetModuleMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/dataset/SectionCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/SectionCollectionRepositoryMockIntegrationShould.java index be81bb88ad..628a702e64 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/SectionCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/SectionCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/datastore/LocalDataStoreCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datastore/LocalDataStoreCollectionRepositoryMockIntegrationShould.java new file mode 100644 index 0000000000..7b7f4d06e0 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datastore/LocalDataStoreCollectionRepositoryMockIntegrationShould.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2004-2021, 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.datastore; + +import static com.google.common.truth.Truth.assertThat; + +import org.hisp.dhis.android.core.datastore.KeyValuePair; +import org.hisp.dhis.android.core.datastore.LocalDataStoreObjectRepository; +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) +public class LocalDataStoreCollectionRepositoryMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { + + @Test + public void find_all() { + assertThat(d2.dataStoreModule().localDataStore().blockingGet().size()).isEqualTo(2); + } + + @Test + public void filter_by_key() { + KeyValuePair pair = d2.dataStoreModule().localDataStore() + .byKey().eq("key1") + .one() + .blockingGet(); + + assertThat(pair.key()).isEqualTo("key1"); + assertThat(pair.value()).isEqualTo("value1"); + } + + @Test + public void filter_by_value() { + KeyValuePair pair = d2.dataStoreModule().localDataStore() + .byValue().eq("value2") + .one() + .blockingGet(); + + assertThat(pair.key()).isEqualTo("key2"); + assertThat(pair.value()).isEqualTo("value2"); + } + + @Test + public void return_object_repository() { + LocalDataStoreObjectRepository objectRepository = d2.dataStoreModule().localDataStore() + .value("key1"); + assertThat(objectRepository.blockingExists()).isEqualTo(Boolean.TRUE); + assertThat(objectRepository.blockingGet().value()).isEqualTo("value1"); + } +} \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datastore/LocalDataStoreObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datastore/LocalDataStoreObjectRepositoryMockIntegrationShould.java new file mode 100644 index 0000000000..07594ea5cd --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datastore/LocalDataStoreObjectRepositoryMockIntegrationShould.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2004-2021, 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.datastore; + +import static com.google.common.truth.Truth.assertThat; + +import org.hisp.dhis.android.core.datastore.LocalDataStoreObjectRepository; +import org.hisp.dhis.android.core.maintenance.D2Error; +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) +public class LocalDataStoreObjectRepositoryMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { + + @Test + public void update_value() throws D2Error { + String value = "new_value"; + + LocalDataStoreObjectRepository repository = objectRepository(); + + repository.blockingSet(value); + assertThat(repository.blockingGet().value()).isEqualTo(value); + + repository.blockingDelete(); + } + + + @Test + public void delete_value() throws D2Error { + LocalDataStoreObjectRepository repository = objectRepository(); + + repository.blockingSet("value"); + assertThat(repository.blockingExists()).isEqualTo(Boolean.TRUE); + repository.blockingDelete(); + assertThat(repository.blockingExists()).isEqualTo(Boolean.FALSE); + } + + @Test + public void return_that_a_value_exists_only_if_it_has_been_created() { + assertThat(d2.dataStoreModule().localDataStore() + .value("no_key").blockingExists()).isEqualTo(Boolean.FALSE); + assertThat(d2.dataStoreModule().localDataStore() + .value("key1").blockingExists()).isEqualTo(Boolean.TRUE); + } + + private LocalDataStoreObjectRepository objectRepository() { + return d2.dataStoreModule().localDataStore().value("new_key"); + } +} \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueCollectionRepositoryMockIntegrationShould.java index ff6076c45b..e7f77d6e44 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -179,7 +179,7 @@ public void filter_by_follow_up() { public void filter_by_state() { List dataValues = d2.dataValueModule().dataValues() - .byState().eq(State.SYNCED) + .bySyncState().eq(State.SYNCED) .blockingGet(); assertThat(dataValues.size()).isEqualTo(5); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueObjectRepositoryMockIntegrationShould.java index d314b92e5d..a287d3048e 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -84,7 +84,7 @@ public void delete_value_if_state_to_post() throws D2Error { repository.blockingSet("value"); assertThat(repository.blockingExists()).isEqualTo(Boolean.TRUE); - assertThat(repository.blockingGet().state()).isEqualTo(State.TO_POST); + assertThat(repository.blockingGet().syncState()).isEqualTo(State.TO_POST); repository.blockingDelete(); assertThat(repository.blockingExists()).isEqualTo(Boolean.FALSE); } @@ -96,11 +96,11 @@ public void set_state_to_delete_if_state_is_not_to_post() throws D2Error { repository.blockingSet("value"); DataValueStore.create(databaseAdapter).setState(repository.blockingGet(), State.ERROR); assertThat(repository.blockingExists()).isEqualTo(Boolean.TRUE); - assertThat(repository.blockingGet().state()).isEqualTo(State.ERROR); + assertThat(repository.blockingGet().syncState()).isEqualTo(State.ERROR); repository.blockingDelete(); assertThat(repository.blockingExists()).isEqualTo(Boolean.TRUE); assertThat(repository.blockingGet().deleted()).isEqualTo(Boolean.TRUE); - assertThat(repository.blockingGet().state()).isEqualTo(State.TO_UPDATE); + assertThat(repository.blockingGet().syncState()).isEqualTo(State.TO_UPDATE); } @Test @@ -111,12 +111,12 @@ public void set_not_deleted_when_updating_deleted_value() throws D2Error { DataValueStore.create(databaseAdapter).setState(repository.blockingGet(), State.TO_UPDATE); repository.blockingDelete(); - assertThat(repository.blockingGet().state()).isEqualTo(State.TO_UPDATE); assertThat(repository.blockingGet().deleted()).isEqualTo(Boolean.TRUE); + assertThat(repository.blockingGet().syncState()).isEqualTo(State.TO_UPDATE); repository.blockingSet("new_value"); - assertThat(repository.blockingGet().state()).isEqualTo(State.TO_UPDATE); assertThat(repository.blockingGet().deleted()).isEqualTo(Boolean.FALSE); + assertThat(repository.blockingGet().syncState()).isEqualTo(State.TO_UPDATE); } @Test diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentCollectionRepositoryMockIntegrationShould.java index 84db5f7422..c15e8cc617 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -179,9 +179,17 @@ public void filter_by_tracked_entity_instance() { } @Test - public void filter_by_state() { + public void filter_by_aggregated_sync_state() { List enrollments = d2.enrollmentModule().enrollments() - .byState().eq(State.SYNCED) + .byAggregatedSyncState().eq(State.SYNCED) + .blockingGet(); + assertThat(enrollments.size()).isEqualTo(2); + } + + @Test + public void filter_by_sync_state() { + List enrollments = d2.enrollmentModule().enrollments() + .bySyncState().eq(State.SYNCED) .blockingGet(); assertThat(enrollments.size()).isEqualTo(2); } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentObjectRepositoryMockIntegrationShould.java index 2ad9e6b34f..bb2a934c4e 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/event/EventCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventCollectionRepositoryMockIntegrationShould.java index 0d47b07ff9..9690a3d35b 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -221,7 +221,7 @@ public void filter_by_due_date() throws ParseException { public void filter_by_state() { List events = d2.eventModule().events() - .byState().eq(State.SYNCED) + .bySyncState().eq(State.SYNCED) .blockingGet(); assertThat(events.size()).isEqualTo(4); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventFilterCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventFilterCollectionRepositoryMockIntegrationShould.java index 72fa139cab..e43b76fa35 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventFilterCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventFilterCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/event/EventObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventObjectRepositoryMockIntegrationShould.java index 9bc4ffa5b8..36d98c59f0 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/event/search/EventQueryCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/search/EventQueryCollectionRepositoryMockIntegrationShould.java index 66733bd4ff..c4f5833bda 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/search/EventQueryCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/search/EventQueryCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.java index 9fad2b08a6..bee2c81041 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -138,7 +138,7 @@ public void filter_by_state() throws D2Error, IOException { d2.fileResourceModule().fileResources().blockingAdd(getFile()); List fileResources = d2.fileResourceModule().fileResources() - .byState().eq(State.TO_POST) + .bySyncState().eq(State.TO_POST) .blockingGet(); assertThat(fileResources.size()).isEqualTo(1); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/imports/TrackerImportConflictCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/imports/TrackerImportConflictCollectionRepositoryMockIntegrationShould.java index 228de14c31..f7ff0a90ae 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/imports/TrackerImportConflictCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/imports/TrackerImportConflictCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/indicator/IndicatorCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/indicator/IndicatorCollectionRepositoryMockIntegrationShould.java index 41eb50e990..03903afa90 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/indicator/IndicatorCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/indicator/IndicatorCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/indicator/IndicatorTypeCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/indicator/IndicatorTypeCollectionRepositoryMockIntegrationShould.java index edf9f3961b..14e5e94383 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/indicator/IndicatorTypeCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/indicator/IndicatorTypeCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/legendset/LegendCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/legendset/LegendCollectionRepositoryMockIntegrationShould.java index 081a7324cc..a4dff9f1e9 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/legendset/LegendCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/legendset/LegendCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/legendset/LegendSetCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/legendset/LegendSetCollectionRepositoryMockIntegrationShould.java index 3cfdf36609..91b75215f8 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/legendset/LegendSetCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/legendset/LegendSetCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/maintenance/D2ErrorCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/D2ErrorCollectionRepositoryMockIntegrationShould.java index b7a8bac574..9812f08f05 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/D2ErrorCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/D2ErrorCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/maintenance/ForeignKeyViolationCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/ForeignKeyViolationCollectionRepositoryMockIntegrationShould.java index d22a63715b..c1a97190d1 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/ForeignKeyViolationCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/ForeignKeyViolationCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/maintenance/MaintenanceMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/MaintenanceMockIntegrationShould.java index 7fbeeb88fe..08994bdd59 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/MaintenanceMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/maintenance/MaintenanceMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/note/NoteCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/note/NoteCollectionRepositoryMockIntegrationShould.java index e72e532342..1da29745be 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/note/NoteCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/note/NoteCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -28,6 +28,7 @@ package org.hisp.dhis.android.testapp.note; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.note.Note; import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; import org.hisp.dhis.android.core.utils.runner.D2JunitRunner; @@ -88,6 +89,12 @@ public void filter_by_stored_by() { assertThat(notes.size()).isEqualTo(10); } + @Test + public void filter_by_state() { + List notes = d2.noteModule().notes().bySyncState().eq(State.SYNCED).blockingGet(); + assertThat(notes.size()).isEqualTo(10); + } + @Test public void filter_by_stored_date() { List notes = d2.noteModule().notes().byStoredDate().eq("2018-03-19T15:20:55.058").blockingGet(); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionCollectionRepositoryMockIntegrationShould.java index 9424983b2e..23afb585a3 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/option/OptionGroupCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionGroupCollectionRepositoryMockIntegrationShould.java index 5a104a07ee..b89ca9c9b5 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionGroupCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionGroupCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/option/OptionSetCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionSetCollectionRepositoryMockIntegrationShould.java index 544957c016..5b72687541 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionSetCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/option/OptionSetCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitCollectionRepositoryMockIntegrationShould.java index e0186c02f1..7c655a04c9 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitGroupCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitGroupCollectionRepositoryMockIntegrationShould.java index 6fdfe34e87..abb11f8dbe 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitGroupCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitGroupCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitLevelCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitLevelCollectionRepositoryMockIntegrationShould.java index a8980545a0..d331daedf2 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitLevelCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/organisationunit/OrganisationUnitLevelCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/period/PeriodCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/period/PeriodCollectionRepositoryMockIntegrationShould.java index ae623efbd7..40b72de3f6 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/period/PeriodCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/period/PeriodCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramCollectionRepositoryMockIntegrationShould.java index a4a7187a09..d9b7c730ab 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramIndicatorCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramIndicatorCollectionRepositoryMockIntegrationShould.java index 39fda78266..56d791ec16 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramIndicatorCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramIndicatorCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleActionCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleActionCollectionRepositoryMockIntegrationShould.java index 6cc70ea834..82549eadbe 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleActionCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleActionCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleCollectionRepositoryMockIntegrationShould.java index d103d41747..f712c70eab 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleVariableCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleVariableCollectionRepositoryMockIntegrationShould.java index 71c6d960f8..f705c00d08 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleVariableCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramRuleVariableCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramSectionCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramSectionCollectionRepositoryMockIntegrationShould.java index 5dc5f3c62a..b9cb7a66e7 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramSectionCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramSectionCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageCollectionRepositoryMockIntegrationShould.java index 49717af923..67fcd013eb 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageDataElementCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageDataElementCollectionRepositoryMockIntegrationShould.java index 7e44c5e667..29b2893c28 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageDataElementCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageDataElementCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageSectionCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageSectionCollectionRepositoryMockIntegrationShould.java index b11f9495fc..1579e97567 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageSectionCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageSectionCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramTrackedEntityAttributeCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramTrackedEntityAttributeCollectionRepositoryMockIntegrationShould.java index 036c40fdb8..5f8590c31f 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramTrackedEntityAttributeCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramTrackedEntityAttributeCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould.java new file mode 100644 index 0000000000..33bdd601fb --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2021, 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.settings; + +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationsSetting; +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; + +import static com.google.common.truth.Truth.assertThat; + +@RunWith(D2JunitRunner.class) +public class AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould + extends BaseMockIntegrationTestFullDispatcher { + + @Test + public void find_analytics_settings() { + AnalyticsDhisVisualizationsSetting analyticsDhisVisualizationsSetting = d2 + .settingModule() + .analyticsSetting() + .visualizationsSettings() + .blockingGet(); + + assertThat(analyticsDhisVisualizationsSetting.home().size()).isEqualTo(2); + assertThat(analyticsDhisVisualizationsSetting.program().size()).isEqualTo(1); + assertThat(analyticsDhisVisualizationsSetting.dataSet().size()).isEqualTo(1); + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsSettingsObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsSettingsObjectRepositoryMockIntegrationShould.java index ad1eb2ee40..372f217b30 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsSettingsObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsSettingsObjectRepositoryMockIntegrationShould.java @@ -28,6 +28,7 @@ package org.hisp.dhis.android.testapp.settings; +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationsGroup; import org.hisp.dhis.android.core.settings.AnalyticsSettings; import org.hisp.dhis.android.core.settings.AnalyticsTeiSetting; import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; @@ -56,7 +57,7 @@ public void find_analytics_settings() { assertThat(teiSetting.data().indicators().size()).isEqualTo(1); assertThat(teiSetting.data().attributes().size()).isEqualTo(1); assertThat(teiSetting.whoNutritionData()).isNull(); - } else if("yEdtdG7ql9K".equals(teiSetting.uid())) { + } else if ("yEdtdG7ql9K".equals(teiSetting.uid())) { assertThat(teiSetting.data()).isNull(); assertThat(teiSetting.whoNutritionData().x().dataElements().size()).isEqualTo(1); assertThat(teiSetting.whoNutritionData().x().indicators().size()).isEqualTo(0); @@ -64,5 +65,13 @@ public void find_analytics_settings() { assertThat(teiSetting.whoNutritionData().y().indicators().size()).isEqualTo(1); } } + + for (AnalyticsDhisVisualizationsGroup analyticsDhisVisualizationsGroup : analyticsSettings.dhisVisualizations().home()) { + if (analyticsDhisVisualizationsGroup.id().equals("12345678910")) { + assertThat(analyticsDhisVisualizationsGroup.visualizations().size()).isEqualTo(2); + } else if (analyticsDhisVisualizationsGroup.id().equals("12345678911")) { + assertThat(analyticsDhisVisualizationsGroup.visualizations().size()).isEqualTo(1); + } + } } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsTeiSettingCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsTeiSettingCollectionRepositoryMockIntegrationShould.java index 400e539045..6d45585ca3 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsTeiSettingCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsTeiSettingCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/settings/AppearanceSettingsObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AppearanceSettingsObjectRepositoryMockIntegrationShould.java index 13867b708a..914d46d9d8 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AppearanceSettingsObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AppearanceSettingsObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/settings/DataSetSettingObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/DataSetSettingObjectRepositoryMockIntegrationShould.java index 6a8f546bd1..8597dd4880 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/DataSetSettingObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/DataSetSettingObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/settings/GeneralSettingsObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/GeneralSettingsObjectRepositoryMockIntegrationShould.java index 27db9b8942..29c9079cec 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/GeneralSettingsObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/GeneralSettingsObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/settings/ProgramSettingObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/ProgramSettingObjectRepositoryMockIntegrationShould.java index 1be6f3f739..e912ffc480 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/ProgramSettingObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/ProgramSettingObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/settings/SynchronizationSettingObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/SynchronizationSettingObjectRepositoryMockIntegrationShould.java index 82020a0106..a989707cfd 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/SynchronizationSettingObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/SynchronizationSettingObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/settings/UserSettingsObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/UserSettingsObjectRepositoryMockIntegrationShould.java index 0180f3fa98..18ae4789f2 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/UserSettingsObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/UserSettingsObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/sms/SmsModuleMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/sms/SmsModuleMockIntegrationShould.java index 3e80302062..16f80cc83d 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/sms/SmsModuleMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/sms/SmsModuleMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -28,18 +28,18 @@ package org.hisp.dhis.android.testapp.sms; +import static com.google.common.truth.Truth.assertThat; + import org.hisp.dhis.android.core.sms.domain.interactor.ConfigCase; import org.hisp.dhis.android.core.sms.domain.interactor.QrCodeCase; import org.hisp.dhis.android.core.sms.domain.interactor.SmsSubmitCase; -import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTest; +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyEnqueable; import org.hisp.dhis.android.core.utils.runner.D2JunitRunner; import org.junit.Test; import org.junit.runner.RunWith; -import static com.google.common.truth.Truth.assertThat; - @RunWith(D2JunitRunner.class) -public class SmsModuleMockIntegrationShould extends BaseMockIntegrationTest { +public class SmsModuleMockIntegrationShould extends BaseMockIntegrationTestEmptyEnqueable { @Test public void access_submit_case() { diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeCollectionRepositoryMockIntegrationShould.java index 5cb9d0b4e8..d2b13e872d 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeValueCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeValueCollectionRepositoryMockIntegrationShould.java index 8def1caee7..ed9d2c84af 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeValueCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeValueCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeValueObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeValueObjectRepositoryMockIntegrationShould.java index 520b46e7fe..85cf70faa6 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeValueObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityAttributeValueObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityCollectionRepositoryMockIntegrationShould.java index 34186dbc56..ebe4dcf10a 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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 @@ -148,16 +148,26 @@ public void filter_by_geometry_coordinates() { } @Test - public void filter_by_state() { + public void filter_by_aggregated_sync_state() { List trackedEntityInstances = d2.trackedEntityModule().trackedEntityInstances() - .byState().eq(State.SYNCED) + .byAggregatedSyncState().eq(State.SYNCED) .blockingGet(); // TODO set to assertThat(trackedEntityInstances.size()).isEqualTo(2); after moving write tests to another db assertThat(trackedEntityInstances.size()).isEqualTo(1); } + @Test + public void filter_by_sync_state() { + List trackedEntityInstances = + d2.trackedEntityModule().trackedEntityInstances() + .bySyncState().eq(State.SYNCED) + .blockingGet(); + + assertThat(trackedEntityInstances.size()).isEqualTo(1); + } + @Test public void filter_by_deleted() { List trackedEntityInstances = diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityDataValueCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityDataValueCollectionRepositoryMockIntegrationShould.java index cf57064c32..fdaab33b9b 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityDataValueCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityDataValueCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityDataValueObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityDataValueObjectRepositoryMockIntegrationShould.java index dec5748097..4f62ae1017 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityDataValueObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityDataValueObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceCollectionRepositoryMockIntegrationShould.java index dba384f1ab..cbbf46449c 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceFilterCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceFilterCollectionRepositoryMockIntegrationShould.java index 870529c633..05e3edf1a7 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceFilterCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceFilterCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceObjectRepositoryMockIntegrationShould.java index 95ee947959..86fd92ed47 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityInstanceObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityTypeAttributeCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityTypeAttributeCollectionRepositoryMockIntegrationShould.java index bd320e9347..d4bcf86b2d 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityTypeAttributeCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/TrackedEntityTypeAttributeCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockIntegrationShould.java index 32ba61a2dc..3eeace1af5 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/user/AuthorityCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AuthorityCollectionRepositoryMockIntegrationShould.java index 265e1f78f6..b02db8f6f9 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AuthorityCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AuthorityCollectionRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/user/UserCredentialsObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/UserCredentialsObjectRepositoryMockIntegrationShould.java index 3d4c4b9ee5..9cf30cb494 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/UserCredentialsObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/UserCredentialsObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/user/UserObjectRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/UserObjectRepositoryMockIntegrationShould.java index 888f750a9a..84984563c8 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/UserObjectRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/UserObjectRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/validation/ValidationRuleRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/validation/ValidationRuleRepositoryMockIntegrationShould.java index 396637de8f..7141eb0bac 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/validation/ValidationRuleRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/validation/ValidationRuleRepositoryMockIntegrationShould.java @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2021, 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/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.java new file mode 100644 index 0000000000..a370a91de7 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/visualization/VisualizationCollectionRepositoryMockIntegrationShould.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2004-2021, 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.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; + +import static com.google.common.truth.Truth.assertThat; + +@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_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/main/assets/migrations/102.sql b/core/src/main/assets/migrations/102.sql new file mode 100644 index 0000000000..9422b0e887 --- /dev/null +++ b/core/src/main/assets/migrations/102.sql @@ -0,0 +1,3 @@ +# New table for tracker importer +DROP TABLE TrackerJob; +CREATE TABLE TrackerJobObject (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackerType TEXT NOT NULL, objectUid TEXT NOT NULL, jobUid TEXT NOT NULL, lastUpdated TEXT NOT NULL); diff --git a/core/src/main/assets/migrations/103.sql b/core/src/main/assets/migrations/103.sql new file mode 100644 index 0000000000..ceaca6fe7f --- /dev/null +++ b/core/src/main/assets/migrations/103.sql @@ -0,0 +1 @@ +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); diff --git a/core/src/main/assets/migrations/104.sql b/core/src/main/assets/migrations/104.sql new file mode 100644 index 0000000000..449eac0e68 --- /dev/null +++ b/core/src/main/assets/migrations/104.sql @@ -0,0 +1,42 @@ +# Rename state column to syncState + +ALTER TABLE DataValue RENAME TO DataValue_Old; +CREATE TABLE DataValue (_id INTEGER PRIMARY KEY AUTOINCREMENT, dataElement TEXT NOT NULL, period TEXT NOT NULL, organisationUnit TEXT NOT NULL, categoryOptionCombo TEXT NOT NULL, attributeOptionCombo TEXT NOT NULL, value TEXT, storedBy TEXT, created TEXT, lastUpdated TEXT, comment TEXT, followUp INTEGER, syncState TEXT, deleted INTEGER, FOREIGN KEY (dataElement) REFERENCES DataElement (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (period) REFERENCES Period (periodId), FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (dataElement, period, organisationUnit, categoryOptionCombo, attributeOptionCombo)); +INSERT INTO DataValue (_id, dataElement, period, organisationUnit, categoryOptionCombo, attributeOptionCombo, value, storedBy, created, lastUpdated, comment, followUp, syncState, deleted) SELECT _id, dataElement, period, organisationUnit, categoryOptionCombo, attributeOptionCombo, value, storedBy, created, lastUpdated, comment, followUp, state, deleted FROM DataValue_Old; +DROP TABLE DataValue_Old; + + +ALTER TABLE DataSetCompleteRegistration RENAME TO DataSetCompleteRegistration_Old; +CREATE TABLE DataSetCompleteRegistration (_id INTEGER PRIMARY KEY AUTOINCREMENT, period TEXT NOT NULL, dataSet TEXT NOT NULL, organisationUnit TEXT NOT NULL, attributeOptionCombo TEXT, date TEXT, storedBy TEXT, syncState TEXT, deleted INTEGER, FOREIGN KEY (dataSet) REFERENCES DataSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (period) REFERENCES Period (periodId) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (period, dataSet, organisationUnit, attributeOptionCombo)); +INSERT INTO DataSetCompleteRegistration(_id, period, dataSet, organisationUnit, attributeOptionCombo, date, storedBy, syncState, deleted) SELECT _id, period, dataSet, organisationUnit, attributeOptionCombo, date, storedBy, state, deleted FROM DataSetCompleteRegistration_Old; +DROP TABLE DataSetCompleteRegistration_Old; + +ALTER TABLE Relationship RENAME TO Relationship_Old; +CREATE TABLE Relationship (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, name TEXT, created TEXT, lastUpdated TEXT, relationshipType TEXT NOT NULL, syncState TEXT, deleted INTEGER, FOREIGN KEY (relationshipType) REFERENCES RelationshipType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +INSERT INTO Relationship (_id, uid, name, created, lastUpdated, relationshipType, syncState, deleted) SELECT _id, uid, name, created, lastUpdated, relationshipType, state, deleted FROM Relationship_Old; +DROP TABLE Relationship_Old; + +ALTER TABLE Note RENAME TO Note_Old; +CREATE TABLE Note (_id INTEGER PRIMARY KEY AUTOINCREMENT, noteType TEXT, event TEXT, enrollment TEXT, value TEXT, storedBy TEXT, storedDate TEXT, uid TEXT, syncState TEXT, deleted INTEGER, FOREIGN KEY (event) REFERENCES Event (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (enrollment) REFERENCES Enrollment (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (noteType, event, enrollment, value, storedBy, storedDate)); +INSERT INTO Note (_id, noteType, event, enrollment, value, storedBy, storedDate, uid, syncState, deleted) SELECT _id, noteType, event, enrollment, value, storedBy, storedDate, uid, state, deleted FROM Note_Old; +DROP TABLE Note_Old; + +ALTER TABLE FileResource RENAME TO FileResource_Old; +CREATE TABLE FileResource (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, name TEXT, created TEXT, lastUpdated TEXT, contentType TEXT, contentLength INTEGER, path TEXT, syncState TEXT); +INSERT INTO FileResource (_id, uid, name, created, lastUpdated, contentType, contentLength, path, syncState) SELECT _id, uid, name, created, lastUpdated, contentType, contentLength, path, state FROM FileResource_Old; +DROP TABLE FileResource_Old; + +ALTER TABLE TrackedEntityInstance RENAME TO TrackedEntityInstance_Old; +CREATE TABLE TrackedEntityInstance (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, organisationUnit TEXT, trackedEntityType TEXT, geometryType TEXT, geometryCoordinates TEXT, syncState TEXT, aggregatedSyncState TEXT, deleted INTEGER, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityType) REFERENCES TrackedEntityType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +INSERT INTO TrackedEntityInstance (_id, uid, created, lastUpdated, createdAtClient, lastUpdatedAtClient, organisationUnit, trackedEntityType, geometryType, geometryCoordinates, syncState, aggregatedSyncState, deleted) SELECT _id, uid, created, lastUpdated, createdAtClient, lastUpdatedAtClient, organisationUnit, trackedEntityType, geometryType, geometryCoordinates, state, state, deleted FROM TrackedEntityInstance_Old; +DROP TABLE TrackedEntityInstance_Old; + +ALTER TABLE Enrollment RENAME TO Enrollment_Old; +CREATE TABLE Enrollment (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, organisationUnit TEXT NOT NULL, program TEXT NOT NULL, enrollmentDate TEXT, incidentDate TEXT, followup INTEGER, status TEXT, trackedEntityInstance TEXT NOT NULL, syncState TEXT, aggregatedSyncState TEXT, geometryType TEXT, geometryCoordinates TEXT, deleted INTEGER, completedDate TEXT, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityInstance) REFERENCES TrackedEntityInstance (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +INSERT INTO Enrollment (_id, uid, created, lastUpdated, createdAtClient, lastUpdatedAtClient, organisationUnit, program, enrollmentDate, incidentDate, followup, status, trackedEntityInstance, syncState, aggregatedSyncState, geometryType, geometryCoordinates, deleted, completedDate) SELECT _id, uid, created, lastUpdated, createdAtClient, lastUpdatedAtClient, organisationUnit, program, enrollmentDate, incidentDate, followup, status, trackedEntityInstance, state, state, geometryType, geometryCoordinates, deleted, completedDate FROM Enrollment_Old; +DROP TABLE Enrollment_Old; + +ALTER TABLE Event RENAME TO Event_Old; +CREATE TABLE Event (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, enrollment TEXT, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, status TEXT, geometryType TEXT, geometryCoordinates TEXT, program TEXT NOT NULL, programStage TEXT NOT NULL, organisationUnit TEXT NOT NULL, eventDate TEXT, completedDate TEXT, dueDate TEXT, syncState TEXT, aggregatedSyncState TEXT, attributeOptionCombo TEXT, deleted INTEGER, assignedUser 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 (enrollment) REFERENCES Enrollment (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +INSERT INTO Event (_id, uid, enrollment, created, lastUpdated, createdAtClient, lastUpdatedAtClient, status, geometryType, geometryCoordinates, program, programStage, organisationUnit, eventDate, completedDate, dueDate, syncState, aggregatedSyncState, attributeOptionCombo, deleted, assignedUser) SELECT _id, uid, enrollment, created, lastUpdated, createdAtClient, lastUpdatedAtClient, status, geometryType, geometryCoordinates, program, programStage, organisationUnit, eventDate, completedDate, dueDate, state, state, attributeOptionCombo, deleted, assignedUser FROM Event_Old; +DROP TABLE Event_Old; \ No newline at end of file diff --git a/core/src/main/assets/migrations/105.sql b/core/src/main/assets/migrations/105.sql new file mode 100644 index 0000000000..b0386950a5 --- /dev/null +++ b/core/src/main/assets/migrations/105.sql @@ -0,0 +1 @@ +CREATE TABLE AnalyticsDhisVisualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT, scopeUid TEXT, scope TEXT, groupUid TEXT, groupName TEXT, timestamp TEXT); \ No newline at end of file diff --git a/core/src/main/assets/migrations/106.sql b/core/src/main/assets/migrations/106.sql new file mode 100644 index 0000000000..a155ad398e --- /dev/null +++ b/core/src/main/assets/migrations/106.sql @@ -0,0 +1,4 @@ +# New tables for visualization objects +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); +CREATE TABLE VisualizationCategoryDimensionLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, category TEXT NOT NULL, categoryOption TEXT NOT NULL, 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); \ No newline at end of file diff --git a/core/src/main/assets/migrations/107.sql b/core/src/main/assets/migrations/107.sql new file mode 100644 index 0000000000..f4453f5c59 --- /dev/null +++ b/core/src/main/assets/migrations/107.sql @@ -0,0 +1,6 @@ +# Add foreign key for visualization(uid) + +ALTER TABLE AnalyticsDhisVisualization RENAME TO AnalyticsDhisVisualization_Old; +CREATE TABLE AnalyticsDhisVisualization (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL, scopeUid TEXT, scope TEXT, groupUid TEXT, groupName TEXT, timestamp TEXT, FOREIGN KEY (uid) REFERENCES Visualization (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +INSERT INTO AnalyticsDhisVisualization (_id, uid, scopeUid, scope, groupUid, groupName, timestamp) SELECT _id, uid, scopeUid, scope, groupUid, groupName, timestamp FROM AnalyticsDhisVisualization_Old; +DROP TABLE IF EXISTS AnalyticsDhisVisualization_Old; \ No newline at end of file diff --git a/core/src/main/assets/migrations/108.sql b/core/src/main/assets/migrations/108.sql new file mode 100644 index 0000000000..7994958bf0 --- /dev/null +++ b/core/src/main/assets/migrations/108.sql @@ -0,0 +1,3 @@ +# Add local data store table + +CREATE TABLE LocalDataStore (_id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT NOT NULL UNIQUE, value TEXT); \ No newline at end of file diff --git a/core/src/main/assets/snapshots/101.sql b/core/src/main/assets/snapshots/108.sql similarity index 87% rename from core/src/main/assets/snapshots/101.sql rename to core/src/main/assets/snapshots/108.sql index 32247e688c..1e4982bb56 100644 --- a/core/src/main/assets/snapshots/101.sql +++ b/core/src/main/assets/snapshots/108.sql @@ -29,7 +29,7 @@ CREATE TABLE Indicator (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL CREATE TABLE DataSetIndicatorLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, dataSet TEXT NOT NULL, indicator TEXT NOT NULL, FOREIGN KEY (dataSet) REFERENCES DataSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (indicator) REFERENCES Indicator (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (dataSet, indicator)); CREATE TABLE Period (_id INTEGER PRIMARY KEY AUTOINCREMENT, periodId TEXT, periodType TEXT, startDate TEXT, endDate TEXT, UNIQUE (periodId)); CREATE TABLE ValueTypeDeviceRendering (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT, objectTable TEXT, deviceType TEXT, type TEXT, min INTEGER, max INTEGER, step INTEGER, decimalPoints INTEGER, UNIQUE (uid, deviceType)); -CREATE TABLE Note (_id INTEGER PRIMARY KEY AUTOINCREMENT, noteType TEXT, event TEXT, enrollment TEXT, value TEXT, storedBy TEXT, storedDate TEXT, uid TEXT, state TEXT, deleted INTEGER, FOREIGN KEY (event) REFERENCES Event (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (enrollment) REFERENCES Enrollment (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (noteType, event, enrollment, value, storedBy, storedDate)); +CREATE TABLE Note (_id INTEGER PRIMARY KEY AUTOINCREMENT, noteType TEXT, event TEXT, enrollment TEXT, value TEXT, storedBy TEXT, storedDate TEXT, uid TEXT, syncState TEXT, deleted INTEGER, FOREIGN KEY (event) REFERENCES Event (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (enrollment) REFERENCES Enrollment (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (noteType, event, enrollment, value, storedBy, storedDate)); CREATE TABLE Legend (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, startValue REAL, endValue REAL, color TEXT, legendSet TEXT, FOREIGN KEY (legendSet) REFERENCES LegendSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE LegendSet (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, symbolizer TEXT); CREATE TABLE ProgramIndicatorLegendSetLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, programIndicator TEXT NOT NULL, legendSet TEXT NOT NULL, FOREIGN KEY (programIndicator) REFERENCES ProgramIndicator (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (legendSet) REFERENCES LegendSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (programIndicator, legendSet)); @@ -53,7 +53,7 @@ CREATE TABLE ForeignKeyViolation (_id INTEGER PRIMARY KEY AUTOINCREMENT, fromTab CREATE TABLE D2Error (_id INTEGER PRIMARY KEY AUTOINCREMENT, resourceType TEXT, uid TEXT, url TEXT, errorComponent TEXT, errorCode TEXT, errorDescription TEXT, httpErrorCode INTEGER, created TEXT); CREATE TABLE Authority (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT); CREATE TABLE TrackedEntityTypeAttribute (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackedEntityType TEXT, trackedEntityAttribute TEXT, displayInList INTEGER, mandatory INTEGER, searchable INTEGER, sortOrder INTEGER, FOREIGN KEY (trackedEntityType) REFERENCES TrackedEntityType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityAttribute) REFERENCES TrackedEntityAttribute (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE Relationship (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, name TEXT, created TEXT, lastUpdated TEXT, relationshipType TEXT NOT NULL, state TEXT, deleted INTEGER, FOREIGN KEY (relationshipType) REFERENCES RelationshipType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE Relationship (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, name TEXT, created TEXT, lastUpdated TEXT, relationshipType TEXT NOT NULL, syncState TEXT, deleted INTEGER, FOREIGN KEY (relationshipType) REFERENCES RelationshipType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE DataElement (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, shortName TEXT, displayShortName TEXT, description TEXT, displayDescription TEXT, valueType TEXT, zeroIsSignificant INTEGER, aggregationType TEXT, formName TEXT, domainType TEXT, displayFormName TEXT, optionSet TEXT, categoryCombo TEXT NOT NULL, fieldMask TEXT, color TEXT, icon TEXT, FOREIGN KEY (optionSet) REFERENCES OptionSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryCombo) REFERENCES CategoryCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE OptionGroup (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, optionSet TEXT NOT NULL, FOREIGN KEY (optionSet) REFERENCES OptionSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE OptionGroupOptionLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, optionGroup TEXT NOT NULL, option TEXT NOT NULL, FOREIGN KEY (optionGroup) REFERENCES OptionGroup (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (option) REFERENCES Option (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (optionGroup, option)); @@ -68,14 +68,14 @@ CREATE TABLE UserOrganisationUnit (_id INTEGER PRIMARY KEY AUTOINCREMENT, user T CREATE TABLE RelationshipType (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, fromToName TEXT, toFromName TEXT, bidirectional INTEGER, accessDataWrite INTEGER ); CREATE TABLE ProgramStage (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, executionDateLabel TEXT, allowGenerateNextVisit INTEGER, validCompleteOnly INTEGER, reportDateToUse TEXT, openAfterEnrollment INTEGER, repeatable INTEGER, formType TEXT, displayGenerateEventBox INTEGER, generatedByEnrollmentDate INTEGER, autoGenerateEvent INTEGER, sortOrder INTEGER, hideDueDate INTEGER, blockEntryForm INTEGER, minDaysFromStart INTEGER, standardInterval INTEGER, program TEXT NOT NULL, periodType TEXT, accessDataWrite INTEGER, remindCompleted INTEGER, description TEXT, displayDescription TEXT, featureType TEXT, color TEXT, icon TEXT, enableUserAssignment INTEGER, dueDateLabel TEXT, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE Program (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, shortName TEXT, displayShortName TEXT, description TEXT, displayDescription TEXT, version INTEGER, onlyEnrollOnce INTEGER, enrollmentDateLabel TEXT, displayIncidentDate INTEGER, incidentDateLabel TEXT, registration INTEGER, selectEnrollmentDatesInFuture INTEGER, dataEntryMethod INTEGER, ignoreOverdueEvents INTEGER, selectIncidentDatesInFuture INTEGER, useFirstStageDuringRegistration INTEGER, displayFrontPageList INTEGER, programType TEXT, relatedProgram TEXT, trackedEntityType TEXT, categoryCombo TEXT, accessDataWrite INTEGER, expiryDays INTEGER, completeEventsExpiryDays INTEGER, expiryPeriodType TEXT, minAttributesRequiredToSearch INTEGER, maxTeiCountToReturn INTEGER, featureType TEXT, accessLevel TEXT, color TEXT, icon TEXT, FOREIGN KEY (trackedEntityType) REFERENCES TrackedEntityType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryCombo) REFERENCES CategoryCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE TrackedEntityInstance (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, organisationUnit TEXT, trackedEntityType TEXT, geometryType TEXT, geometryCoordinates TEXT, state TEXT, deleted INTEGER, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityType) REFERENCES TrackedEntityType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE Enrollment (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, organisationUnit TEXT NOT NULL, program TEXT NOT NULL, enrollmentDate TEXT, incidentDate TEXT, followup INTEGER, status TEXT, trackedEntityInstance TEXT NOT NULL, state TEXT, geometryType TEXT, geometryCoordinates TEXT, deleted INTEGER, completedDate TEXT, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityInstance) REFERENCES TrackedEntityInstance (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE Event (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, enrollment TEXT, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, status TEXT, geometryType TEXT, geometryCoordinates TEXT, program TEXT NOT NULL, programStage TEXT NOT NULL, organisationUnit TEXT NOT NULL, eventDate TEXT, completedDate TEXT, dueDate TEXT, state TEXT, attributeOptionCombo TEXT, deleted INTEGER, assignedUser 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 (enrollment) REFERENCES Enrollment (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE DataValue (_id INTEGER PRIMARY KEY AUTOINCREMENT, dataElement TEXT NOT NULL, period TEXT NOT NULL, organisationUnit TEXT NOT NULL, categoryOptionCombo TEXT NOT NULL, attributeOptionCombo TEXT NOT NULL, value TEXT, storedBy TEXT, created TEXT, lastUpdated TEXT, comment TEXT, followUp INTEGER, state TEXT, deleted INTEGER, FOREIGN KEY (dataElement) REFERENCES DataElement (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (period) REFERENCES Period (periodId), FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (dataElement, period, organisationUnit, categoryOptionCombo, attributeOptionCombo)); +CREATE TABLE TrackedEntityInstance (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, organisationUnit TEXT, trackedEntityType TEXT, geometryType TEXT, geometryCoordinates TEXT, syncState TEXT, aggregatedSyncState TEXT, deleted INTEGER, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityType) REFERENCES TrackedEntityType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE Enrollment (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, organisationUnit TEXT NOT NULL, program TEXT NOT NULL, enrollmentDate TEXT, incidentDate TEXT, followup INTEGER, status TEXT, trackedEntityInstance TEXT NOT NULL, syncState TEXT, aggregatedSyncState TEXT, geometryType TEXT, geometryCoordinates TEXT, deleted INTEGER, completedDate TEXT, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityInstance) REFERENCES TrackedEntityInstance (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE Event (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, enrollment TEXT, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, status TEXT, geometryType TEXT, geometryCoordinates TEXT, program TEXT NOT NULL, programStage TEXT NOT NULL, organisationUnit TEXT NOT NULL, eventDate TEXT, completedDate TEXT, dueDate TEXT, syncState TEXT, aggregatedSyncState TEXT, attributeOptionCombo TEXT, deleted INTEGER, assignedUser 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 (enrollment) REFERENCES Enrollment (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE DataValue (_id INTEGER PRIMARY KEY AUTOINCREMENT, dataElement TEXT NOT NULL, period TEXT NOT NULL, organisationUnit TEXT NOT NULL, categoryOptionCombo TEXT NOT NULL, attributeOptionCombo TEXT NOT NULL, value TEXT, storedBy TEXT, created TEXT, lastUpdated TEXT, comment TEXT, followUp INTEGER, syncState TEXT, deleted INTEGER, FOREIGN KEY (dataElement) REFERENCES DataElement (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (period) REFERENCES Period (periodId), FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (dataElement, period, organisationUnit, categoryOptionCombo, attributeOptionCombo)); CREATE TABLE TrackedEntityDataValue (_id INTEGER PRIMARY KEY AUTOINCREMENT, event TEXT NOT NULL, dataElement TEXT NOT NULL, storedBy TEXT, value TEXT, created TEXT, lastUpdated TEXT, providedElsewhere INTEGER, FOREIGN KEY (event) REFERENCES Event (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (dataElement) REFERENCES DataElement (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE TrackedEntityAttributeValue (_id INTEGER PRIMARY KEY AUTOINCREMENT, created TEXT, lastUpdated TEXT, value TEXT, trackedEntityAttribute TEXT NOT NULL, trackedEntityInstance TEXT NOT NULL, FOREIGN KEY (trackedEntityAttribute) REFERENCES trackedEntityAttribute (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityInstance) REFERENCES TrackedEntityInstance (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE FileResource (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, name TEXT, created TEXT, lastUpdated TEXT, contentType TEXT, contentLength INTEGER, path TEXT, state TEXT); -CREATE TABLE DataSetCompleteRegistration (_id INTEGER PRIMARY KEY AUTOINCREMENT, period TEXT NOT NULL, dataSet TEXT NOT NULL, organisationUnit TEXT NOT NULL, attributeOptionCombo TEXT, date TEXT, storedBy TEXT, state TEXT, deleted INTEGER, FOREIGN KEY (dataSet) REFERENCES DataSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (period) REFERENCES Period (periodId) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (period, dataSet, organisationUnit, attributeOptionCombo)); +CREATE TABLE FileResource (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, name TEXT, created TEXT, lastUpdated TEXT, contentType TEXT, contentLength INTEGER, path TEXT, syncState TEXT); +CREATE TABLE DataSetCompleteRegistration (_id INTEGER PRIMARY KEY AUTOINCREMENT, period TEXT NOT NULL, dataSet TEXT NOT NULL, organisationUnit TEXT NOT NULL, attributeOptionCombo TEXT, date TEXT, storedBy TEXT, syncState TEXT, deleted INTEGER, FOREIGN KEY (dataSet) REFERENCES DataSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (period) REFERENCES Period (periodId) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (period, dataSet, organisationUnit, attributeOptionCombo)); CREATE TABLE SectionGreyedFieldsLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, section TEXT NOT NULL, dataElementOperand TEXT NOT NULL, categoryOptionCombo TEXT, FOREIGN KEY (section) REFERENCES Section (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (dataElementOperand) REFERENCES DataElementOperand (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryOptionCombo) REFERENCES CategoryOptionCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (section, dataElementOperand, categoryOptionCombo)); CREATE TABLE AuthenticatedUser (_id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT NOT NULL UNIQUE, hash TEXT, FOREIGN KEY (user) REFERENCES User (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE UNIQUE INDEX event_data_element ON TrackedEntityDataValue(event, dataElement); @@ -103,10 +103,16 @@ CREATE TABLE TrackedEntityInstanceEventFilter (_id INTEGER PRIMARY KEY AUTOINCRE CREATE TABLE EventFilter (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, program TEXT NOT NULL, programStage TEXT, description TEXT, followUp INTEGER, organisationUnit TEXT, ouMode TEXT, assignedUserMode TEXT, orderProperty TEXT, displayColumnOrder TEXT, events TEXT, eventStatus TEXT, eventDate TEXT, dueDate TEXT, lastUpdatedDate TEXT, completedDate 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 (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE EventDataFilter (_id INTEGER PRIMARY KEY AUTOINCREMENT, eventFilter TEXT NOT NULL, dataItem TEXT, le TEXT, ge TEXT, gt TEXT, lt TEXT, eq TEXT, inProperty TEXT, like TEXT, dateFilter TEXT, FOREIGN KEY (eventFilter) REFERENCES EventFilter (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE ReservedValueSetting (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT UNIQUE, numberOfValuesToReserve INTEGER, FOREIGN KEY (uid) REFERENCES TrackedEntityAttribute (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE TrackerJob (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT UNIQUE); CREATE TABLE SectionIndicatorLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, section TEXT NOT NULL, indicator TEXT NOT NULL, FOREIGN KEY (section) REFERENCES Section (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (indicator) REFERENCES Indicator (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (section, indicator)); CREATE TABLE DataElementLegendSetLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, dataElement TEXT NOT NULL, legendSet TEXT NOT NULL, FOREIGN KEY (dataElement) REFERENCES DataElement (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (legendSet) REFERENCES LegendSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (dataElement, legendSet)); CREATE TABLE Attribute (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, shortName TEXT, displayShortName TEXT, description TEXT, displayDescription TEXT, valueType TEXT, uniqueProperty INTEGER, mandatory INTEGER, indicatorAttribute INTEGER, indicatorGroupAttribute INTEGER, userGroupAttribute INTEGER, dataElementAttribute INTEGER, constantAttribute INTEGER, categoryOptionAttribute INTEGER, optionSetAttribute INTEGER, sqlViewAttribute INTEGER, legendSetAttribute INTEGER, trackedEntityAttributeAttribute INTEGER, organisationUnitAttribute INTEGER, dataSetAttribute INTEGER, documentAttribute INTEGER, validationRuleGroupAttribute INTEGER, dataElementGroupAttribute INTEGER, sectionAttribute INTEGER, trackedEntityTypeAttribute INTEGER, userAttribute INTEGER, categoryOptionGroupAttribute INTEGER, programStageAttribute INTEGER, programAttribute INTEGER, categoryAttribute INTEGER, categoryOptionComboAttribute INTEGER, categoryOptionGroupSetAttribute INTEGER, validationRuleAttribute INTEGER, programIndicatorAttribute INTEGER, organisationUnitGroupAttribute INTEGER, dataElementGroupSetAttribute INTEGER, organisationUnitGroupSetAttribute INTEGER, optionAttribute INTEGER); CREATE TABLE ProgramStageAttributeValueLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, programStage TEXT NOT NULL, attribute TEXT NOT NULL, value TEXT, FOREIGN KEY (programStage) REFERENCES ProgramStage (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attribute) REFERENCES Attribute (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (programStage, attribute)); CREATE TABLE DataElementAttributeValueLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, dataElement TEXT NOT NULL, attribute TEXT NOT NULL, value TEXT, FOREIGN KEY (dataElement) REFERENCES DataElement (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attribute) REFERENCES Attribute (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (dataElement, attribute)); CREATE TABLE ProgramAttributeValueLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, program TEXT NOT NULL, attribute TEXT NOT NULL, value TEXT, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (attribute) REFERENCES Attribute (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (program, attribute)); +CREATE TABLE TrackerJobObject (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackerType TEXT NOT NULL, objectUid TEXT NOT NULL, jobUid TEXT NOT NULL, lastUpdated TEXT NOT NULL); +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, 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); +CREATE TABLE VisualizationCategoryDimensionLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, visualization TEXT NOT NULL, category TEXT NOT NULL, categoryOption TEXT NOT NULL, 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 LocalDataStore (_id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT NOT NULL UNIQUE, value TEXT); diff --git a/core/src/main/java/org/hisp/dhis/android/core/D2.java b/core/src/main/java/org/hisp/dhis/android/core/D2.java index 7aecbb32ed..b098f952d2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/D2.java +++ b/core/src/main/java/org/hisp/dhis/android/core/D2.java @@ -40,6 +40,7 @@ import org.hisp.dhis.android.core.constant.ConstantModule; import org.hisp.dhis.android.core.dataelement.DataElementModule; import org.hisp.dhis.android.core.dataset.DataSetModule; +import org.hisp.dhis.android.core.datastore.DataStoreModule; 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; @@ -61,6 +62,7 @@ import org.hisp.dhis.android.core.trackedentity.TrackedEntityModule; import org.hisp.dhis.android.core.user.UserModule; import org.hisp.dhis.android.core.validation.ValidationModule; +import org.hisp.dhis.android.core.visualization.VisualizationModule; import org.hisp.dhis.android.core.wipe.internal.WipeModule; import retrofit2.Retrofit; @@ -171,6 +173,10 @@ public LegendSetModule legendSetModule() { return this.modules.legendSet; } + public DataStoreModule dataStoreModule() { + return this.modules.dataStore; + } + public MaintenanceModule maintenanceModule() { return this.modules.maintenance; } @@ -199,6 +205,10 @@ public ValidationModule validationModule() { return modules.validation; } + public VisualizationModule visualizationModule() { + return modules.visualization; + } + public WipeModule wipeModule() { return this.d2DIComponent.wipeModule(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsModule.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsModule.kt index 491be7606d..281df57e4b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsModule.kt @@ -27,9 +27,15 @@ */ package org.hisp.dhis.android.core.analytics +import org.hisp.dhis.android.core.analytics.aggregated.AnalyticsRepository +import org.hisp.dhis.android.core.analytics.aggregated.AnalyticsVisualizationsRepository import org.hisp.dhis.android.core.analytics.linelist.EventLineListRepository interface AnalyticsModule { fun eventLineList(): EventLineListRepository + + fun analytics(): AnalyticsRepository + + fun visualizations(): AnalyticsVisualizationsRepository } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsModuleImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsModuleImpl.kt index 04603c76d3..a9a4dc15a2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsModuleImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsModuleImpl.kt @@ -29,12 +29,20 @@ package org.hisp.dhis.android.core.analytics import dagger.Reusable import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.AnalyticsRepository +import org.hisp.dhis.android.core.analytics.aggregated.AnalyticsVisualizationsRepository import org.hisp.dhis.android.core.analytics.linelist.EventLineListRepository @Reusable internal class AnalyticsModuleImpl @Inject constructor( - private val eventLineListRepository: EventLineListRepository + private val eventLineListRepository: EventLineListRepository, + private val analyticsRepository: AnalyticsRepository, + private val analyticsVisualizationsRepository: AnalyticsVisualizationsRepository ) : AnalyticsModule { override fun eventLineList(): EventLineListRepository = eventLineListRepository + + override fun analytics(): AnalyticsRepository = analyticsRepository + + override fun visualizations(): AnalyticsVisualizationsRepository = analyticsVisualizationsRepository } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsPackageDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsPackageDIModule.kt index d4026a608a..cba24254d5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsPackageDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/AnalyticsPackageDIModule.kt @@ -30,10 +30,12 @@ package org.hisp.dhis.android.core.analytics import dagger.Module import dagger.Provides import dagger.Reusable +import org.hisp.dhis.android.core.analytics.aggregated.AggregatedEntityDIModule import org.hisp.dhis.android.core.analytics.linelist.LineListEntityDIModule @Module( includes = [ + AggregatedEntityDIModule::class, LineListEntityDIModule::class ] ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AggregatedEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AggregatedEntityDIModule.kt new file mode 100644 index 0000000000..4b52b3cb30 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AggregatedEntityDIModule.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2021, 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 + +import dagger.Module +import dagger.Provides +import dagger.Reusable +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsRepositoryImpl +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsRepositoryParams +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsVisualizationsRepositoryImpl +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsVisualizationsRepositoryParams + +@Module +internal class AggregatedEntityDIModule { + + @Provides + @Reusable + fun analytics(impl: AnalyticsRepositoryImpl): AnalyticsRepository { + return impl + } + + @Provides + @Reusable + fun visualizations(impl: AnalyticsVisualizationsRepositoryImpl): AnalyticsVisualizationsRepository { + return impl + } + + @Provides + @Reusable + fun emptyAnalyticsParams(): AnalyticsRepositoryParams { + return AnalyticsRepositoryParams(listOf(), listOf()) + } + + @Provides + @Reusable + fun emptyAnalyticsVisualizationsParam(): AnalyticsVisualizationsRepositoryParams { + return AnalyticsVisualizationsRepositoryParams(null, null, null) + } +} 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 new file mode 100644 index 0000000000..1c344a5118 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsModel.kt @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2004-2021, 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 + +import org.hisp.dhis.android.core.category.Category +import org.hisp.dhis.android.core.category.CategoryOption +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.indicator.Indicator +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitLevel +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.program.ProgramIndicator + +sealed class MetadataItem(val id: String, val displayName: String) { + class DataElementItem(val item: DataElement) : MetadataItem(item.uid(), item.displayName()!!) + class DataElementOperandItem(val item: DataElementOperand) : MetadataItem(item.uid()!!, item.uid()!!) + class IndicatorItem(val item: Indicator) : MetadataItem(item.uid(), item.displayName()!!) + class ProgramIndicatorItem(val item: ProgramIndicator) : 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()!!) + + class CategoryOptionGroupSetItem(uid: String, displayName: String) : MetadataItem(uid, displayName) + + class OrganisationUnitItem(val item: OrganisationUnit) : MetadataItem(item.uid(), item.displayName()!!) + class OrganisationUnitLevelItem(val item: OrganisationUnitLevel) : MetadataItem(item.uid(), item.displayName()!!) + class OrganisationUnitGroupItem(val item: OrganisationUnitGroup) : MetadataItem(item.uid(), item.displayName()!!) + class OrganisationUnitRelativeItem( + val item: RelativeOrganisationUnit, + val organisationUnits: List + ) : MetadataItem(item.name, item.name) + + class PeriodItem(val item: Period) : MetadataItem(item.periodId()!!, item.periodId()!!) + class RelativePeriodItem(val item: RelativePeriod, val periods: List) : MetadataItem(item.name, item.name) +} + +sealed class Dimension { + object Data : Dimension() + object Period : Dimension() + object OrganisationUnit : Dimension() + data class Category(val uid: String) : Dimension() +} + +sealed class DimensionItem(val dimension: Dimension, val id: String) { + sealed class DataItem(id: String) : DimensionItem(Dimension.Data, id), AbsoluteDimensionItem { + data class DataElementItem(val uid: String) : DataItem(uid) + data class DataElementOperandItem(val dataElement: String, val categoryOptionCombo: String) : + DataItem("$dataElement.$categoryOptionCombo") + + data class IndicatorItem(val uid: String) : DataItem(uid) + data class ProgramIndicatorItem(val uid: String) : DataItem(uid) + } + + sealed class PeriodItem(id: String) : DimensionItem(Dimension.Period, id) { + data class Absolute(val periodId: String) : PeriodItem(periodId), AbsoluteDimensionItem + data class Relative(val relative: RelativePeriod) : PeriodItem(relative.name) + } + + sealed class OrganisationUnitItem(id: String) : DimensionItem(Dimension.OrganisationUnit, id) { + data class Absolute(val uid: String) : OrganisationUnitItem(uid), AbsoluteDimensionItem + data class Relative(val relative: RelativeOrganisationUnit) : OrganisationUnitItem(relative.name) + data class Level(val level: Int) : OrganisationUnitItem(level.toString()) + data class Group(val uid: String) : OrganisationUnitItem(uid) + } + + class CategoryItem( + val uid: String, + val categoryOption: String + ) : DimensionItem(Dimension.Category(uid), categoryOption), AbsoluteDimensionItem +} + +internal interface AbsoluteDimensionItem diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipService.java b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsRepository.kt similarity index 80% rename from core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipService.java rename to core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsRepository.kt index 52936c1b94..03c69807d2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipService.java +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsRepository.kt @@ -25,16 +25,18 @@ * (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.relationship.internal; -import org.hisp.dhis.android.core.imports.internal.RelationshipDeleteWebResponse; +package org.hisp.dhis.android.core.analytics.aggregated -import retrofit2.Call; -import retrofit2.http.DELETE; -import retrofit2.http.Path; +import io.reactivex.Single -interface RelationshipService { +interface AnalyticsRepository { - @DELETE("relationships/{uid}") - Call deleteRelationship(@Path("uid") String relationship); -} \ No newline at end of file + fun withDimension(dimensionItem: DimensionItem): AnalyticsRepository + + fun withFilter(dimensionItem: DimensionItem): AnalyticsRepository + + fun evaluate(): Single + + fun blockingEvaluate(): DimensionalResponse +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStore.java b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsVisualizationsRepository.kt similarity index 74% rename from core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStore.java rename to core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsVisualizationsRepository.kt index d8a6500b01..04bbf950cb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/AnalyticsVisualizationsRepository.kt @@ -26,14 +26,19 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.relationship.internal; +package org.hisp.dhis.android.core.analytics.aggregated -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStore; -import org.hisp.dhis.android.core.relationship.Relationship; -import org.hisp.dhis.android.core.relationship.RelationshipItem; +import io.reactivex.Single -import java.util.List; +interface AnalyticsVisualizationsRepository { -public interface RelationshipStore extends IdentifiableDeletableDataObjectStore { - List getRelationshipsByItem(RelationshipItem relationshipItem); -} \ No newline at end of file + fun withVisualization(visualization: String): AnalyticsVisualizationsRepository + + fun withPeriods(periods: List): AnalyticsVisualizationsRepository + + fun withOrganisationUnits(orgUnits: List): AnalyticsVisualizationsRepository + + fun evaluate(): Single + + fun blockingEvaluate(): GridAnalyticsResponse +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/DimensionalResponse.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/DimensionalResponse.kt new file mode 100644 index 0000000000..55d84fbb57 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/DimensionalResponse.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004-2021, 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 + +data class DimensionalResponse( + val metadata: Map, + val dimensions: List, + val dimensionItems: Map>, + val filters: List, + val values: List +) + +data class DimensionalValue( + val dimensions: List, + val value: String? +) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/GridAnalyticsResponse.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/GridAnalyticsResponse.kt new file mode 100644 index 0000000000..c352e24f2d --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/GridAnalyticsResponse.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2021, 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 + +data class GridAnalyticsResponse( + val metadata: Map, + val headers: GridHeader, + val dimensions: GridDimension, + val dimensionItems: Map>, + val filters: List, + val values: List> +) + +data class GridHeader( + val columns: List>, + val rows: List> +) + +data class GridHeaderItem( + val id: String, + val weight: Int +) + +data class GridDimension( + val columns: List, + val rows: List +) + +data class GridResponseValue( + val columns: List, + val rows: List, + val value: String? +) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsException.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsException.kt new file mode 100644 index 0000000000..3e58696501 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsException.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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 + +sealed class AnalyticsException(message: String) : Throwable(message) { + class InvalidArguments(message: String) : AnalyticsException(message) +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsOrganisationUnitHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsOrganisationUnitHelper.kt new file mode 100644 index 0000000000..58e732f2c9 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsOrganisationUnitHelper.kt @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2004-2021, 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 + +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.common.RelativeOrganisationUnit +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitTableInfo +import org.hisp.dhis.android.core.user.internal.UserOrganisationUnitLinkStore + +internal class AnalyticsOrganisationUnitHelper @Inject constructor( + private val userOrganisationUnitStore: UserOrganisationUnitLinkStore, + private val organisationUnitStore: IdentifiableObjectStore +) { + + fun getRelativeOrganisationUnits(relative: RelativeOrganisationUnit): List { + val orgunitUids = getRelativeOrganisationUnitUids(relative) + + return organisationUnitStore.selectByUids(orgunitUids) + } + + fun getRelativeOrganisationUnitUids(relative: RelativeOrganisationUnit): List { + val userOrganisationUnits = userOrganisationUnitStore.queryRootCaptureOrganisationUnitUids() + + return when (relative) { + RelativeOrganisationUnit.USER_ORGUNIT -> + userOrganisationUnits + RelativeOrganisationUnit.USER_ORGUNIT_CHILDREN -> + queryChildrenOrganisationUnitUids(userOrganisationUnits) + RelativeOrganisationUnit.USER_ORGUNIT_GRANDCHILDREN -> + queryGrandChildrenOrganisationUnitUids(userOrganisationUnits) + } + } + + fun getOrganisationUnitUidsByLevel(level: Int): List { + val whereClause = WhereClauseBuilder() + .appendKeyNumberValue(OrganisationUnitTableInfo.Columns.LEVEL, level) + .build() + + return organisationUnitStore.selectUidsWhere(whereClause) + } + + private fun queryChildrenOrganisationUnitUids(parentUids: List): List { + return getChildrenOrganisationUnitUids(parentUids) + } + + private fun queryGrandChildrenOrganisationUnitUids(parentUids: List): List { + return getChildrenOrganisationUnitUids(queryChildrenOrganisationUnitUids(parentUids)) + } + + private fun getChildrenOrganisationUnitUids(parentUids: List): List { + val childrenClause = WhereClauseBuilder() + .appendInKeyStringValues(OrganisationUnitTableInfo.Columns.PARENT, parentUids) + .build() + + return organisationUnitStore.selectUidsWhere(childrenClause) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsRepositoryImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsRepositoryImpl.kt new file mode 100644 index 0000000000..e71bd8fe61 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsRepositoryImpl.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004-2021, 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 + +import io.reactivex.Single +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.AnalyticsRepository +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.DimensionalResponse + +internal class AnalyticsRepositoryImpl @Inject constructor( + private val params: AnalyticsRepositoryParams, + private val analyticsService: AnalyticsService +) : AnalyticsRepository { + + override fun withDimension(dimensionItem: DimensionItem): AnalyticsRepositoryImpl { + return updateParams { params -> params.copy(dimensions = params.dimensions + dimensionItem) } + } + + override fun withFilter(dimensionItem: DimensionItem): AnalyticsRepositoryImpl { + return updateParams { params -> params.copy(filters = params.filters + dimensionItem) } + } + + override fun evaluate(): Single { + return Single.fromCallable { blockingEvaluate() } + } + + override fun blockingEvaluate(): DimensionalResponse { + return analyticsService.evaluate(params) + } + + private fun updateParams( + func: (params: AnalyticsRepositoryParams) -> AnalyticsRepositoryParams + ): AnalyticsRepositoryImpl { + return AnalyticsRepositoryImpl(func(params), analyticsService) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsRepositoryParams.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsRepositoryParams.kt new file mode 100644 index 0000000000..6d40111b59 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsRepositoryParams.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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 + +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem + +internal data class AnalyticsRepositoryParams( + val dimensions: List, + val filters: List +) 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 new file mode 100644 index 0000000000..7cc067c9d0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsService.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004-2021, 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 + +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.Dimension +import org.hisp.dhis.android.core.analytics.aggregated.DimensionalResponse + +internal class AnalyticsService @Inject constructor( + private val analyticsServiceDimensionHelper: AnalyticsServiceDimensionHelper, + private val analyticsServiceMetadataHelper: AnalyticsServiceMetadataHelper, + private val analyticsServiceEvaluatorHelper: AnalyticsServiceEvaluatorHelper +) { + + fun evaluate(params: AnalyticsRepositoryParams): DimensionalResponse { + if (params.dimensions.isEmpty()) { + throw AnalyticsException.InvalidArguments("At least one dimension must be specified") + } + + val dimensionItems = params.dimensions + params.filters + + if (dimensionItems.none { it.dimension == Dimension.Data }) { + throw AnalyticsException.InvalidArguments("At least one data dimension must be specified") + } + + val dimensions = analyticsServiceDimensionHelper.getDimensions(params) + val evaluationItems = analyticsServiceDimensionHelper.getEvaluationItems(params, dimensions) + + val metadata = analyticsServiceMetadataHelper.getMetadata(evaluationItems) + + val values = evaluationItems.map { analyticsServiceEvaluatorHelper.evaluate(it, metadata) } + + return DimensionalResponse( + metadata = metadata, + dimensions = dimensions, + dimensionItems = dimensionItems.groupBy { it.dimension }, + filters = params.filters.map { it.id }, + values = values + ) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceDimensionHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceDimensionHelper.kt new file mode 100644 index 0000000000..9085e89e2b --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceDimensionHelper.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2004-2021, 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 + +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.AbsoluteDimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.Dimension +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.period.internal.ParentPeriodGenerator + +internal class AnalyticsServiceDimensionHelper @Inject constructor( + private val periodGenerator: ParentPeriodGenerator, + private val analyticsOrganisationUnitHelper: AnalyticsOrganisationUnitHelper +) { + + fun getDimensions(params: AnalyticsRepositoryParams): List { + return params.dimensions.map { it.dimension }.distinct() + } + + fun getEvaluationItems( + params: AnalyticsRepositoryParams, + dimensions: List + ): List { + val absoluteDimensionItemList = dimensions.map { dimension -> + params.dimensions + .filter { it.dimension == dimension } + .flatMap { item -> toAbsoluteDimensionItems(item) } + } + + val dimensionCartesianProductList = absoluteDimensionItemList + .fold(listOf(listOf())) { acc, dimension -> + acc.flatMap { list -> dimension.map { element -> list + element } } + } + + return dimensionCartesianProductList.map { dimensionList -> + AnalyticsServiceEvaluationItem( + dimensionItems = dimensionList, + filters = params.filters + ) + } + } + + private fun toAbsoluteDimensionItems(item: DimensionItem): List { + return when (item) { + is DimensionItem.DataItem -> listOf(item) + is DimensionItem.PeriodItem -> + when (item) { + is DimensionItem.PeriodItem.Absolute -> listOf(item) + is DimensionItem.PeriodItem.Relative -> + periodGenerator.generateRelativePeriods(item.relative).map { period -> + DimensionItem.PeriodItem.Absolute(period.periodId()!!) + } + } + is DimensionItem.OrganisationUnitItem -> + when (item) { + is DimensionItem.OrganisationUnitItem.Absolute -> listOf(item) + is DimensionItem.OrganisationUnitItem.Relative -> + analyticsOrganisationUnitHelper.getRelativeOrganisationUnitUids(item.relative).map { + DimensionItem.OrganisationUnitItem.Absolute(it) + } + is DimensionItem.OrganisationUnitItem.Level -> + analyticsOrganisationUnitHelper.getOrganisationUnitUidsByLevel(item.level).map { + DimensionItem.OrganisationUnitItem.Absolute(it) + } + is DimensionItem.OrganisationUnitItem.Group -> TODO() + } + is DimensionItem.CategoryItem -> listOf(item) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluationItem.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluationItem.kt new file mode 100644 index 0000000000..4a28da7ae7 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluationItem.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-2021, 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 + +import org.hisp.dhis.android.core.analytics.aggregated.AbsoluteDimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem + +internal data class AnalyticsServiceEvaluationItem( + val dimensionItems: List, + val filters: List +) 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 new file mode 100644 index 0000000000..2c721cec42 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceEvaluatorHelper.kt @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2004-2021, 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 + +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.DimensionalValue +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.DataElementEvaluator + +internal class AnalyticsServiceEvaluatorHelper @Inject constructor( + private val dataElementEvaluator: DataElementEvaluator +) { + + fun evaluate( + evaluationItem: AnalyticsServiceEvaluationItem, + metadata: Map + ): DimensionalValue { + val evaluator = getEvaluator(evaluationItem) + + return DimensionalValue( + dimensions = evaluationItem.dimensionItems.map { (it as DimensionItem).id }, + value = evaluator.evaluate(evaluationItem, metadata) + ) + } + + private fun getEvaluator(evaluationItem: AnalyticsServiceEvaluationItem): AnalyticsEvaluator { + val dimensionDataItems = evaluationItem.dimensionItems.filterIsInstance() + + return when (dimensionDataItems.size) { + 0 -> getEvaluatorFromFilters(evaluationItem.filters) + 1 -> getEvaluatorFromDataDimension(dimensionDataItems.first()) + else -> + throw AnalyticsException.InvalidArguments("Invalid arguments: more than one data item as dimension.") + } + } + + private fun getEvaluatorFromFilters(filters: List): AnalyticsEvaluator { + val filterDataItems = filters.filterIsInstance() + + val allAreDataElements = filterDataItems.all { + it is DimensionItem.DataItem.DataElementItem || it is DimensionItem.DataItem.DataElementOperandItem + } + + return when { + filterDataItems.isEmpty() -> + throw AnalyticsException.InvalidArguments("Invalid arguments: no data dimension is specified.") + filterDataItems.size == 1 -> getEvaluatorFromDataDimension(filterDataItems.first()) + allAreDataElements -> dataElementEvaluator + else -> + throw AnalyticsException.InvalidArguments( + "Invalid arguments: Only a single indicator " + + "can be specified as filter." + ) + } + } + + private fun getEvaluatorFromDataDimension(item: DimensionItem.DataItem): AnalyticsEvaluator { + return when (item) { + is DimensionItem.DataItem.DataElementItem -> dataElementEvaluator + is DimensionItem.DataItem.DataElementOperandItem -> dataElementEvaluator + is DimensionItem.DataItem.ProgramIndicatorItem -> TODO() + is DimensionItem.DataItem.IndicatorItem -> 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 new file mode 100644 index 0000000000..329fa02b57 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelper.kt @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2004-2021, 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 + +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.category.Category +import org.hisp.dhis.android.core.category.CategoryOption +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.dataelement.DataElementOperand +import org.hisp.dhis.android.core.indicator.Indicator +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitLevel +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitLevelTableInfo +import org.hisp.dhis.android.core.period.internal.ParentPeriodGenerator +import org.hisp.dhis.android.core.period.internal.PeriodHelper +import org.hisp.dhis.android.core.program.ProgramIndicator + +@Suppress("LongParameterList") +internal class AnalyticsServiceMetadataHelper @Inject constructor( + private val categoryStore: IdentifiableObjectStore, + private val categoryOptionStore: IdentifiableObjectStore, + private val dataElementStore: IdentifiableObjectStore, + private val dataElementOperandStore: IdentifiableObjectStore, + private val indicatorStore: IdentifiableObjectStore, + private val organisationUnitStore: IdentifiableObjectStore, + private val organisationUnitGroupStore: IdentifiableObjectStore, + private val organisationUnitLevelStore: IdentifiableObjectStore, + private val programIndicatorStore: IdentifiableObjectStore, + private val analyticsOrganisationUnitHelper: AnalyticsOrganisationUnitHelper, + private val parentPeriodGenerator: ParentPeriodGenerator, + private val periodHelper: PeriodHelper +) { + + fun getMetadata(evaluationItems: List): Map { + val metadata: MutableMap = mutableMapOf() + + evaluationItems.forEach { evaluationItem -> + metadata += getMetadata(evaluationItem) + } + + return metadata + } + + private fun getMetadata(evaluationItem: AnalyticsServiceEvaluationItem): Map { + val metadata: MutableMap = mutableMapOf() + + (evaluationItem.dimensionItems + evaluationItem.filters) + .map { it as DimensionItem } + .forEach { item -> + if (!metadata.containsKey(item.id)) { + val metadataItems = getMetadataItems(item).map { it.id to it }.toMap() + metadata += metadataItems + } + } + + return metadata + } + + private fun getMetadataItems(item: DimensionItem): List { + return when (item) { + is DimensionItem.DataItem -> listOf( + when (item) { + is DimensionItem.DataItem.DataElementItem -> + dataElementStore.selectByUid(item.uid)!! + .let { dataElement -> MetadataItem.DataElementItem(dataElement) } + // TODO Build a meaningful name for DataElementOperand + is DimensionItem.DataItem.DataElementOperandItem -> + dataElementOperandStore.selectByUid(item.id)!! + .let { dataElementOperand -> MetadataItem.DataElementOperandItem(dataElementOperand) } + is DimensionItem.DataItem.IndicatorItem -> + indicatorStore.selectByUid(item.uid)!! + .let { indicator -> MetadataItem.IndicatorItem(indicator) } + is DimensionItem.DataItem.ProgramIndicatorItem -> + programIndicatorStore.selectByUid(item.uid)!! + .let { programIndicator -> MetadataItem.ProgramIndicatorItem(programIndicator) } + } + ) + + is DimensionItem.PeriodItem -> listOf( + when (item) { + is DimensionItem.PeriodItem.Absolute -> { + val period = periodHelper.blockingGetPeriodForPeriodId(item.periodId) + MetadataItem.PeriodItem(period) + } + is DimensionItem.PeriodItem.Relative -> { + val periods = parentPeriodGenerator.generateRelativePeriods(item.relative) + MetadataItem.RelativePeriodItem(item.relative, periods) + } + } + ) + + is DimensionItem.OrganisationUnitItem -> listOf( + when (item) { + is DimensionItem.OrganisationUnitItem.Absolute -> + organisationUnitStore.selectByUid(item.uid)!! + .let { organisationUnit -> MetadataItem.OrganisationUnitItem(organisationUnit) } + is DimensionItem.OrganisationUnitItem.Relative -> { + val orgunitUids = analyticsOrganisationUnitHelper.getRelativeOrganisationUnits(item.relative) + MetadataItem.OrganisationUnitRelativeItem(item.relative, orgunitUids) + } + is DimensionItem.OrganisationUnitItem.Level -> { + val levelClauseBuilder = WhereClauseBuilder() + .appendKeyNumberValue(OrganisationUnitLevelTableInfo.Columns.LEVEL, item.level) + .build() + organisationUnitLevelStore.selectOneWhere(levelClauseBuilder)!! + .let { level -> MetadataItem.OrganisationUnitLevelItem(level) } + } + is DimensionItem.OrganisationUnitItem.Group -> + organisationUnitGroupStore.selectByUid(item.uid)!! + .let { group -> MetadataItem.OrganisationUnitGroupItem(group) } + } + ) + + is DimensionItem.CategoryItem -> listOf( + categoryStore.selectByUid(item.uid)!! + .let { category -> MetadataItem.CategoryItem(category) }, + categoryOptionStore.selectByUid(item.categoryOption)!! + .let { categoryOption -> MetadataItem.CategoryOptionItem(categoryOption) } + ) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsRepositoryImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsRepositoryImpl.kt new file mode 100644 index 0000000000..f98ee9af58 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsRepositoryImpl.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2021, 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 + +import io.reactivex.Single +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.AnalyticsVisualizationsRepository +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.GridAnalyticsResponse + +internal class AnalyticsVisualizationsRepositoryImpl @Inject constructor( + private val params: AnalyticsVisualizationsRepositoryParams, + private val service: AnalyticsVisualizationsService +) : AnalyticsVisualizationsRepository { + + override fun withVisualization(visualization: String): AnalyticsVisualizationsRepositoryImpl { + return updateParams { params -> params.copy(visualization = visualization) } + } + + override fun withPeriods(periods: List): AnalyticsVisualizationsRepository { + return updateParams { params -> params.copy(periods = periods) } + } + + override fun withOrganisationUnits( + orgUnits: List + ): AnalyticsVisualizationsRepository { + return updateParams { params -> params.copy(organisationUnits = orgUnits) } + } + + override fun evaluate(): Single { + return Single.fromCallable { blockingEvaluate() } + } + + override fun blockingEvaluate(): GridAnalyticsResponse { + return service.evaluate(params) + } + + private fun updateParams( + func: (params: AnalyticsVisualizationsRepositoryParams) -> AnalyticsVisualizationsRepositoryParams + ): AnalyticsVisualizationsRepositoryImpl { + return AnalyticsVisualizationsRepositoryImpl(func(params), service) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsRepositoryParams.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsRepositoryParams.kt new file mode 100644 index 0000000000..95f8be516f --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsRepositoryParams.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-2021, 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 + +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem + +internal data class AnalyticsVisualizationsRepositoryParams( + val visualization: String?, + val periods: List?, + val organisationUnits: List? +) 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 new file mode 100644 index 0000000000..74c5533e5e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsService.kt @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2004-2021, 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 + +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.* +import org.hisp.dhis.android.core.visualization.Visualization +import org.hisp.dhis.android.core.visualization.VisualizationCollectionRepository + +internal class AnalyticsVisualizationsService @Inject constructor( + private val analyticsRepository: AnalyticsRepository, + private val visualizationCollectionRepository: VisualizationCollectionRepository, + private val dimensionHelper: AnalyticsVisualizationsServiceDimensionHelper +) { + + fun evaluate(params: AnalyticsVisualizationsRepositoryParams): GridAnalyticsResponse { + if (params.visualization == null) { + throw AnalyticsException.InvalidArguments("Null visualization id") + } + + val visualization = getVisualization(params.visualization) + val dimensionalResponse = getDimensionalResponse(visualization, params) + + return buildGridResponse(visualization, dimensionalResponse) + } + + private fun getVisualization(visualizationId: String): Visualization { + return visualizationCollectionRepository + .withCategoryDimensions() + .withDataDimensionItems() + .uid(visualizationId) + .blockingGet() + ?: throw AnalyticsException.InvalidArguments("Visualization $visualizationId does not exist") + } + + private fun getDimensionalResponse( + visualization: Visualization, + params: AnalyticsVisualizationsRepositoryParams + ): DimensionalResponse { + + var analyticsRepository = analyticsRepository + + val queryDimensions = + (visualization.rowDimensions() ?: emptyList()) + + (visualization.columnDimensions() ?: emptyList()) + + var queryItems = dimensionHelper.getDimensionItems(visualization, queryDimensions) + var filterItems = dimensionHelper.getDimensionItems(visualization, visualization.filterDimensions()) + + // Overwrite periods + if (!params.periods.isNullOrEmpty()) { + if (queryItems.any { it.dimension == Dimension.Period }) { + queryItems = queryItems.filterNot { it.dimension == Dimension.Period } + params.periods + } else if (filterItems.any { it.dimension == Dimension.Period }) { + filterItems = filterItems.filterNot { it.dimension == Dimension.Period } + params.periods + } else { + filterItems = filterItems + params.periods + } + } + + // Overwrite organisationUnits + if (!params.organisationUnits.isNullOrEmpty()) { + if (queryItems.any { it.dimension == Dimension.OrganisationUnit }) { + queryItems = + queryItems.filterNot { it.dimension == Dimension.OrganisationUnit } + params.organisationUnits + } else if (filterItems.any { it.dimension == Dimension.OrganisationUnit }) { + filterItems = + filterItems.filterNot { it.dimension == Dimension.OrganisationUnit } + params.organisationUnits + } else { + filterItems = filterItems + params.organisationUnits + } + } + + queryItems.forEach { analyticsRepository = analyticsRepository.withDimension(it) } + filterItems.forEach { analyticsRepository = analyticsRepository.withFilter(it) } + + return analyticsRepository.blockingEvaluate() + } + + private fun buildGridResponse( + visualization: Visualization, + dimensionalResponse: DimensionalResponse + ): GridAnalyticsResponse { + + val gridDimension = dimensionHelper.getGridDimensions(visualization) + + val rowIndexes = gridDimension.rows.map { dimensionalResponse.dimensions.indexOf(it) } + + val groupedByRow = dimensionalResponse.values.groupBy { value -> + rowIndexes.map { rowIndex -> value.dimensions[rowIndex] } + } + + val gridValues = groupedByRow.map { (rows, valueList) -> + valueList.map { value -> + GridResponseValue( + columns = value.dimensions.filterNot { rows.contains(it) }, + rows = rows, + value = value.value + ) + } + } + + val gridHeader = buildGridHeader(gridValues) + + return GridAnalyticsResponse( + metadata = dimensionalResponse.metadata, + headers = gridHeader, + dimensions = gridDimension, + dimensionItems = dimensionalResponse.dimensionItems, + filters = dimensionalResponse.filters, + values = gridValues + ) + } + + private fun buildGridHeader(gridValues: List>): GridHeader { + if (gridValues.isEmpty() || gridValues.first().isEmpty()) { + return GridHeader(emptyList(), emptyList()) + } + + val sampleRow = gridValues.first() + + val columnCount = sampleRow.first().columns.size + val columns = (0 until columnCount).map { columnIdx -> + val columnValues = sampleRow.map { it.columns[columnIdx] } + getGridHeaderItems(columnValues) + } + + val sampleColumn = gridValues.map { it.first() } + + val rowCount = sampleColumn.first().rows.size + val rows = (0 until rowCount).map { rowIdx -> + val rowValues = sampleColumn.map { it.rows[rowIdx] } + getGridHeaderItems(rowValues) + } + + return GridHeader(columns, rows) + } + + private fun getGridHeaderItems(values: List): List { + val groups = mutableListOf() + values.forEach { + val last = groups.lastOrNull() + if (last?.id == it) { + groups[groups.lastIndex] = last.copy(weight = last.weight + 1) + } else { + groups.add(GridHeaderItem(it, 1)) + } + } + return groups + } +} 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 new file mode 100644 index 0000000000..83be5c1891 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelper.kt @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2004-2021, 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 + +import javax.inject.Inject +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.GridDimension +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.category.Category +import org.hisp.dhis.android.core.common.RelativeOrganisationUnit.* +import org.hisp.dhis.android.core.visualization.DataDimensionItemType +import org.hisp.dhis.android.core.visualization.Visualization + +internal class AnalyticsVisualizationsServiceDimensionHelper @Inject constructor( + private val categoryStore: IdentifiableObjectStore +) { + private val dataDimension = "dx" + private val orgUnitDimension = "ou" + private val periodDimension = "pe" + private val uidRegex = "^\\w{11}\$".toRegex() + private val dataElementOperandRegex = "^(\\w{11})\\.(\\w{11})\$".toRegex() + + fun getDimensionItems(visualization: Visualization, dimensions: List?): List { + return dimensions?.map { dimension -> + when (dimension) { + dataDimension -> extractDataDimensionItems(visualization) + orgUnitDimension -> extractOrgunitDimensionItems(visualization) + periodDimension -> extractPeriodDimensionItems(visualization) + else -> { + if (uidRegex.matches(dimension)) { + extractUidDimensionItems(visualization, dimension) + } else { + emptyList() + } + } + } + }?.flatten() ?: emptyList() + } + + private fun extractDataDimensionItems(visualization: Visualization): List { + return visualization.dataDimensionItems()?.mapNotNull { item -> + when (item.dataDimensionItemType()) { + DataDimensionItemType.INDICATOR -> + item.indicator()?.let { DimensionItem.DataItem.IndicatorItem(it.uid()) } + DataDimensionItemType.DATA_ELEMENT -> + item.dataElement()?.let { DimensionItem.DataItem.DataElementItem(it.uid()) } + DataDimensionItemType.DATA_ELEMENT_OPERAND -> + item.dataElementOperand()?.let { + val (dataElement, coc) = dataElementOperandRegex.find(it.uid())!!.destructured + DimensionItem.DataItem.DataElementOperandItem(dataElement, coc) + } + DataDimensionItemType.PROGRAM_INDICATOR -> + item.programIndicator()?.let { DimensionItem.DataItem.ProgramIndicatorItem(it.uid()) } + 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 { + DimensionItem.OrganisationUnitItem.Level(it) + } ?: emptyList() + + 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) + } + + return absolute + levels + relative + } + + private fun extractPeriodDimensionItems(visualization: Visualization): List { + val absolute = visualization.periods()?.map { + DimensionItem.PeriodItem.Absolute(it.uid()) + } ?: emptyList() + + val relative = visualization.relativePeriods() + ?.filter { it.value == true } + ?.map { + DimensionItem.PeriodItem.Relative(it.key) + } ?: emptyList() + + return absolute + relative + } + + private fun extractUidDimensionItems(visualization: Visualization, uid: String): List { + val category = categoryStore.selectByUid(uid) + + return if (category != null) { + val categoryDimension = visualization.categoryDimensions()?.find { it.category()?.uid() == uid } + + categoryDimension?.categoryOptions()?.map { + DimensionItem.CategoryItem(uid, it.uid()) + } ?: emptyList() + } else { + emptyList() + } + } + + fun getGridDimensions(visualization: Visualization): GridDimension { + val columns = mapDimensions(visualization.columnDimensions()) + val rows = mapDimensions(visualization.rowDimensions()) + + return GridDimension(columns, rows) + } + + private fun mapDimensions(dimensionStrs: List?): List { + return dimensionStrs?.mapNotNull { + when (it) { + dataDimension -> Dimension.Data + periodDimension -> Dimension.Period + orgUnitDimension -> Dimension.OrganisationUnit + else -> { + if (uidRegex.matches(it)) { + extractUidDimension(it) + } else { + null + } + } + } + } ?: emptyList() + } + + private fun extractUidDimension(uid: String): Dimension? { + val category = categoryStore.selectByUid(uid) + + return if (category != null) { + Dimension.Category(uid) + } else { + null + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsEvaluator.kt new file mode 100644 index 0000000000..24a01cb058 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsEvaluator.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004-2021, 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.aggregated.MetadataItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem + +internal interface AnalyticsEvaluator { + + fun evaluate( + evaluationItem: AnalyticsServiceEvaluationItem, + metadata: Map + ): String? +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsEvaluatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsEvaluatorHelper.kt new file mode 100644 index 0000000000..b01eb27907 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsEvaluatorHelper.kt @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2004-2021, 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.arch.helpers.DateUtils +import org.hisp.dhis.android.core.category.CategoryCategoryComboLinkTableInfo as cToCcInfo +import org.hisp.dhis.android.core.category.CategoryOptionComboCategoryOptionLinkTableInfo as cocToCoInfo +import org.hisp.dhis.android.core.category.CategoryOptionComboTableInfo as cocInfo +import org.hisp.dhis.android.core.common.AggregationType +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitTableInfo +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.PeriodTableInfo + +internal object AnalyticsEvaluatorHelper { + + const val Sum = "SUM" + const val Avg = "AVG" + const val Count = "COUNT" + const val Max = "MAX" + const val Min = "MIN" + + fun getInPeriodClause(period: Period): String { + return "SELECT ${PeriodTableInfo.Columns.PERIOD_ID} " + + "FROM ${PeriodTableInfo.TABLE_INFO.name()} " + + "WHERE ${getPeriodWhereClause(period)}" + } + + fun getInPeriodsClause(periods: List): String { + return "SELECT ${PeriodTableInfo.Columns.PERIOD_ID} " + + "FROM ${PeriodTableInfo.TABLE_INFO.name()} " + + "WHERE ${periods.joinToString(" OR ") { "(${getPeriodWhereClause(it)})" }}" + } + + private fun getPeriodWhereClause(period: Period): String { + return "${PeriodTableInfo.Columns.START_DATE} >= '${DateUtils.DATE_FORMAT.format(period.startDate()!!)}' " + + "AND " + + "${PeriodTableInfo.Columns.END_DATE} <= '${DateUtils.DATE_FORMAT.format(period.endDate()!!)}'" + } + + fun getOrgunitClause(orgunitUid: String): String { + return "SELECT ${OrganisationUnitTableInfo.Columns.UID} " + + "FROM ${OrganisationUnitTableInfo.TABLE_INFO.name()} " + + "WHERE " + + "${OrganisationUnitTableInfo.Columns.PATH} LIKE '%$orgunitUid%'" + } + + fun getLevelOrgunitClause(level: Int): String { + return "SELECT ${OrganisationUnitTableInfo.Columns.UID} " + + "FROM ${OrganisationUnitTableInfo.TABLE_INFO.name()} " + + "WHERE " + + "${OrganisationUnitTableInfo.Columns.LEVEL} = $level" + } + + fun getOrgunitListClause(orgunitUids: List): String { + return "SELECT ${OrganisationUnitTableInfo.Columns.UID} " + + "FROM ${OrganisationUnitTableInfo.TABLE_INFO.name()} " + + "WHERE " + + orgunitUids.joinToString(" OR ") { "${OrganisationUnitTableInfo.Columns.PATH} LIKE '%$it%'" } + } + + fun getCategoryOptionClause(categoryUid: String, categoryOptionUid: String): String { + return "SELECT ${cocInfo.Columns.UID} " + + "FROM ${cocInfo.TABLE_INFO.name()} " + + "WHERE " + + "${cocInfo.Columns.UID} IN " + + "(" + + "SELECT ${cocToCoInfo.Columns.CATEGORY_OPTION_COMBO} " + + "FROM ${cocToCoInfo.TABLE_INFO.name()} " + + "WHERE ${cocToCoInfo.Columns.CATEGORY_OPTION} = '$categoryOptionUid'" + + ") " + + "AND " + + "${cocInfo.Columns.CATEGORY_COMBO} IN " + + "(" + + "SELECT ${cToCcInfo.Columns.CATEGORY_COMBO} " + + "FROM ${cToCcInfo.TABLE_INFO.name()} " + + "WHERE ${cToCcInfo.Columns.CATEGORY} = '$categoryUid'" + + ") " + } + + fun getDataElementAggregator(aggregationType: String?): String { + return when (aggregationType?.let { AggregationType.valueOf(it) } ?: AggregationType.SUM) { + AggregationType.SUM -> Sum + AggregationType.AVERAGE -> Avg + AggregationType.COUNT -> Count + AggregationType.MAX -> Max + AggregationType.MIN -> Min + else -> Sum + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluator.kt new file mode 100644 index 0000000000..246ef4a756 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementEvaluator.kt @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2004-2021, 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 javax.inject.Inject +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.MetadataItem +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsException +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem +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.common.AggregationType +import org.hisp.dhis.android.core.datavalue.DataValueTableInfo + +internal class DataElementEvaluator @Inject constructor( + private val databaseAdapter: DatabaseAdapter +) : AnalyticsEvaluator { + + override fun evaluate( + evaluationItem: AnalyticsServiceEvaluationItem, + metadata: Map + ): String? { + val items = (evaluationItem.dimensionItems + evaluationItem.filters) + .map { it as DimensionItem } + .groupBy { it.dimension } + + val whereClause = + items.entries.fold(WhereClauseBuilder()) { builder, entry -> + when (entry.key) { + is Dimension.Data -> appendDataWhereClause(entry.value, builder) + is Dimension.Period -> appendPeriodWhereClause(entry.value, builder, metadata) + is Dimension.OrganisationUnit -> appendOrgunitWhereClause(entry.value, builder, metadata) + is Dimension.Category -> appendCategoryWhereClause(entry.value, builder) + } + } + .appendKeyNumberValue(DataValueTableInfo.Columns.DELETED, 0) + .build() + + val aggregator = getAggregator(evaluationItem, metadata) + + val sqlQuery = + "SELECT $aggregator(${DataValueTableInfo.Columns.VALUE}) " + + "FROM ${DataValueTableInfo.TABLE_INFO.name()} " + + "WHERE $whereClause" + + return databaseAdapter.rawQuery(sqlQuery)?.use { c -> + c.moveToFirst() + c.getString(0) + } + } + + private fun appendDataWhereClause( + items: List, + builder: WhereClauseBuilder + ): WhereClauseBuilder { + val innerClause = items.map { it as DimensionItem.DataItem } + .foldRight(WhereClauseBuilder()) { item, innerBuilder -> + when (item) { + is DimensionItem.DataItem.DataElementItem -> + innerBuilder.appendOrKeyStringValue(DataValueTableInfo.Columns.DATA_ELEMENT, item.uid) + is DimensionItem.DataItem.DataElementOperandItem -> { + val operandClause = WhereClauseBuilder() + .appendKeyStringValue(DataValueTableInfo.Columns.DATA_ELEMENT, item.dataElement) + .appendKeyStringValue( + DataValueTableInfo.Columns.CATEGORY_OPTION_COMBO, + item.categoryOptionCombo + ) + .build() + innerBuilder.appendOrComplexQuery(operandClause) + } + else -> + throw AnalyticsException.InvalidArguments( + "Invalid arguments: unexpected " + + "dataItem ${item.javaClass.name} in DataElement Evaluator." + ) + } + }.build() + + return builder.appendComplexQuery(innerClause) + } + + private fun appendPeriodWhereClause( + items: List, + builder: WhereClauseBuilder, + metadata: Map + ): WhereClauseBuilder { + val innerClause = items.map { it as DimensionItem.PeriodItem } + .foldRight(WhereClauseBuilder()) { item, innerBuilder -> + when (item) { + is DimensionItem.PeriodItem.Absolute -> { + val periodItem = metadata[item.periodId] as MetadataItem.PeriodItem + innerBuilder.appendOrInSubQuery( + DataValueTableInfo.Columns.PERIOD, + AnalyticsEvaluatorHelper.getInPeriodClause(periodItem.item) + ) + } + is DimensionItem.PeriodItem.Relative -> { + val relativeItem = metadata[item.id] as MetadataItem.RelativePeriodItem + innerBuilder.appendOrInSubQuery( + DataValueTableInfo.Columns.PERIOD, + AnalyticsEvaluatorHelper.getInPeriodsClause(relativeItem.periods) + ) + } + } + }.build() + + return builder.appendComplexQuery(innerClause) + } + + private fun appendOrgunitWhereClause( + items: List, + builder: WhereClauseBuilder, + metadata: Map + ): WhereClauseBuilder { + val innerClause = items.map { it as DimensionItem.OrganisationUnitItem } + .foldRight(WhereClauseBuilder()) { item, innerBuilder -> + when (item) { + is DimensionItem.OrganisationUnitItem.Absolute -> + innerBuilder.appendOrInSubQuery( + DataValueTableInfo.Columns.ORGANISATION_UNIT, + AnalyticsEvaluatorHelper.getOrgunitClause(item.uid) + ) + is DimensionItem.OrganisationUnitItem.Level -> + innerBuilder.appendOrInSubQuery( + DataValueTableInfo.Columns.ORGANISATION_UNIT, + AnalyticsEvaluatorHelper.getLevelOrgunitClause(item.level) + ) + is DimensionItem.OrganisationUnitItem.Relative -> { + val metadataItem = metadata[item.id] as MetadataItem.OrganisationUnitRelativeItem + val orgunits = metadataItem.organisationUnits.map { it.uid() } + + innerBuilder.appendOrInSubQuery( + DataValueTableInfo.Columns.ORGANISATION_UNIT, + AnalyticsEvaluatorHelper.getOrgunitListClause(orgunits) + ) + } + is DimensionItem.OrganisationUnitItem.Group -> TODO() + } + }.build() + + return builder.appendComplexQuery(innerClause) + } + + private fun appendCategoryWhereClause( + items: List, + builder: WhereClauseBuilder + ): WhereClauseBuilder { + val innerClause = items.map { it as DimensionItem.CategoryItem } + .foldRight(WhereClauseBuilder()) { item, innerBuilder -> + innerBuilder.appendOrInSubQuery( + DataValueTableInfo.Columns.CATEGORY_OPTION_COMBO, + AnalyticsEvaluatorHelper.getCategoryOptionClause(item.uid, item.categoryOption) + ) + }.build() + + return builder.appendComplexQuery(innerClause) + } + + @Suppress("ThrowsCount") + private fun getAggregator( + evaluationItem: AnalyticsServiceEvaluationItem, + metadata: Map + ): String { + val dimensionDataItem = evaluationItem.dimensionItems.filterIsInstance() + + val dataItemList = when (dimensionDataItem.size) { + 0 -> evaluationItem.filters.filterIsInstance() + 1 -> dimensionDataItem + else -> + throw AnalyticsException.InvalidArguments("Invalid arguments: more than one data item as dimension.") + } + + return when (dataItemList.size) { + 0 -> throw AnalyticsException.InvalidArguments("Invalid arguments: no data dimension is specified.") + 1 -> { + val item = metadata[dataItemList.first().id] + val aggregationType = when (item) { + is MetadataItem.DataElementItem -> item.item.aggregationType() + is MetadataItem.DataElementOperandItem -> + metadata[item.item.dataElement()?.uid()]?.let { + (it as MetadataItem.DataElementItem).item.aggregationType() + } + else -> throw AnalyticsException.InvalidArguments( + "Invalid arguments: dimension is not " + + "dataelement or operand." + ) + } + AnalyticsEvaluatorHelper.getDataElementAggregator(aggregationType) + } + else -> AnalyticsEvaluatorHelper.getDataElementAggregator(AggregationType.SUM.name) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/AggregatedSamples.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/AggregatedSamples.kt new file mode 100644 index 0000000000..def5a0ac45 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/AggregatedSamples.kt @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2004-2021, 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.mock + +import org.hisp.dhis.android.core.category.Category +import org.hisp.dhis.android.core.category.CategoryOption +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.indicator.Indicator +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.program.ProgramIndicator + +object AggregatedSamples { + val dataElement1 = DataElement.builder() + .uid("fbfJHSPpUQD") + .displayName("ANC 1st visit") + .build() + + val dataElement2 = DataElement.builder() + .uid("cYeuwXTCPkU") + .displayName("ANC 2nd visit") + .build() + + val programIndicator1 = ProgramIndicator.builder() + .uid("p2Zxg0wcPQ3") + .displayName("BCG doses") + .build() + + val indicator1 = Indicator.builder() + .uid("Uvn6LCg7dVU") + .displayName("ANC 1 Coverage") + .build() + + val cc1 = Category.builder() + .uid("fMZEcRHuamy") + .displayName("Fixed / Outreach") + .build() + + val co11 = CategoryOption.builder() + .uid("pq2XI5kz2BY") + .displayName("Fixed") + .build() + + val co12 = CategoryOption.builder() + .uid("PT59n8BQbqM") + .displayName("Outreach") + .build() + + val cc2 = Category.builder() + .uid("cX5k9anHEHd") + .displayName("Gender") + .build() + + val co21 = CategoryOption.builder() + .uid("jRbMi0aBjYn") + .displayName("Male") + .build() + + val period1 = Period.builder() + .periodId("202103") + .build() + + val period2 = Period.builder() + .periodId("202104") + .build() + + val orgunit1 = OrganisationUnit.builder() + .uid("DiszpKrYNg8") + .displayName("Ngelehun") + .build() + + val orgunit2 = OrganisationUnit.builder() + .uid("g8upMTyEZGZ") + .displayName("Njandama") + .build() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/DimensionalResponseSamples.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/DimensionalResponseSamples.kt new file mode 100644 index 0000000000..85956ffef9 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/DimensionalResponseSamples.kt @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2004-2021, 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.mock + +import org.hisp.dhis.android.core.analytics.aggregated.* +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.cc1 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.co11 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.co12 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.dataElement1 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.dataElement2 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.orgunit1 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.orgunit2 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.period1 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.period2 + +object DimensionalResponseSamples { + val sample1 = DimensionalResponse( + metadata = mapOf( + dataElement1.uid() to MetadataItem.DataElementItem(dataElement1), + dataElement2.uid() to MetadataItem.DataElementItem(dataElement2), + co11.uid() to MetadataItem.CategoryOptionItem(co11), + co12.uid() to MetadataItem.CategoryOptionItem(co12), + cc1.uid() to MetadataItem.CategoryItem(cc1), + period1.periodId()!! to MetadataItem.PeriodItem(period1), + period2.periodId()!! to MetadataItem.PeriodItem(period2), + orgunit1.uid() to MetadataItem.OrganisationUnitItem(orgunit1), + orgunit2.uid() to MetadataItem.OrganisationUnitItem(orgunit2) + ), + dimensions = listOf(Dimension.Data, Dimension.Category(cc1.uid()), Dimension.Period), + dimensionItems = mapOf( + Dimension.Data to listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()), + DimensionItem.DataItem.DataElementItem(dataElement2.uid()) + ), + Dimension.Category(cc1.uid()) to listOf( + DimensionItem.CategoryItem(cc1.uid(), co11.uid()), + DimensionItem.CategoryItem(cc1.uid(), co12.uid()) + ), + Dimension.Period to listOf( + DimensionItem.PeriodItem.Absolute(period1.periodId()!!), + DimensionItem.PeriodItem.Absolute(period2.periodId()!!) + ), + Dimension.OrganisationUnit to listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunit1.uid()), + DimensionItem.OrganisationUnitItem.Absolute(orgunit2.uid()) + ) + ), + filters = listOf(orgunit1.uid(), orgunit2.uid()), + values = listOf( + DimensionalValue( + listOf(dataElement1.uid(), co11.uid(), period1.periodId()!!), + "34.5" + ), + DimensionalValue( + listOf(dataElement1.uid(), co12.uid(), period1.periodId()!!), + "10.0" + ), + DimensionalValue( + listOf(dataElement2.uid(), co11.uid(), period1.periodId()!!), + "13" + ), + DimensionalValue( + listOf(dataElement2.uid(), co12.uid(), period1.periodId()!!), + "15" + ), + DimensionalValue( + listOf(dataElement1.uid(), co11.uid(), period2.periodId()!!), + "34.5" + ), + DimensionalValue( + listOf(dataElement1.uid(), co12.uid(), period2.periodId()!!), + "10.0" + ), + DimensionalValue( + listOf(dataElement2.uid(), co11.uid(), period2.periodId()!!), + "13" + ), + DimensionalValue( + listOf(dataElement2.uid(), co12.uid(), period2.periodId()!!), + "15" + ) + ) + ) +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/GridAnalyticsResponseSamples.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/GridAnalyticsResponseSamples.kt new file mode 100644 index 0000000000..037b4e1a34 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/GridAnalyticsResponseSamples.kt @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2004-2021, 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.mock + +import org.hisp.dhis.android.core.analytics.aggregated.* +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.cc1 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.co11 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.co12 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.dataElement1 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.dataElement2 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.orgunit1 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.orgunit2 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.period1 +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples.period2 + +object GridAnalyticsResponseSamples { + val sample1 = GridAnalyticsResponse( + metadata = mapOf( + dataElement1.uid() to MetadataItem.DataElementItem(dataElement1), + dataElement2.uid() to MetadataItem.DataElementItem(dataElement2), + co11.uid() to MetadataItem.CategoryOptionItem(co11), + co12.uid() to MetadataItem.CategoryOptionItem(co12), + cc1.uid() to MetadataItem.CategoryItem(cc1), + period1.periodId()!! to MetadataItem.PeriodItem(period1), + period2.periodId()!! to MetadataItem.PeriodItem(period2), + orgunit1.uid() to MetadataItem.OrganisationUnitItem(orgunit1), + orgunit2.uid() to MetadataItem.OrganisationUnitItem(orgunit2) + ), + headers = GridHeader( + columns = listOf( + listOf( + GridHeaderItem(dataElement1.uid(), 2), + GridHeaderItem(dataElement2.uid(), 2) + ), + listOf( + GridHeaderItem(co11.uid(), 1), + GridHeaderItem(co12.uid(), 1), + GridHeaderItem(co11.uid(), 1), + GridHeaderItem(co12.uid(), 1) + ) + ), + rows = listOf( + listOf( + GridHeaderItem(period1.periodId()!!, 1), + GridHeaderItem(period2.periodId()!!, 1) + ) + ) + ), + dimensions = GridDimension( + columns = listOf(Dimension.Data, Dimension.Category(cc1.uid())), + rows = listOf(Dimension.Period) + ), + dimensionItems = mapOf( + Dimension.Data to listOf( + DimensionItem.DataItem.DataElementItem(dataElement1.uid()), + DimensionItem.DataItem.DataElementItem(dataElement2.uid()) + ), + Dimension.Category(cc1.uid()) to listOf( + DimensionItem.CategoryItem(cc1.uid(), co11.uid()), + DimensionItem.CategoryItem(cc1.uid(), co12.uid()) + ), + Dimension.Period to listOf( + DimensionItem.PeriodItem.Absolute(period1.periodId()!!), + DimensionItem.PeriodItem.Absolute(period2.periodId()!!) + ), + Dimension.OrganisationUnit to listOf( + DimensionItem.OrganisationUnitItem.Absolute(orgunit1.uid()), + DimensionItem.OrganisationUnitItem.Absolute(orgunit2.uid()) + ) + ), + filters = listOf(orgunit1.uid(), orgunit2.uid()), + values = listOf( + listOf( + GridResponseValue( + listOf(dataElement1.uid(), co11.uid()), + listOf(period1.periodId()!!), + "34.5" + ), + GridResponseValue( + listOf(dataElement1.uid(), co12.uid()), + listOf(period1.periodId()!!), + "10.0" + ), + GridResponseValue( + listOf(dataElement2.uid(), co11.uid()), + listOf(period1.periodId()!!), + "13" + ), + GridResponseValue( + listOf(dataElement2.uid(), co12.uid()), + listOf(period1.periodId()!!), + "15" + ) + ), + listOf( + GridResponseValue( + listOf(dataElement1.uid(), co11.uid()), + listOf(period2.periodId()!!), + "34.5" + ), + GridResponseValue( + listOf(dataElement1.uid(), co12.uid()), + listOf(period2.periodId()!!), + "10.0" + ), + GridResponseValue( + listOf(dataElement2.uid(), co11.uid()), + listOf(period2.periodId()!!), + "13" + ), + GridResponseValue( + listOf(dataElement2.uid(), co12.uid()), + listOf(period2.periodId()!!), + "15" + ) + ) + ) + ) +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/MockAnalyticsRepository.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/MockAnalyticsRepository.kt new file mode 100644 index 0000000000..ad81a9c467 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/MockAnalyticsRepository.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004-2021, 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.mock + +import dagger.Reusable +import io.reactivex.Single +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.AnalyticsRepository +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.DimensionalResponse + +@Reusable +class MockAnalyticsRepository @Inject constructor() : AnalyticsRepository { + + override fun withDimension(dimensionItem: DimensionItem): AnalyticsRepository = this + + override fun withFilter(dimensionItem: DimensionItem): AnalyticsRepository = this + + override fun evaluate(): Single { + return Single.fromCallable { blockingEvaluate() } + } + + override fun blockingEvaluate(): DimensionalResponse = DimensionalResponseSamples.sample1 +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/MockAnalyticsVisualizationsRepository.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/MockAnalyticsVisualizationsRepository.kt new file mode 100644 index 0000000000..251455b5d1 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/MockAnalyticsVisualizationsRepository.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004-2021, 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.mock + +import dagger.Reusable +import io.reactivex.Single +import javax.inject.Inject +import org.hisp.dhis.android.core.analytics.aggregated.AnalyticsVisualizationsRepository +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.GridAnalyticsResponse + +@Reusable +class MockAnalyticsVisualizationsRepository @Inject constructor() : AnalyticsVisualizationsRepository { + + override fun withVisualization(visualization: String): AnalyticsVisualizationsRepository { + return MockAnalyticsVisualizationsRepository() + } + + override fun withPeriods(periods: List): AnalyticsVisualizationsRepository { + return MockAnalyticsVisualizationsRepository() + } + + override fun withOrganisationUnits( + orgUnits: List + ): AnalyticsVisualizationsRepository { + return MockAnalyticsVisualizationsRepository() + } + + override fun evaluate(): Single { + return Single.fromCallable { blockingEvaluate() } + } + + override fun blockingEvaluate(): GridAnalyticsResponse = GridAnalyticsResponseSamples.sample1 +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.java b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.java index dbaa02c925..5c372c5657 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.java @@ -59,6 +59,8 @@ import org.hisp.dhis.android.core.dataelement.internal.DataElementPackageDIModule; import org.hisp.dhis.android.core.dataset.DataSet; import org.hisp.dhis.android.core.dataset.internal.DataSetPackageDIModule; +import org.hisp.dhis.android.core.datastore.internal.DataStorePackageDIModule; +import org.hisp.dhis.android.core.datavalue.internal.DataValueConflictDIModule; import org.hisp.dhis.android.core.datavalue.internal.DataValuePackageDIModule; import org.hisp.dhis.android.core.domain.aggregated.data.internal.AggregatedDataPackageDIModule; import org.hisp.dhis.android.core.domain.aggregated.internal.AggregatedModuleImpl; @@ -88,10 +90,12 @@ import org.hisp.dhis.android.core.systeminfo.internal.SystemInfoPackageDIModule; import org.hisp.dhis.android.core.trackedentity.TrackedEntityPackageDIModule; import org.hisp.dhis.android.core.trackedentity.TrackedEntityType; -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstancePostPayloadGenerator; +import org.hisp.dhis.android.core.trackedentity.internal.OldTrackerImporterPayloadGenerator; +import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstancePostPayloadGenerator29; import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterPackageDIModule; import org.hisp.dhis.android.core.user.internal.UserPackageDIModule; import org.hisp.dhis.android.core.validation.internal.ValidationPackageDIModule; +import org.hisp.dhis.android.core.visualization.internal.VisualizationPackageDIModule; import org.hisp.dhis.android.core.wipe.internal.WipeDIModule; import org.hisp.dhis.android.core.wipe.internal.WipeModule; @@ -128,6 +132,7 @@ ImportPackageDIModule.class, IndicatorPackageDIModule.class, LegendPackageDIModule.class, + DataStorePackageDIModule.class, MaintenancePackageDIModule.class, MaintenancePackageDIModule.class, NotePackageDIModule.class, @@ -143,7 +148,9 @@ TrackerImporterPackageDIModule.class, SmsDIModule.class, UserPackageDIModule.class, - ValidationPackageDIModule.class} + ValidationPackageDIModule.class, + VisualizationPackageDIModule.class, + DataValueConflictDIModule.class} ) public interface D2DIComponent { @@ -177,7 +184,9 @@ public interface D2DIComponent { @VisibleForTesting Handler trackedEntityTypeHandler(); @VisibleForTesting - TrackedEntityInstancePostPayloadGenerator trackedEntityInstancePostPayloadGenerator(); + TrackedEntityInstancePostPayloadGenerator29 trackedEntityInstancePostPayloadGenerator(); + @VisibleForTesting + OldTrackerImporterPayloadGenerator oldTrackerImporterPayloadGenerator(); @VisibleForTesting EventPostPayloadGenerator eventPostPayloadGenerator(); @VisibleForTesting @@ -207,6 +216,7 @@ interface Builder { Builder importPackageDIModule(ImportPackageDIModule importPackageDIModule); Builder indicatorPackageDIModule(IndicatorPackageDIModule indicatorPackageDIModule); Builder legendPackageDIModule(LegendPackageDIModule legendPackageDIModule); + Builder dataStorePackageDIModule(DataStorePackageDIModule dataStorePackageDIModule); Builder maintenancePackageDIModule(MaintenancePackageDIModule maintenancePackageDIModule); Builder optionPackageDIModule(OptionPackageDIModule optionPackageDIModule); Builder organisationUnitPackageDIModule(OrganisationUnitPackageDIModule organisationUnitPackageDIModule); @@ -220,6 +230,8 @@ interface Builder { Builder trackerImporterPackageDIModule(TrackerImporterPackageDIModule trackerImporterPackageDIModule); Builder userPackageDIModule(UserPackageDIModule userPackageDIModule); Builder validationPackageDIModule(ValidationPackageDIModule validationPackageDIModule); + Builder visualizationPackageDIModule(VisualizationPackageDIModule visualizationPackageDIModule); + Builder dataValueConflictDIModule(DataValueConflictDIModule dataValueConflictDIModule); D2DIComponent build(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2InternalModules.java b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2InternalModules.java index 364c852736..da102afd99 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2InternalModules.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2InternalModules.java @@ -30,6 +30,7 @@ import org.hisp.dhis.android.core.category.internal.CategoryInternalModule; import org.hisp.dhis.android.core.user.internal.UserInternalModule; +import org.hisp.dhis.android.core.visualization.internal.VisualizationInternalModule; import javax.inject.Inject; @@ -38,12 +39,15 @@ @Reusable public final class D2InternalModules { public final CategoryInternalModule category; + public final VisualizationInternalModule visualization; public final UserInternalModule user; @Inject public D2InternalModules(CategoryInternalModule category, + VisualizationInternalModule visualization, UserInternalModule user) { this.category = category; + this.visualization = visualization; this.user = user; } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.java b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.java index 41aea5fef5..f8def3b1b6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.java @@ -33,6 +33,7 @@ import org.hisp.dhis.android.core.constant.ConstantModule; import org.hisp.dhis.android.core.dataelement.DataElementModule; import org.hisp.dhis.android.core.dataset.DataSetModule; +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; @@ -53,6 +54,7 @@ import org.hisp.dhis.android.core.trackedentity.TrackedEntityModule; import org.hisp.dhis.android.core.user.UserModule; import org.hisp.dhis.android.core.validation.ValidationModule; +import org.hisp.dhis.android.core.visualization.VisualizationModule; import javax.inject.Inject; @@ -75,6 +77,7 @@ public final class D2Modules { public final ImportModule importModule; public final IndicatorModule indicator; public final LegendSetModule legendSet; + public final DataStoreModule dataStore; public final MaintenanceModule maintenance; public final NoteModule note; public final ProgramModule program; @@ -86,6 +89,7 @@ public final class D2Modules { public final TrackedEntityModule trackedEntity; public final UserModule user; public final ValidationModule validation; + public final VisualizationModule visualization; public final SmsModule sms; @Inject @@ -102,6 +106,7 @@ public D2Modules(AnalyticsModule analytics, ImportModule importModule, IndicatorModule indicator, LegendSetModule legendSet, + DataStoreModule dataStore, MaintenanceModule maintenance, NoteModule note, ProgramModule program, @@ -113,6 +118,7 @@ public D2Modules(AnalyticsModule analytics, TrackedEntityModule trackedEntity, UserModule user, ValidationModule validation, + VisualizationModule visualization, SmsModule sms) { this.analytics = analytics; this.category = category; @@ -127,6 +133,7 @@ public D2Modules(AnalyticsModule analytics, this.importModule = importModule; this.indicator = indicator; this.legendSet = legendSet; + this.dataStore = dataStore; this.maintenance = maintenance; this.note = note; this.program = program; @@ -138,6 +145,7 @@ public D2Modules(AnalyticsModule analytics, this.trackedEntity = trackedEntity; this.user = user; this.validation = validation; + this.visualization = visualization; this.sms = sms; } } \ No newline at end of file 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 a7adfeb070..d9bd3a6106 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 @@ -36,7 +36,7 @@ class BaseDatabaseOpenHelper { - static final int VERSION = 101; + static final int VERSION = 108; private final AssetManager assetManager; private final int targetVersion; diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/DbDateColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/DbDateColumnAdapter.java index 4201fe8115..9aae781cfc 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/DbDateColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/DbDateColumnAdapter.java @@ -33,7 +33,7 @@ import com.gabrielittner.auto.value.cursor.ColumnTypeAdapter; -import org.hisp.dhis.android.core.common.BaseIdentifiableObject; +import org.hisp.dhis.android.core.arch.helpers.DateUtils; import java.text.ParseException; import java.util.Date; @@ -49,7 +49,7 @@ public Date fromCursor(Cursor cursor, String columnName) { Date date = null; if (sourceDate != null) { try { - date = BaseIdentifiableObject.DATE_FORMAT.parse(sourceDate); + date = DateUtils.DATE_FORMAT.parse(sourceDate); } catch (ParseException parseException) { // wrap checked exception into unchecked throw new RuntimeException(parseException); @@ -62,7 +62,7 @@ public Date fromCursor(Cursor cursor, String columnName) { @Override public void toContentValues(ContentValues contentValues, String columnName, Date date) { if (date != null) { - contentValues.put(columnName, BaseIdentifiableObject.DATE_FORMAT.format(date)); + contentValues.put(columnName, DateUtils.DATE_FORMAT.format(date)); } } } 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/adapters/custom/internal/IntegerListColumnAdapter.kt new file mode 100644 index 0000000000..8880c8d478 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/IntegerListColumnAdapter.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-2021, 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 org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory + +internal class IntegerListColumnAdapter : JSONObjectListColumnAdapter() { + override fun getObjectClass(): Class> { + return ArrayList().javaClass + } + + override fun serialize(o: List?): String? = IntegerListColumnAdapter.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/ObjectWithUidListColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/ObjectWithUidListColumnAdapter.kt new file mode 100644 index 0000000000..a233e40c79 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/ObjectWithUidListColumnAdapter.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2004-2021, 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 new file mode 100644 index 0000000000..02881ddc2c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/custom/internal/RelativePeriodsColumnAdapter.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2004-2021, 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/helpers/internal/EnumHelper.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.java similarity index 79% rename from core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/EnumHelper.java rename to core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.java index 3854c2a2a3..d341797b1a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/EnumHelper.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/AnalyticsDhisVisualizationScopeColumnAdapter.java @@ -26,21 +26,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.arch.helpers.internal; +package org.hisp.dhis.android.core.arch.db.adapters.enums.internal; -import java.util.ArrayList; -import java.util.List; +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationScope; -public final class EnumHelper { - - public static List asStringList(Enum... enums) { - List enumsStr = new ArrayList<>(enums.length); - for (Enum e: enums) { - enumsStr.add(e.name()); - } - return enumsStr; - } - - private EnumHelper() { +public class AnalyticsDhisVisualizationScopeColumnAdapter extends EnumColumnAdapter { + @Override + protected Class getEnumClass() { + return AnalyticsDhisVisualizationScope.class; } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DataDimensionItemTypeColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DataDimensionItemTypeColumnAdapter.kt new file mode 100644 index 0000000000..db0f93899e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DataDimensionItemTypeColumnAdapter.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.enums.internal + +import org.hisp.dhis.android.core.visualization.DataDimensionItemType + +class DataDimensionItemTypeColumnAdapter : EnumColumnAdapter() { + override fun getEnumClass(): Class { + return DataDimensionItemType::class.java + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DigitGroupSeparatorColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DigitGroupSeparatorColumnAdapter.kt new file mode 100644 index 0000000000..8a284a38c3 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DigitGroupSeparatorColumnAdapter.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.enums.internal + +import org.hisp.dhis.android.core.visualization.DigitGroupSeparator + +class DigitGroupSeparatorColumnAdapter : EnumColumnAdapter() { + override fun getEnumClass(): Class { + return DigitGroupSeparator::class.java + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DisplayDensityColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DisplayDensityColumnAdapter.kt new file mode 100644 index 0000000000..defcefe168 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/DisplayDensityColumnAdapter.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.enums.internal + +import org.hisp.dhis.android.core.visualization.DisplayDensity + +class DisplayDensityColumnAdapter : EnumColumnAdapter() { + override fun getEnumClass(): Class { + return DisplayDensity::class.java + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/HideEmptyItemStrategyColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/HideEmptyItemStrategyColumnAdapter.kt new file mode 100644 index 0000000000..f48303e1e0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/HideEmptyItemStrategyColumnAdapter.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.enums.internal + +import org.hisp.dhis.android.core.visualization.HideEmptyItemStrategy + +class HideEmptyItemStrategyColumnAdapter : EnumColumnAdapter() { + override fun getEnumClass(): Class { + return HideEmptyItemStrategy::class.java + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerImporterObjectTypeColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerImporterObjectTypeColumnAdapter.kt new file mode 100644 index 0000000000..dcb71df9cd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/TrackerImporterObjectTypeColumnAdapter.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.enums.internal + +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType + +internal class TrackerImporterObjectTypeColumnAdapter : EnumColumnAdapter() { + override fun getEnumClass(): Class { + return TrackerImporterObjectType::class.java + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/VisualizationTypeColumnAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/VisualizationTypeColumnAdapter.kt new file mode 100644 index 0000000000..58b7e16b5a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/enums/internal/VisualizationTypeColumnAdapter.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.enums.internal + +import org.hisp.dhis.android.core.visualization.VisualizationType + +class VisualizationTypeColumnAdapter : EnumColumnAdapter() { + override fun getEnumClass(): Class { + return VisualizationType::class.java + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreCategoryDimensionListColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreCategoryDimensionListColumnAdapter.java new file mode 100644 index 0000000000..cc9ea128ac --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreCategoryDimensionListColumnAdapter.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-2021, 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.ignore.internal; + +import org.hisp.dhis.android.core.visualization.CategoryDimension; + +import java.util.List; + +public final class IgnoreCategoryDimensionListColumnAdapter + extends IgnoreColumnAdapter> { +} \ 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/IgnoreDataDimensionItemListColumnAdapter.java new file mode 100644 index 0000000000..58c76cfcbe --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreDataDimensionItemListColumnAdapter.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-2021, 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.ignore.internal; + +import org.hisp.dhis.android.core.visualization.DataDimensionItem; + +import java.util.List; + +public final class IgnoreDataDimensionItemListColumnAdapter + extends IgnoreColumnAdapter> { +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreNewTrackerImporterRelationshipItemAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreNewTrackerImporterRelationshipItemAdapter.java new file mode 100644 index 0000000000..4559d186fb --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreNewTrackerImporterRelationshipItemAdapter.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.ignore.internal; + +import org.hisp.dhis.android.core.relationship.NewTrackerImporterRelationshipItem; + +public final class IgnoreNewTrackerImporterRelationshipItemAdapter + extends IgnoreColumnAdapter { +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/AggregationType.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreStateColumnAdapter.java similarity index 88% rename from core/src/main/java/org/hisp/dhis/android/core/common/AggregationType.java rename to core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreStateColumnAdapter.java index 924764f62c..87e345038d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/common/AggregationType.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreStateColumnAdapter.java @@ -26,9 +26,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.common; +package org.hisp.dhis.android.core.arch.db.adapters.ignore.internal; -public enum AggregationType { - SUM, AVERAGE, AVERAGE_SUM_ORG_UNIT, LAST, LAST_AVERAGE_ORG_UNIT, COUNT, - STDDEV, VARIANCE, MIN, MAX, NONE, CUSTOM, DEFAULT +import org.hisp.dhis.android.core.common.State; + +public final class IgnoreStateColumnAdapter extends IgnoreColumnAdapter { } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/querybuilders/internal/WhereClauseBuilder.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/querybuilders/internal/WhereClauseBuilder.java index 43289031bd..eea24e094a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/querybuilders/internal/WhereClauseBuilder.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/querybuilders/internal/WhereClauseBuilder.java @@ -129,6 +129,10 @@ public WhereClauseBuilder appendInSubQuery(String column, String subQuery) { return appendKeyValue(column, subQuery, AND, IN, PARENTHESES_END); } + public WhereClauseBuilder appendOrInSubQuery(String column, String subQuery) { + return appendKeyValue(column, subQuery, OR, IN, PARENTHESES_END); + } + public WhereClauseBuilder appendIsNullValue(String column) { return appendKeyValue(column, "", AND, IS_NULL, ""); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/DeletableStoreWithState.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/DeletableStoreWithState.kt index 1d3b861041..e443ff6ef2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/DeletableStoreWithState.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/DeletableStoreWithState.kt @@ -31,6 +31,7 @@ import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction import org.hisp.dhis.android.core.common.State internal interface DeletableStoreWithState : StoreWithState { - fun setStateOrDelete(uid: String, state: State): HandleAction + fun setSyncStateOrDelete(uid: String, state: State): HandleAction fun setDeleted(uid: String): Int + fun selectSyncStateWhere(where: String): List } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableDataObjectStoreImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableDataObjectStoreImpl.kt index ca0458eda7..c9af00eaf3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableDataObjectStoreImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableDataObjectStoreImpl.kt @@ -55,9 +55,9 @@ internal open class IdentifiableDataObjectStoreImpl( if (setStateStatement == null) { val whereUid = " WHERE " + IdentifiableColumns.UID + " =?" val setState = "UPDATE " + tableName + " SET " + - DataColumns.STATE + " =?" + whereUid + DataColumns.SYNC_STATE + " =?" + whereUid setStateStatement = databaseAdapter.compileStatement(setState) - selectStateQuery = "SELECT " + DataColumns.STATE + " FROM " + tableName + whereUid + selectStateQuery = "SELECT " + DataColumns.SYNC_STATE + " FROM " + tableName + whereUid existsQuery = "SELECT 1 FROM $tableName$whereUid" } } @@ -75,7 +75,7 @@ internal open class IdentifiableDataObjectStoreImpl( } } - override fun setState(uid: String, state: State): Int { + override fun setSyncState(uid: String, state: State): Int { compileStatements() setStateStatement!!.bind(1, state) @@ -86,16 +86,16 @@ internal open class IdentifiableDataObjectStoreImpl( return updatedRow } - override fun setState(uids: List, state: State): Int { + override fun setSyncState(uids: List, state: State): Int { val updates = ContentValues() - updates.put(DataColumns.STATE, state.toString()) + updates.put(DataColumns.SYNC_STATE, state.toString()) val whereClause = WhereClauseBuilder() .appendInKeyStringValues(IdentifiableColumns.UID, uids) .build() return databaseAdapter.update(tableName, updates, whereClause, null) } - override fun getState(uid: String): State? { + override fun getSyncState(uid: String): State? { compileStatements() val cursor = databaseAdapter.rawQuery(selectStateQuery, uid) var state: State? = null diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableDeletableDataObjectStoreImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableDeletableDataObjectStoreImpl.kt index c93c4f8fbf..e5d6fef7f8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableDeletableDataObjectStoreImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableDeletableDataObjectStoreImpl.kt @@ -57,8 +57,8 @@ internal open class IdentifiableDeletableDataObjectStoreImpl( if (setStateIfUploadingStatement == null) { val whereUid = " WHERE " + IdentifiableColumns.UID + " =?" val setState = "UPDATE " + tableName + " SET " + - DataColumns.STATE + " =?" + whereUid - val setStateIfUploading = setState + " AND " + DataColumns.STATE + EQ + "'" + State.UPLOADING + "'" + DataColumns.SYNC_STATE + " =?" + whereUid + val setStateIfUploading = setState + " AND " + DataColumns.SYNC_STATE + EQ + "'" + State.UPLOADING + "'" setStateIfUploadingStatement = databaseAdapter.compileStatement(setStateIfUploading) val setDeleted = "UPDATE " + tableName + " SET " + DeletableDataColumns.DELETED + " = 1" + whereUid @@ -81,13 +81,13 @@ internal open class IdentifiableDeletableDataObjectStoreImpl( } } - override fun setStateOrDelete(uid: String, state: State): HandleAction { + override fun setSyncStateOrDelete(uid: String, state: State): HandleAction { var deleted = false if (state == State.SYNCED) { val whereClause = WhereClauseBuilder() .appendKeyStringValue(IdentifiableColumns.UID, uid) .appendKeyNumberValue(DeletableDataColumns.DELETED, 1) - .appendKeyStringValue(DataColumns.STATE, State.UPLOADING) + .appendKeyStringValue(DataColumns.SYNC_STATE, State.UPLOADING) .build() deleted = deleteWhere(whereClause) } @@ -116,4 +116,9 @@ internal open class IdentifiableDeletableDataObjectStoreImpl( setDeletedStatement!!.clearBindings() return updatedRow } + + override fun selectSyncStateWhere(where: String): List { + val statesStr = selectStringColumnsWhereClause(DataColumns.SYNC_STATE, where) + return statesStr.map { State.valueOf(it) } + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableObjectStore.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableObjectStore.kt index 50fea8e8ee..b1204aeceb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableObjectStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableObjectStore.kt @@ -53,4 +53,7 @@ internal interface IdentifiableObjectStore : ObjectS @Throws(RuntimeException::class) fun selectByUid(uid: String): O? + + @Throws(RuntimeException::class) + fun selectByUids(uid: List): List } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableObjectStoreImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableObjectStoreImpl.kt index 9ce506345d..405b938905 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableObjectStoreImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/IdentifiableObjectStoreImpl.kt @@ -35,6 +35,7 @@ import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapp import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper import org.hisp.dhis.android.core.common.CoreObject +import org.hisp.dhis.android.core.common.IdentifiableColumns.UID import org.hisp.dhis.android.core.common.ObjectWithUidInterface @Suppress("TooManyFunctions") @@ -145,6 +146,11 @@ internal open class IdentifiableObjectStoreImpl( return mapObjectFromCursor(cursor) } + @Throws(RuntimeException::class) + override fun selectByUids(uid: List): List { + return selectWhere("$UID IN (${uid.joinToString(",") { "'$it'" }})") + } + private fun mapObjectFromCursor(cursor: Cursor): O? { cursor.use { c -> if (c.count > 0) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/ObjectStore.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/ObjectStore.kt index a0186fb696..a52331d770 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/ObjectStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/ObjectStore.kt @@ -27,6 +27,8 @@ */ package org.hisp.dhis.android.core.arch.db.stores.internal +import android.content.ContentValues + internal interface ObjectStore : ReadableStore { @Throws(RuntimeException::class) fun selectStringColumnsWhereClause(column: String, clause: String): List @@ -40,6 +42,8 @@ internal interface ObjectStore : ReadableStore { fun deleteById(o: O): Boolean fun deleteWhere(clause: String): Boolean + fun updateWhere(updates: ContentValues, whereClause: String): Int + @Throws(RuntimeException::class) fun deleteWhereIfExists(whereClause: String) val isReady: Boolean diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/ObjectStoreImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/ObjectStoreImpl.kt index fe093e8a7c..6f1dc4f166 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/ObjectStoreImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/ObjectStoreImpl.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.arch.db.stores.internal +import android.content.ContentValues import android.database.Cursor import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilder @@ -125,6 +126,10 @@ internal open class ObjectStoreImpl internal constructor( return databaseAdapter.delete(builder.tableName, clause, null) > 0 } + override fun updateWhere(updates: ContentValues, whereClause: String): Int { + return databaseAdapter.update(builder.tableName, updates, whereClause, null) + } + @Throws(RuntimeException::class) @Suppress("TooGenericExceptionCaught") override fun deleteWhereIfExists(whereClause: String) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/StoreUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/StoreUtils.kt index 2ec705e899..6baaea5a80 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/StoreUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/StoreUtils.kt @@ -41,7 +41,7 @@ internal object StoreUtils { * @return the state from the ImportStatus */ @JvmStatic - fun getState(importStatus: ImportStatus): State { + fun getSyncState(importStatus: ImportStatus): State { return when (importStatus) { ImportStatus.ERROR -> State.ERROR ImportStatus.SUCCESS -> State.SYNCED diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/StoreWithState.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/StoreWithState.kt index 664dc871d8..1e71b169da 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/StoreWithState.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/stores/internal/StoreWithState.kt @@ -30,8 +30,8 @@ package org.hisp.dhis.android.core.arch.db.stores.internal import org.hisp.dhis.android.core.common.State internal interface StoreWithState { - fun setState(uid: String, state: State): Int - fun setState(uids: List, state: State): Int - fun getState(uid: String): State? + fun setSyncState(uid: String, state: State): Int + fun setSyncState(uids: List, state: State): Int + fun getSyncState(uid: String): State? fun exists(uid: String): Boolean } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt index 9b744af6ab..94653dea1e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/handlers/internal/IdentifiableDataHandlerImpl.kt @@ -137,7 +137,7 @@ internal abstract class IdentifiableDataHandlerImpl( private fun relationshipTransformer(): (O) -> O { return { o: O -> - val currentState = store.getState(o.uid()) + val currentState = store.getSyncState(o.uid()) if (currentState == State.RELATIONSHIP || currentState == null) { addRelationshipState(o) } else { @@ -161,7 +161,7 @@ internal abstract class IdentifiableDataHandlerImpl( relationships ) { relationship: Relationship -> relationship.toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) .deleted(false) .build()!! } @@ -254,7 +254,8 @@ internal abstract class IdentifiableDataHandlerImpl( if (storedObjectUids.isNotEmpty()) { val syncedObjectUidsWhereClause2 = WhereClauseBuilder() .appendInKeyStringValues(IdentifiableColumns.UID, storedObjectUids) - .appendInKeyStringValues(DataColumns.STATE, states) + .appendInKeyStringValues(DataColumns.SYNC_STATE, states) + .appendInKeyStringValues(DataColumns.AGGREGATED_SYNC_STATE, states) .build() return store.selectUidsWhere(syncedObjectUidsWhereClause2) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/Result.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/Result.kt new file mode 100644 index 0000000000..1763371869 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/Result.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004-2021, 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.helpers + +sealed class Result { + data class Success(val value: S) : Result() + data class Failure(val failure: F) : Result() + + val succeeded: Boolean get() = this is Success && value != null + + fun fold(onSuccess: (S) -> Unit, onFailure: (F) -> Unit) { + when (this) { + is Success -> onSuccess(value) + is Failure -> onFailure(failure) + } + } + + override fun toString(): String { + return when (this) { + is Success -> "Success[value=$value]" + is Failure -> "Failure[failure=$failure]" + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/DataStateHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/DataStateHelper.kt index 0f1d56b38e..0187a9d02c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/DataStateHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/DataStateHelper.kt @@ -40,6 +40,6 @@ object DataStateHelper { @JvmStatic fun forcedOrOwn(o: DataObject, forcedState: State?): State { - return forcedState ?: o.state() + return forcedState ?: o.syncState() } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/EnumHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/EnumHelper.kt new file mode 100644 index 0000000000..e91d04860c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/internal/EnumHelper.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004-2021, 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.helpers.internal + +internal object EnumHelper { + + @JvmStatic + fun asStringList(enums: Collection>): List { + return enums.map { it.name } + } + + @JvmStatic + fun asStringList(vararg enums: Enum<*>): List { + return asStringList(enums.toList()) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadWriteWithUidDataObjectRepositoryImpl.java b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadWriteWithUidDataObjectRepositoryImpl.java index 09a3629d6f..0ad285333d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadWriteWithUidDataObjectRepositoryImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadWriteWithUidDataObjectRepositoryImpl.java @@ -64,7 +64,7 @@ public ReadWriteWithUidDataObjectRepositoryImpl(IdentifiableDeletableDataObjectS } /** - * Removes the object in scope in an asynchronous way. Field {@link DataObject#state()} is marked as + * Removes the object in scope in an asynchronous way. Field {@link DataObject#syncState()} is marked as * {@link State#TO_UPDATE} and {@link DeletableDataObject#deleted()} as true. In the next upload, it will be deleted * in the server. It returns a {@code Completable} that completes as soon as the object is deleted in the database. * The {@code Completable} fails if the object doesn't exist. @@ -76,7 +76,7 @@ public Completable delete() { } /** - * Removes the object in scope in a synchronous way. Field {@link DataObject#state()} is marked as + * Removes the object in scope in a synchronous way. Field {@link DataObject#syncState()} is marked as * {@link State#TO_UPDATE} and {@link DeletableDataObject#deleted()} as true. In the next upload, it will be deleted * in the server. It blocks the thread and finishes as soon as the object is deleted in the database. * It throws an exception if the object doesn't exist. @@ -97,18 +97,18 @@ public void blockingDelete() throws D2Error { .errorDescription("Tried to delete non existing object") .build(); } else { - if (object.state() == State.TO_POST) { + if (object.syncState() == State.TO_POST) { store.delete(object.uid()); } else { store.setDeleted(object.uid()); - store.setState(object.uid(), State.TO_UPDATE); + store.setSyncState(object.uid(), State.TO_UPDATE); propagateState(object); } } } /** - * Removes the object in scope in a synchronous way. Field {@link DataObject#state()} is marked as + * Removes the object in scope in a synchronous way. Field {@link DataObject#syncState()} is marked as * {@link State#TO_POST} and {@link DeletableDataObject#deleted()} as true. Unlike {@link #delete()}, * it doesn't throw an exception if the object doesn't exist. * It returns a {@code Completable} that completes as soon as the object is deleted in the database. @@ -120,7 +120,7 @@ public Completable deleteIfExist() { } /** - * Removes the object in scope in an asynchronous way. Field {@link DataObject#state()} is marked as + * Removes the object in scope in an asynchronous way. Field {@link DataObject#syncState()} is marked as * {@link State#TO_POST} and {@link DeletableDataObject#deleted()} as true. * Unlike {@link #blockingDelete()}, it doesn't throw an exception if the object doesn't exist. * It blocks the thread and finishes as soon as the object is deleted in the database. diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadWriteWithValueObjectRepositoryImpl.java b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadWriteWithValueObjectRepositoryImpl.java index 547b1da833..987694851e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadWriteWithValueObjectRepositoryImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadWriteWithValueObjectRepositoryImpl.java @@ -60,7 +60,7 @@ public ReadWriteWithValueObjectRepositoryImpl(ObjectWithoutUidStore store, /** * Removes the object in scope in an asynchronous way. It removes the value in the database and propagates - * the changes to modify the {@link DataObject#state()} of the parent, so it's updated in the server in + * the changes to modify the {@link DataObject#syncState()} of the parent, so it's updated in the server in * the next upload. * It returns a {@code Completable} that completes as soon as the object is deleted in the database. * The {@code Completable} fails if the object doesn't exist. @@ -73,7 +73,7 @@ public Completable delete() { /** * Removes the object in scope in a synchronous way. It removes the value in the database and propagates - * the changes to modify the {@link DataObject#state()} of the parent, so it's updated in the server in + * the changes to modify the {@link DataObject#syncState()} of the parent, so it's updated in the server in * the next upload. * It blocks the thread and finishes as soon as the object is deleted in the database. * @@ -89,7 +89,7 @@ public void blockingDelete() throws D2Error { /** * Removes the object in scope in an asynchronous way. It removes the value in the database and propagates - * the changes to modify the {@link DataObject#state()} of the parent, so it's updated in the server in + * the changes to modify the {@link DataObject#syncState()} of the parent, so it's updated in the server in * the next upload. * It returns a {@code Completable} that completes as soon as the object is deleted in the database. * Unlike {@link #delete()}, it doesn't throw an exception if the object doesn't exist. @@ -103,7 +103,7 @@ public Completable deleteIfExist() { /** * Removes the object in scope in a synchronous way. It removes the value in the database and propagates - * the changes to modify the {@link DataObject#state()} of the parent, so it's updated in the server in + * the changes to modify the {@link DataObject#syncState()} of the parent, so it's updated in the server in * the next upload. * Unlike {@link #blockingDelete()}, it doesn't throw an exception if the object doesn't exist. * It blocks the thread and finishes as soon as the object is deleted in the database. diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryComboLinkStore.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryComboLinkStore.kt similarity index 69% rename from core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryComboLinkStore.java rename to core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryComboLinkStore.kt index 6f82b69244..bd2ce04a1d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryComboLinkStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryComboLinkStore.kt @@ -25,30 +25,31 @@ * (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.category.internal -package org.hisp.dhis.android.core.category.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.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.linkStore +import org.hisp.dhis.android.core.category.CategoryCategoryComboLink +import org.hisp.dhis.android.core.category.CategoryCategoryComboLinkTableInfo +@Suppress("MagicNumber") +internal object CategoryCategoryComboLinkStore { -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.internal.LinkStore; -import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory; -import org.hisp.dhis.android.core.category.CategoryCategoryComboLink; -import org.hisp.dhis.android.core.category.CategoryCategoryComboLinkTableInfo; - -final class CategoryCategoryComboLinkStore { - - private static final StatementBinder BINDER = (o, w) -> { - w.bind(1, o.category()); - w.bind(2, o.categoryCombo()); - w.bind(3, o.sortOrder()); - }; - - private CategoryCategoryComboLinkStore() {} + private val BINDER = StatementBinder { o: CategoryCategoryComboLink, w: StatementWrapper -> + w.bind(1, o.category()) + w.bind(2, o.categoryCombo()) + w.bind(3, o.sortOrder()) + } - public static LinkStore create(DatabaseAdapter databaseAdapter) { - return StoreFactory.linkStore(databaseAdapter, CategoryCategoryComboLinkTableInfo.TABLE_INFO, - CategoryCategoryComboLinkTableInfo.Columns.CATEGORY_COMBO, BINDER, - CategoryCategoryComboLink::create); + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): LinkStore { + return linkStore( + databaseAdapter, CategoryCategoryComboLinkTableInfo.TABLE_INFO, + CategoryCategoryComboLinkTableInfo.Columns.CATEGORY_COMBO, BINDER + ) { cursor: Cursor -> CategoryCategoryComboLink.create(cursor) } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryOptionLinkStore.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryOptionLinkStore.kt similarity index 68% rename from core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryOptionLinkStore.java rename to core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryOptionLinkStore.kt index 690df1b9e6..4e52ddd066 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryOptionLinkStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryCategoryOptionLinkStore.kt @@ -25,31 +25,33 @@ * (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.category.internal -package org.hisp.dhis.android.core.category.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.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.linkStore +import org.hisp.dhis.android.core.category.CategoryCategoryOptionLink +import org.hisp.dhis.android.core.category.CategoryCategoryOptionLinkTableInfo -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.internal.LinkStore; -import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory; -import org.hisp.dhis.android.core.category.CategoryCategoryOptionLink; -import org.hisp.dhis.android.core.category.CategoryCategoryOptionLinkTableInfo; +@Suppress("MagicNumber") +internal object CategoryCategoryOptionLinkStore { -final class CategoryCategoryOptionLinkStore { - - private static final StatementBinder BINDER = (o, w) -> { - w.bind(1, o.category()); - w.bind(2, o.categoryOption()); - w.bind(3, o.sortOrder()); - }; - - private CategoryCategoryOptionLinkStore() {} + private val BINDER = StatementBinder { o: CategoryCategoryOptionLink, w: StatementWrapper -> + w.bind(1, o.category()) + w.bind(2, o.categoryOption()) + w.bind(3, o.sortOrder()) + } - public static LinkStore create(DatabaseAdapter databaseAdapter) { - return StoreFactory.linkStore(databaseAdapter, - CategoryCategoryOptionLinkTableInfo.TABLE_INFO, - CategoryCategoryOptionLinkTableInfo.Columns.CATEGORY, - BINDER, - CategoryCategoryOptionLink::create); + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): LinkStore { + return linkStore( + databaseAdapter, + CategoryCategoryOptionLinkTableInfo.TABLE_INFO, + CategoryCategoryOptionLinkTableInfo.Columns.CATEGORY, + BINDER + ) { cursor: Cursor -> CategoryCategoryOptionLink.create(cursor) } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboStore.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboStore.kt similarity index 69% rename from core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboStore.java rename to core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboStore.kt index 6f88f7c6b7..b3de0c50a2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboStore.kt @@ -25,35 +25,34 @@ * (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.category.internal; - - -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; -import org.hisp.dhis.android.core.category.CategoryCombo; -import org.hisp.dhis.android.core.category.CategoryComboTableInfo; - -import androidx.annotation.NonNull; - -public final class CategoryComboStore { - - private CategoryComboStore() {} - - private static StatementBinder BINDER = new IdentifiableStatementBinder() { - @Override - public void bindToStatement(@NonNull CategoryCombo o, @NonNull StatementWrapper w) { - super.bindToStatement(o, w); - w.bind(7, o.isDefault()); +package org.hisp.dhis.android.core.category.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.category.CategoryCombo +import org.hisp.dhis.android.core.category.CategoryComboTableInfo + +@Suppress("MagicNumber") +internal object CategoryComboStore { + + private val BINDER: StatementBinder = object : IdentifiableStatementBinder() { + override fun bindToStatement(o: CategoryCombo, w: StatementWrapper) { + super.bindToStatement(o, w) + w.bind(7, o.isDefault) } - }; + } - public static IdentifiableObjectStore create(DatabaseAdapter databaseAdapter) { - return StoreFactory.objectWithUidStore(databaseAdapter, CategoryComboTableInfo.TABLE_INFO, BINDER, - CategoryCombo::create); + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + return objectWithUidStore( + databaseAdapter, + CategoryComboTableInfo.TABLE_INFO, + BINDER + ) { cursor: Cursor -> CategoryCombo.create(cursor) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboCategoryOptionLinkStore.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboCategoryOptionLinkStore.kt similarity index 67% rename from core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboCategoryOptionLinkStore.java rename to core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboCategoryOptionLinkStore.kt index 5293c0f323..341b619a1a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboCategoryOptionLinkStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboCategoryOptionLinkStore.kt @@ -25,30 +25,30 @@ * (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.category.internal -package org.hisp.dhis.android.core.category.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.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.linkStore +import org.hisp.dhis.android.core.category.CategoryOptionComboCategoryOptionLink +import org.hisp.dhis.android.core.category.CategoryOptionComboCategoryOptionLinkTableInfo +@Suppress("MagicNumber") +internal object CategoryOptionComboCategoryOptionLinkStore { -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.internal.LinkStore; -import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory; -import org.hisp.dhis.android.core.category.CategoryOptionComboCategoryOptionLink; -import org.hisp.dhis.android.core.category.CategoryOptionComboCategoryOptionLinkTableInfo; - -final class CategoryOptionComboCategoryOptionLinkStore { - - private static final StatementBinder BINDER = (o, w) -> { - w.bind(1, o.categoryOptionCombo()); - w.bind(2, o.categoryOption()); - }; - - private CategoryOptionComboCategoryOptionLinkStore() {} + private val BINDER = StatementBinder { o: CategoryOptionComboCategoryOptionLink, w: StatementWrapper -> + w.bind(1, o.categoryOptionCombo()) + w.bind(2, o.categoryOption()) + } - public static LinkStore create(DatabaseAdapter databaseAdapter) { - return StoreFactory.linkStore(databaseAdapter, CategoryOptionComboCategoryOptionLinkTableInfo.TABLE_INFO, - CategoryOptionComboCategoryOptionLinkTableInfo.Columns.CATEGORY_OPTION_COMBO, BINDER, - CategoryOptionComboCategoryOptionLink::create - ); + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): LinkStore { + return linkStore( + databaseAdapter, CategoryOptionComboCategoryOptionLinkTableInfo.TABLE_INFO, + CategoryOptionComboCategoryOptionLinkTableInfo.Columns.CATEGORY_OPTION_COMBO, BINDER + ) { cursor: Cursor -> CategoryOptionComboCategoryOptionLink.create(cursor) } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStore.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStore.kt similarity index 83% rename from core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStore.java rename to core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStore.kt index a135007191..abd44ccb67 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStore.kt @@ -25,15 +25,11 @@ * (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.category.internal -package org.hisp.dhis.android.core.category.internal; +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.category.CategoryOptionCombo - -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; -import org.hisp.dhis.android.core.category.CategoryOptionCombo; - -import java.util.List; - -public interface CategoryOptionComboStore extends IdentifiableObjectStore { - List getForCategoryCombo(String categoryComboUid); +internal interface CategoryOptionComboStore : IdentifiableObjectStore { + fun getForCategoryCombo(categoryComboUid: String): List } diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStoreImpl.kt similarity index 54% rename from core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStoreImpl.java rename to core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStoreImpl.kt index 67d2335be0..a62516061f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboStoreImpl.kt @@ -25,52 +25,52 @@ * (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.category.internal -package org.hisp.dhis.android.core.category.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.SQLStatementBuilderImpl +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +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.IdentifiableObjectStoreImpl +import org.hisp.dhis.android.core.arch.helpers.UidsHelper.getUidOrNull +import org.hisp.dhis.android.core.category.CategoryOptionCombo +import org.hisp.dhis.android.core.category.CategoryOptionComboTableInfo -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -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.IdentifiableObjectStoreImpl; -import org.hisp.dhis.android.core.arch.helpers.UidsHelper; -import org.hisp.dhis.android.core.category.CategoryOptionCombo; -import org.hisp.dhis.android.core.category.CategoryOptionComboTableInfo; +@Suppress("MagicNumber") +internal class CategoryOptionComboStoreImpl private constructor( + databaseAdapter: DatabaseAdapter, + statementBuilder: SQLStatementBuilderImpl +) : IdentifiableObjectStoreImpl( + databaseAdapter, + statementBuilder, + BINDER, + { cursor: Cursor -> CategoryOptionCombo.create(cursor) } +), + CategoryOptionComboStore { -import java.util.List; - -import androidx.annotation.NonNull; - -public final class CategoryOptionComboStoreImpl extends IdentifiableObjectStoreImpl - implements CategoryOptionComboStore { - - private CategoryOptionComboStoreImpl(DatabaseAdapter databaseAdapter, - SQLStatementBuilderImpl statementBuilder) { - super(databaseAdapter, statementBuilder, BINDER, CategoryOptionCombo::create); + override fun getForCategoryCombo(categoryComboUid: String): List { + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(CategoryOptionComboTableInfo.Columns.CATEGORY_COMBO, categoryComboUid) + .build() + return selectWhere(whereClause) } - @Override - public List getForCategoryCombo(String categoryComboUid) { - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(CategoryOptionComboTableInfo.Columns.CATEGORY_COMBO, categoryComboUid) - .build(); - return selectWhere(whereClause); - } - - private static StatementBinder BINDER - = new IdentifiableStatementBinder() { + companion object { + private val BINDER: StatementBinder = + object : IdentifiableStatementBinder() { + override fun bindToStatement(o: CategoryOptionCombo, w: StatementWrapper) { + super.bindToStatement(o, w) + w.bind(7, getUidOrNull(o.categoryCombo())) + } + } - @Override - public void bindToStatement(@NonNull CategoryOptionCombo o, @NonNull StatementWrapper w) { - super.bindToStatement(o, w); - w.bind(7, UidsHelper.getUidOrNull(o.categoryCombo())); + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): CategoryOptionComboStore { + val statementBuilder = SQLStatementBuilderImpl(CategoryOptionComboTableInfo.TABLE_INFO) + return CategoryOptionComboStoreImpl(databaseAdapter, statementBuilder) } - }; - - public static CategoryOptionComboStore create(DatabaseAdapter databaseAdapter) { - SQLStatementBuilderImpl statementBuilder = new SQLStatementBuilderImpl(CategoryOptionComboTableInfo.TABLE_INFO); - return new CategoryOptionComboStoreImpl(databaseAdapter, statementBuilder); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionStore.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionStore.kt similarity index 68% rename from core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionStore.java rename to core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionStore.kt index 2bba23c6e4..3dbdf5b82f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionStore.kt @@ -25,37 +25,35 @@ * (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.category.internal -package org.hisp.dhis.android.core.category.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.NameableStatementBinder +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.category.CategoryOption +import org.hisp.dhis.android.core.category.CategoryOptionTableInfo -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.db.stores.binders.internal.NameableStatementBinder; -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; -import org.hisp.dhis.android.core.category.CategoryOption; -import org.hisp.dhis.android.core.category.CategoryOptionTableInfo; +@Suppress("MagicNumber") +internal object CategoryOptionStore { -import androidx.annotation.NonNull; - -final class CategoryOptionStore { - - private CategoryOptionStore() { - } - - private static StatementBinder BINDER = new NameableStatementBinder() { - @Override - public void bindToStatement(@NonNull CategoryOption o, @NonNull StatementWrapper w) { - super.bindToStatement(o, w); - w.bind(11, o.startDate()); - w.bind(12, o.endDate()); - w.bind(13, o.access().data().write()); + private val BINDER: StatementBinder = object : NameableStatementBinder() { + override fun bindToStatement(o: CategoryOption, w: StatementWrapper) { + super.bindToStatement(o, w) + w.bind(11, o.startDate()) + w.bind(12, o.endDate()) + w.bind(13, o.access().data().write()) } - }; + } - public static IdentifiableObjectStore create(DatabaseAdapter databaseAdapter) { - return StoreFactory.objectWithUidStore(databaseAdapter, - CategoryOptionTableInfo.TABLE_INFO, BINDER, CategoryOption::create); + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + return objectWithUidStore( + databaseAdapter, + CategoryOptionTableInfo.TABLE_INFO, BINDER + ) { cursor: Cursor -> CategoryOption.create(cursor) } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobStore.kt b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryStore.kt similarity index 70% rename from core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobStore.kt rename to core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryStore.kt index 04dd4777dc..b4f48dc5c0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryStore.kt @@ -25,22 +25,34 @@ * (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.tracker.importer.internal +package org.hisp.dhis.android.core.category.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.common.StorableObjectWithUid +import org.hisp.dhis.android.core.category.Category +import org.hisp.dhis.android.core.category.CategoryTableInfo -internal object TrackerJobStore { +@Suppress("MagicNumber") +internal object CategoryStore { + + private val BINDER: StatementBinder = object : IdentifiableStatementBinder() { + override fun bindToStatement(o: Category, w: StatementWrapper) { + super.bindToStatement(o, w) + w.bind(7, o.dataDimensionType()) + } + } @JvmStatic - fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { return objectWithUidStore( databaseAdapter, - TrackerJobTableInfo.TABLE_INFO, - StatementBinder { o, w -> w.bind(1, o.uid()) } - ) { StorableObjectWithUid.create(it) } + CategoryTableInfo.TABLE_INFO, + BINDER + ) { cursor: Cursor -> Category.create(cursor) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/AggregationType.kt b/core/src/main/java/org/hisp/dhis/android/core/common/AggregationType.kt new file mode 100644 index 0000000000..ca7973c1f0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/AggregationType.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2021, 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.common + +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue + +enum class AggregationType { + SUM, + AVERAGE, + AVERAGE_SUM_ORG_UNIT, + LAST, + LAST_AVERAGE_ORG_UNIT, + LAST_IN_PERIOD, + LAST_IN_PERIOD_AVERAGE_ORG_UNIT, + FIRST, + FIRST_AVERAGE_ORG_UNIT, + COUNT, + STDDEV, + VARIANCE, + MIN, + MAX, + NONE, + CUSTOM, + + @JsonEnumDefaultValue + DEFAULT +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/BaseDataObject.java b/core/src/main/java/org/hisp/dhis/android/core/common/BaseDataObject.java index e23dcdd0db..41a1163a28 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/common/BaseDataObject.java +++ b/core/src/main/java/org/hisp/dhis/android/core/common/BaseDataObject.java @@ -35,17 +35,37 @@ import com.gabrielittner.auto.value.cursor.ColumnName; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.StateColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreStateColumnAdapter; public abstract class BaseDataObject extends BaseObject implements DataObject { + /** + * @deprecated Use {@link #syncState()} instead. + */ + @Deprecated @Override @Nullable - @ColumnName(DataColumns.STATE) + @ColumnAdapter(IgnoreStateColumnAdapter.class) + public State state() { + return syncState(); + } + + @Override + @Nullable + @ColumnName(DataColumns.SYNC_STATE) @ColumnAdapter(StateColumnAdapter.class) - public abstract State state(); + public abstract State syncState(); @JsonPOJOBuilder(withPrefix = "") protected static abstract class Builder extends BaseObject.Builder { - public abstract T state(@Nullable State state); + public abstract T syncState(@Nullable State syncState); + + /** + * @deprecated Use {@link #syncState(State)} instead. + */ + @Deprecated + public T state(State state) { + return syncState(state); + } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/DataColumns.java b/core/src/main/java/org/hisp/dhis/android/core/common/DataColumns.java index b62952a521..bf705725e4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/common/DataColumns.java +++ b/core/src/main/java/org/hisp/dhis/android/core/common/DataColumns.java @@ -29,5 +29,6 @@ package org.hisp.dhis.android.core.common; public class DataColumns extends CoreColumns { - public static final String STATE = "state"; + public static final String AGGREGATED_SYNC_STATE = "aggregatedSyncState"; + public static final String SYNC_STATE = "syncState"; } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/DataObject.java b/core/src/main/java/org/hisp/dhis/android/core/common/DataObject.java index 24fec9e1c0..ec2aa87a6e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/common/DataObject.java +++ b/core/src/main/java/org/hisp/dhis/android/core/common/DataObject.java @@ -29,5 +29,8 @@ package org.hisp.dhis.android.core.common; public interface DataObject extends CoreObject { + @Deprecated State state(); + + State syncState(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/RelativeOrganisationUnit.kt b/core/src/main/java/org/hisp/dhis/android/core/common/RelativeOrganisationUnit.kt new file mode 100644 index 0000000000..3b9924be03 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/RelativeOrganisationUnit.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2021, 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.common + +enum class RelativeOrganisationUnit { + USER_ORGUNIT, + USER_ORGUNIT_CHILDREN, + USER_ORGUNIT_GRANDCHILDREN +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/RelativePeriod.kt b/core/src/main/java/org/hisp/dhis/android/core/common/RelativePeriod.kt index 85abdf8ced..71a994008b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/common/RelativePeriod.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/common/RelativePeriod.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.common +import com.fasterxml.jackson.annotation.JsonAlias import org.hisp.dhis.android.core.period.PeriodType @Suppress("MagicNumber") @@ -37,47 +38,49 @@ enum class RelativePeriod constructor( internal val periodsThisYear: Boolean = false, internal val periodsLastYear: Boolean = false ) { - TODAY(PeriodType.Daily, 0, 1), - YESTERDAY(PeriodType.Daily, -1, 0), - LAST_3_DAYS(PeriodType.Daily, -3, 0), - LAST_7_DAYS(PeriodType.Daily, -7, 0), - LAST_14_DAYS(PeriodType.Daily, -14, 0), - LAST_30_DAYS(PeriodType.Daily, -30, 0), - LAST_60_DAYS(PeriodType.Daily, -60, 0), - LAST_90_DAYS(PeriodType.Daily, -90, 0), - LAST_180_DAYS(PeriodType.Daily, -180, 0), - THIS_MONTH(PeriodType.Monthly, 0, 1), - LAST_MONTH(PeriodType.Monthly, -1, 0), - THIS_BIMONTH(PeriodType.BiMonthly, 0, 1), - LAST_BIMONTH(PeriodType.BiMonthly, -1, 0), - THIS_QUARTER(PeriodType.Quarterly, 0, 1), - LAST_QUARTER(PeriodType.Quarterly, -1, 0), - THIS_SIX_MONTH(PeriodType.SixMonthly, 0, 1), - LAST_SIX_MONTH(PeriodType.SixMonthly, -1, 0), - WEEKS_THIS_YEAR(PeriodType.Weekly, null, null, true, false), - MONTHS_THIS_YEAR(PeriodType.Monthly, null, null, true, false), - BIMONTHS_THIS_YEAR(PeriodType.BiMonthly, null, null, true, false), - QUARTERS_THIS_YEAR(PeriodType.Quarterly, null, null, true, false), - THIS_YEAR(PeriodType.Yearly, 0, 1), - MONTHS_LAST_YEAR(PeriodType.Monthly, null, null, false, true), - QUARTERS_LAST_YEAR(PeriodType.Quarterly, null, null, false, true), - LAST_YEAR(PeriodType.Yearly, -1, 0), - LAST_5_YEARS(PeriodType.Yearly, -5, 0), - LAST_12_MONTHS(PeriodType.Monthly, -12, 0), - LAST_6_MONTHS(PeriodType.Monthly, -6, 0), - LAST_3_MONTHS(PeriodType.Monthly, -3, 0), - LAST_6_BIMONTHS(PeriodType.BiMonthly, -6, 0), - LAST_4_QUARTERS(PeriodType.Quarterly, -4, 0), - LAST_2_SIXMONTHS(PeriodType.SixMonthly, -2, 0), - THIS_FINANCIAL_YEAR(PeriodType.FinancialApril, 0, 1), - LAST_FINANCIAL_YEAR(PeriodType.FinancialApril, -1, 0), - LAST_5_FINANCIAL_YEARS(PeriodType.FinancialApril, -5, 0), - THIS_WEEK(PeriodType.Weekly, 0, 1), - LAST_WEEK(PeriodType.Weekly, -1, 0), - THIS_BIWEEK(PeriodType.BiWeekly, 0, 1), - LAST_BIWEEK(PeriodType.BiWeekly, -1, 0), - LAST_4_WEEKS(PeriodType.Weekly, -4, 0), - LAST_4_BIWEEKS(PeriodType.BiWeekly, -4, 0), - LAST_12_WEEKS(PeriodType.Weekly, -12, 0), - LAST_52_WEEKS(PeriodType.Weekly, -52, 0) + @JsonAlias("today", "thisDay") TODAY(PeriodType.Daily, 0, 1), + @JsonAlias("yesterday") YESTERDAY(PeriodType.Daily, -1, 0), + @JsonAlias("last3Days") LAST_3_DAYS(PeriodType.Daily, -3, 0), + @JsonAlias("last7Days") LAST_7_DAYS(PeriodType.Daily, -7, 0), + @JsonAlias("last14Days") LAST_14_DAYS(PeriodType.Daily, -14, 0), + @JsonAlias("last30Days") LAST_30_DAYS(PeriodType.Daily, -30, 0), + @JsonAlias("last60Days") LAST_60_DAYS(PeriodType.Daily, -60, 0), + @JsonAlias("last90Days") LAST_90_DAYS(PeriodType.Daily, -90, 0), + @JsonAlias("last180Days") LAST_180_DAYS(PeriodType.Daily, -180, 0), + @JsonAlias("thisMonth") THIS_MONTH(PeriodType.Monthly, 0, 1), + @JsonAlias("lastMonth") LAST_MONTH(PeriodType.Monthly, -1, 0), + @JsonAlias("thisBimonth") THIS_BIMONTH(PeriodType.BiMonthly, 0, 1), + @JsonAlias("lastBimonth") LAST_BIMONTH(PeriodType.BiMonthly, -1, 0), + @JsonAlias("thisQuarter") THIS_QUARTER(PeriodType.Quarterly, 0, 1), + @JsonAlias("lastQuarter") LAST_QUARTER(PeriodType.Quarterly, -1, 0), + @JsonAlias("thisSixMonth") THIS_SIX_MONTH(PeriodType.SixMonthly, 0, 1), + @JsonAlias("lastSixMonth") LAST_SIX_MONTH(PeriodType.SixMonthly, -1, 0), + @JsonAlias("weeksThisYear") WEEKS_THIS_YEAR(PeriodType.Weekly, null, null, true, false), + @JsonAlias("monthsThisYear") MONTHS_THIS_YEAR(PeriodType.Monthly, null, null, true, false), + @JsonAlias("biMonthsThisYear") BIMONTHS_THIS_YEAR(PeriodType.BiMonthly, null, null, true, false), + @JsonAlias("quartersThisYear") QUARTERS_THIS_YEAR(PeriodType.Quarterly, null, null, true, false), + @JsonAlias("thisYear") THIS_YEAR(PeriodType.Yearly, 0, 1), + @JsonAlias("monthsLastYear") MONTHS_LAST_YEAR(PeriodType.Monthly, null, null, false, true), + @JsonAlias("quartersLastYear") QUARTERS_LAST_YEAR(PeriodType.Quarterly, null, null, false, true), + @JsonAlias("lastYear") LAST_YEAR(PeriodType.Yearly, -1, 0), + @JsonAlias("last5Years") LAST_5_YEARS(PeriodType.Yearly, -5, 0), + @JsonAlias("last10Years") LAST_10_YEARS(PeriodType.Yearly, -10, 0), + @JsonAlias("last12Months") LAST_12_MONTHS(PeriodType.Monthly, -12, 0), + @JsonAlias("last6Months") LAST_6_MONTHS(PeriodType.Monthly, -6, 0), + @JsonAlias("last3Months") LAST_3_MONTHS(PeriodType.Monthly, -3, 0), + @JsonAlias("last6BiMonths") LAST_6_BIMONTHS(PeriodType.BiMonthly, -6, 0), + @JsonAlias("last4Quarters") LAST_4_QUARTERS(PeriodType.Quarterly, -4, 0), + @JsonAlias("last2SixMonths") LAST_2_SIXMONTHS(PeriodType.SixMonthly, -2, 0), + @JsonAlias("thisFinancialYear") THIS_FINANCIAL_YEAR(PeriodType.FinancialApril, 0, 1), + @JsonAlias("lastFinancialYear") LAST_FINANCIAL_YEAR(PeriodType.FinancialApril, -1, 0), + @JsonAlias("last5FinancialYears") LAST_5_FINANCIAL_YEARS(PeriodType.FinancialApril, -5, 0), + @JsonAlias("last10FinancialYears") LAST_10_FINANCIAL_YEARS(PeriodType.FinancialApril, -10, 0), + @JsonAlias("thisWeek") THIS_WEEK(PeriodType.Weekly, 0, 1), + @JsonAlias("lastWeek") LAST_WEEK(PeriodType.Weekly, -1, 0), + @JsonAlias("thisBiWeek") THIS_BIWEEK(PeriodType.BiWeekly, 0, 1), + @JsonAlias("lastBiWeek") LAST_BIWEEK(PeriodType.BiWeekly, -1, 0), + @JsonAlias("last4Weeks") LAST_4_WEEKS(PeriodType.Weekly, -4, 0), + @JsonAlias("last4BiWeeks") LAST_4_BIWEEKS(PeriodType.BiWeekly, -4, 0), + @JsonAlias("last12Weeks") LAST_12_WEEKS(PeriodType.Weekly, -12, 0), + @JsonAlias("last52Weeks") LAST_52_WEEKS(PeriodType.Weekly, -52, 0) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/State.java b/core/src/main/java/org/hisp/dhis/android/core/common/State.kt similarity index 76% rename from core/src/main/java/org/hisp/dhis/android/core/common/State.java rename to core/src/main/java/org/hisp/dhis/android/core/common/State.kt index 2df0f123d7..d6b43c531d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/common/State.java +++ b/core/src/main/java/org/hisp/dhis/android/core/common/State.kt @@ -25,33 +25,32 @@ * (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.common -package org.hisp.dhis.android.core.common; - -public enum State { - /** Data created locally that does not exist in the server yet. */ +enum class State { + /** Data created locally that does not exist in the server yet. */ TO_POST, - /** Data modified locally that exists in the server. */ + /** Data modified locally that exists in the server. */ TO_UPDATE, - /** Data that received an error from the server after the last upload. */ + /** Data that received an error from the server after the last upload. */ ERROR, - /** The element is synced with the server. There are no local changes for this value. */ + /** The element is synced with the server. There are no local changes for this value. */ SYNCED, - /** Data that received a warning from the server after the last upload. */ + /** Data that received a warning from the server after the last upload. */ WARNING, /** Data is being uploaded. If it is modified before receiving any server response, its state is back to - * TO_UPDATE. When the server response arrives, its state does not change to SYNCED, - * but it remains in TO_UPDATE to indicate that there are local changes. + * **TO_UPDATE**. When the server response arrives, its state does not change to **SYNCED**, + * but it remains in **TO_UPDATE** to indicate that there are local changes. */ UPLOADING, /** This TrackedEntityInstance has been downloaded with the sole purpose of fulfilling a relationship to another - * TEI. This RELATIONSHIP TEI only has basic information (uid, type, etc) and the list of + * TEI. This **RELATIONSHIP** TEI only has basic information (uid, type, etc) and the list of * TrackedEntityAttributes to be able to print meaningful information about the relationship. Other data such * enrollments, events or relationships is not downloaded for this TEI. * Also, this TEI cannot be modified or uploaded to the server. @@ -64,18 +63,19 @@ public enum State { */ SENT_VIA_SMS, - /** Data is sent by sms and there is a successful response from the server. */ + /** Data is sent by sms and there is a successful response from the server. */ SYNCED_VIA_SMS; - public static State[] uploadableStates() { - return new State[] { - TO_POST, TO_UPDATE, SENT_VIA_SMS, SYNCED_VIA_SMS, UPLOADING - }; - } + companion object { + + @JvmStatic + fun uploadableStates(): Array { + return arrayOf(TO_POST, TO_UPDATE, SENT_VIA_SMS, SYNCED_VIA_SMS, UPLOADING) + } - public static State[] uploadableStatesIncludingError() { - return new State[] { - TO_POST, TO_UPDATE, SENT_VIA_SMS, SYNCED_VIA_SMS, UPLOADING, ERROR, WARNING - }; + @JvmStatic + fun uploadableStatesIncludingError(): Array { + return arrayOf(TO_POST, TO_UPDATE, SENT_VIA_SMS, SYNCED_VIA_SMS, UPLOADING, ERROR, WARNING) + } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/ValueType.java b/core/src/main/java/org/hisp/dhis/android/core/common/ValueType.java deleted file mode 100644 index f582323734..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/common/ValueType.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.common; - -import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; - -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -public enum ValueType { - TEXT(String.class), - LONG_TEXT(String.class), - LETTER(String.class), - BOOLEAN(Boolean.class), - TRUE_ONLY(Boolean.class), - DATE(Date.class), - DATETIME(Date.class), - TIME(String.class), - NUMBER(Double.class), - UNIT_INTERVAL(Double.class), - PERCENTAGE(Double.class), - INTEGER(Integer.class), - INTEGER_POSITIVE(Integer.class), - INTEGER_NEGATIVE(Integer.class), - INTEGER_ZERO_OR_POSITIVE(Integer.class), - FILE_RESOURCE(String.class), - COORDINATE(String.class), - - // TODO: Categorize - PHONE_NUMBER(String.class), - EMAIL(String.class), - USERNAME(String.class), - - // TODO: Implement later - ORGANISATION_UNIT(OrganisationUnit.class), - TRACKER_ASSOCIATE(TrackedEntityInstance.class), - - //New values: - AGE(Date.class), - URL(String.class), - - IMAGE(String.class); - - private static final Set INTEGER_TYPES = new HashSet<>(Arrays.asList(INTEGER, - INTEGER_POSITIVE, INTEGER_NEGATIVE, INTEGER_ZERO_OR_POSITIVE)); - - private static final Set NUMERIC_TYPES = new HashSet<>(Arrays.asList(INTEGER, NUMBER, - INTEGER_POSITIVE, INTEGER_NEGATIVE, INTEGER_ZERO_OR_POSITIVE, UNIT_INTERVAL, PERCENTAGE)); - - private static final Set BOOLEAN_TYPES = new HashSet<>(Arrays.asList(BOOLEAN, - TRUE_ONLY)); - - private static final Set TEXT_TYPES = new HashSet<>(Arrays.asList(TEXT, LONG_TEXT, - LETTER, COORDINATE, TIME, IMAGE)); - - private static final Set DATE_TYPES = new HashSet<>(Arrays.asList(DATE, DATETIME)); - - private final Class javaClass; - - ValueType() { - this.javaClass = null; - } - - ValueType(Class javaClass) { - this.javaClass = javaClass; - } - - public Class getJavaClass() { - return javaClass; - } - - public boolean isInteger() { - return INTEGER_TYPES.contains(this); - } - - public boolean isNumeric() { - return NUMERIC_TYPES.contains(this); - } - - public boolean isBoolean() { - return BOOLEAN_TYPES.contains(this); - } - - public boolean isText() { - return TEXT_TYPES.contains(this); - } - - public boolean isDate() { - return DATE_TYPES.contains(this); - } - - public boolean isFile() { - return this == FILE_RESOURCE; - } - - public boolean isCoordinate() { - return this == COORDINATE; - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/ValueType.kt b/core/src/main/java/org/hisp/dhis/android/core/common/ValueType.kt new file mode 100644 index 0000000000..85cf07d5ee --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/ValueType.kt @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2004-2021, 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.common + +import java.util.* +import org.hisp.dhis.android.core.common.valuetype.validation.validators.* +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance + +enum class ValueType(javaClass: Class<*>, val validator: ValueTypeValidator<*>) { + TEXT(String::class.java, TextValidator), + LONG_TEXT(String::class.java, LongTextValidator), + LETTER(String::class.java, LetterValidator), + BOOLEAN(Boolean::class.java, BooleanValidator), + TRUE_ONLY(Boolean::class.java, TrueOnlyValidator), + DATE(Date::class.java, DateValidator), + DATETIME(Date::class.java, DateTimeValidator), + TIME(String::class.java, TimeValidator), + NUMBER(Double::class.java, NumberValidator), + UNIT_INTERVAL(Double::class.java, UnitIntervalValidator), + PERCENTAGE(Double::class.java, PercentageValidator), + INTEGER(Int::class.java, IntegerValidator), + INTEGER_POSITIVE(Int::class.java, IntegerPositiveValidator), + INTEGER_NEGATIVE(Int::class.java, IntegerNegativeValidator), + INTEGER_ZERO_OR_POSITIVE(Int::class.java, IntegerZeroOrPositiveValidator), + FILE_RESOURCE(String::class.java, UidValidator), + COORDINATE(String::class.java, CoordinateValidator), + PHONE_NUMBER(String::class.java, PhoneNumberValidator), + EMAIL(String::class.java, EmailValidator), + USERNAME(String::class.java, TextValidator), + ORGANISATION_UNIT(OrganisationUnit::class.java, UidValidator), + TRACKER_ASSOCIATE(TrackedEntityInstance::class.java, UidValidator), + AGE(Date::class.java, DateValidator), + URL(String::class.java, TextValidator), + IMAGE(String::class.java, UidValidator); + + val isInteger: Boolean + get() = INTEGER_TYPES.contains(this) + val isNumeric: Boolean + get() = NUMERIC_TYPES.contains(this) + val isBoolean: Boolean + get() = BOOLEAN_TYPES.contains(this) + val isText: Boolean + get() = TEXT_TYPES.contains(this) + val isDate: Boolean + get() = DATE_TYPES.contains(this) + val isFile: Boolean + get() = this == FILE_RESOURCE + val isCoordinate: Boolean + get() = this == COORDINATE + + companion object { + private val INTEGER_TYPES: Set = + HashSet(listOf(INTEGER, INTEGER_POSITIVE, INTEGER_NEGATIVE, INTEGER_ZERO_OR_POSITIVE)) + private val NUMERIC_TYPES: Set = + HashSet( + listOf( + INTEGER, NUMBER, INTEGER_POSITIVE, INTEGER_NEGATIVE, INTEGER_ZERO_OR_POSITIVE, + UNIT_INTERVAL, PERCENTAGE + ) + ) + private val BOOLEAN_TYPES: Set = HashSet(listOf(BOOLEAN, TRUE_ONLY)) + private val TEXT_TYPES: Set = HashSet(listOf(TEXT, LONG_TEXT, LETTER, COORDINATE, TIME, IMAGE)) + private val DATE_TYPES: Set = HashSet(listOf(DATE, DATETIME)) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagator.java b/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagator.kt similarity index 54% rename from core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagator.java rename to core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagator.kt index a6385479a4..b6fbff7b19 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagator.java +++ b/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagator.kt @@ -25,30 +25,49 @@ * (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.common.internal -package org.hisp.dhis.android.core.common.internal; +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.note.Note +import org.hisp.dhis.android.core.relationship.RelationshipItem +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.enrollment.Enrollment; -import org.hisp.dhis.android.core.event.Event; -import org.hisp.dhis.android.core.note.Note; -import org.hisp.dhis.android.core.relationship.RelationshipItem; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue; +@Suppress("TooManyFunctions") +internal interface DataStatePropagator { -public interface DataStatePropagator { - void propagateEnrollmentUpdate(Enrollment enrollment); + fun propagateTrackedEntityInstanceUpdate(tei: TrackedEntityInstance?) - void propagateEventUpdate(Event event); + fun propagateEnrollmentUpdate(enrollment: Enrollment?) - void propagateTrackedEntityDataValueUpdate(TrackedEntityDataValue dataValue); + fun propagateEventUpdate(event: Event?) - void propagateTrackedEntityAttributeUpdate(TrackedEntityAttributeValue trackedEntityAttributeValue); + fun propagateTrackedEntityDataValueUpdate(dataValue: TrackedEntityDataValue?) - void propagateNoteCreation(Note note); + fun propagateTrackedEntityAttributeUpdate(trackedEntityAttributeValue: TrackedEntityAttributeValue?) - void propagateRelationshipUpdate(RelationshipItem item); + fun propagateNoteCreation(note: Note?) - void resetUploadingEnrollmentAndEventStates(String trackedEntityInstanceUid); + fun propagateRelationshipUpdate(item: RelationshipItem?) - void resetUploadingEventStates(String enrollmentUid); -} \ No newline at end of file + fun resetUploadingEnrollmentAndEventStates(trackedEntityInstanceUid: String?) + + fun resetUploadingEventStates(enrollmentUid: String?) + + fun refreshTrackedEntityInstanceAggregatedSyncState(trackedEntityInstanceUid: String) + + fun refreshEnrollmentAggregatedSyncState(enrollmentUid: String) + + fun refreshEventAggregatedSyncState(eventUid: String) + + fun refreshAggregatedSyncStates(uidHolder: DataStateUidHolder) + + fun getRelatedUids( + trackedEntityInstanceUids: List, + enrollmentUids: List, + eventUids: List, + relationshipUids: List + ): DataStateUidHolder +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorImpl.java b/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorImpl.java deleted file mode 100644 index 383b131b0b..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorImpl.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.common.internal; - -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.enrollment.Enrollment; -import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo; -import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore; -import org.hisp.dhis.android.core.event.Event; -import org.hisp.dhis.android.core.event.EventTableInfo; -import org.hisp.dhis.android.core.event.internal.EventStore; -import org.hisp.dhis.android.core.note.Note; -import org.hisp.dhis.android.core.relationship.RelationshipItem; -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.TrackedEntityInstanceStore; - -import java.util.Date; -import java.util.List; - -import javax.inject.Inject; - -import dagger.Reusable; - -@Reusable -public final class DataStatePropagatorImpl implements DataStatePropagator { - - private final TrackedEntityInstanceStore trackedEntityInstanceStore; - private final EnrollmentStore enrollmentStore; - private final EventStore eventStore; - - @Inject - DataStatePropagatorImpl(TrackedEntityInstanceStore trackedEntityInstanceStore, - EnrollmentStore enrollmentStore, - EventStore eventStore) { - this.trackedEntityInstanceStore = trackedEntityInstanceStore; - this.enrollmentStore = enrollmentStore; - this.eventStore = eventStore; - } - - @Override - public void propagateEnrollmentUpdate(Enrollment enrollment) { - if (enrollment != null) { - setTeiStateForUpdate(enrollment.trackedEntityInstance()); - } - } - - @Override - public void propagateEventUpdate(Event event) { - if (event != null && event.enrollment() != null) { - Enrollment enrollment = setEnrollmentStateForUpdate(event.enrollment()); - propagateEnrollmentUpdate(enrollment); - } - } - - @Override - public void propagateTrackedEntityDataValueUpdate(TrackedEntityDataValue dataValue) { - Event event = setEventStateForUpdate(dataValue.event()); - propagateEventUpdate(event); - } - - @Override - public void propagateTrackedEntityAttributeUpdate(TrackedEntityAttributeValue trackedEntityAttributeValue) { - setTeiStateForUpdate(trackedEntityAttributeValue.trackedEntityInstance()); - } - - @Override - public void propagateNoteCreation(Note note) { - if (note.noteType() == Note.NoteType.ENROLLMENT_NOTE) { - Enrollment enrollment = setEnrollmentStateForUpdate(note.enrollment()); - propagateEnrollmentUpdate(enrollment); - } else if (note.noteType() == Note.NoteType.EVENT_NOTE) { - Event event = setEventStateForUpdate(note.event()); - propagateEventUpdate(event); - } - } - - @Override - public void propagateRelationshipUpdate(RelationshipItem item) { - if (item != null) { - if (item.hasTrackedEntityInstance()) { - setTeiStateForUpdate(item.trackedEntityInstance().trackedEntityInstance()); - } else if (item.hasEnrollment()) { - Enrollment enrollment = setEnrollmentStateForUpdate(item.enrollment().enrollment()); - propagateEnrollmentUpdate(enrollment); - } else if (item.hasEvent()) { - Event event = setEventStateForUpdate(item.event().event()); - propagateEventUpdate(event); - } - } - } - - private TrackedEntityInstance setTeiStateForUpdate(String trackedEntityInstanceUid) { - TrackedEntityInstance instance = trackedEntityInstanceStore.selectByUid(trackedEntityInstanceUid); - if (instance != null) { - Date now = new Date(); - TrackedEntityInstance updatedTEI = instance.toBuilder() - .state(getStateForUpdate(instance.state())) - .lastUpdated(getMaxDate(instance.lastUpdated(), now)) - .lastUpdatedAtClient(getMaxDate(instance.lastUpdatedAtClient(), now)) - .build(); - trackedEntityInstanceStore.update(updatedTEI); - instance = updatedTEI; - } - return instance; - } - - private Enrollment setEnrollmentStateForUpdate(String enrollmentUid) { - Enrollment enrollment = enrollmentStore.selectByUid(enrollmentUid); - if (enrollment != null) { - Date now = new Date(); - Enrollment updatedEnrollment = enrollment.toBuilder() - .state(getStateForUpdate(enrollment.state())) - .lastUpdated(getMaxDate(enrollment.lastUpdated(), now)) - .lastUpdatedAtClient(getMaxDate(enrollment.lastUpdatedAtClient(), now)) - .build(); - enrollmentStore.update(updatedEnrollment); - enrollment = updatedEnrollment; - } - return enrollment; - } - - private Event setEventStateForUpdate(String eventUid) { - Event event = eventStore.selectByUid(eventUid); - if (event != null) { - Date now = new Date(); - Event updatedEvent = event.toBuilder() - .state(getStateForUpdate(event.state())) - .lastUpdated(getMaxDate(event.lastUpdated(), now)) - .lastUpdatedAtClient(getMaxDate(event.lastUpdatedAtClient(), now)) - .build(); - eventStore.update(updatedEvent); - event = updatedEvent; - } - return event; - } - - public void resetUploadingEnrollmentAndEventStates(String trackedEntityInstanceUid) { - if (trackedEntityInstanceUid == null) { - return; - } - - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE, trackedEntityInstanceUid) - .build(); - List enrollments = enrollmentStore.selectWhere(whereClause); - - for (Enrollment enrollment : enrollments) { - if (State.UPLOADING.equals(enrollment.state())) { - enrollmentStore.setState(enrollment.uid(), State.TO_UPDATE); - resetUploadingEventStates(enrollment.uid()); - } - } - } - - public void resetUploadingEventStates(String enrollmentUid) { - if (enrollmentUid == null) { - return; - } - - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(EventTableInfo.Columns.ENROLLMENT, enrollmentUid) - .build(); - List events = eventStore.selectWhere(whereClause); - - for (Event event : events) { - if (State.UPLOADING.equals(event.state())) { - eventStore.setState(event.uid(), State.TO_UPDATE); - } - } - } - - private Date getMaxDate(Date existing, Date today) { - if (existing == null) { - return today; - } else if (today == null || existing.after(today)) { - return existing; - } else { - return today; - } - } - - private State getStateForUpdate(State existingState) { - if (State.TO_POST.equals(existingState) || State.RELATIONSHIP.equals(existingState)) { - return existingState; - } else { - return State.TO_UPDATE; - } - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorImpl.kt new file mode 100644 index 0000000000..d59a07bedd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStatePropagatorImpl.kt @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2004-2021, 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.common.internal + +import dagger.Reusable +import java.util.* +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.EventTableInfo +import org.hisp.dhis.android.core.event.internal.EventStore +import org.hisp.dhis.android.core.note.Note +import org.hisp.dhis.android.core.relationship.RelationshipConstraintType +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.relationship.RelationshipItem +import org.hisp.dhis.android.core.relationship.internal.RelationshipItemStore +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore +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.TrackedEntityInstanceStore + +@Reusable +@Suppress("TooManyFunctions") +internal class DataStatePropagatorImpl @Inject internal constructor( + private val trackedEntityInstanceStore: TrackedEntityInstanceStore, + private val enrollmentStore: EnrollmentStore, + private val eventStore: EventStore, + private val relationshipStore: RelationshipStore, + private val relationshipItemStore: RelationshipItemStore +) : DataStatePropagator { + + override fun propagateTrackedEntityInstanceUpdate(tei: TrackedEntityInstance?) { + tei?.let { + refreshTrackedEntityInstanceAggregatedSyncState(it.uid()) + refreshTrackedEntityInstanceLastUpdated(it.uid()) + } + } + + override fun propagateEnrollmentUpdate(enrollment: Enrollment?) { + enrollment?.let { + refreshEnrollmentAggregatedSyncState(it.uid()) + refreshEnrollmentLastUpdated(it.uid()) + + val tei = trackedEntityInstanceStore.selectByUid(it.trackedEntityInstance()!!) + propagateTrackedEntityInstanceUpdate(tei) + } + } + + override fun propagateEventUpdate(event: Event?) { + event?.let { + refreshEventAggregatedSyncState(it.uid()) + refreshEventLastUpdated(it.uid()) + + it.enrollment()?.let { enrollmentUid -> + val enrollment = enrollmentStore.selectByUid(enrollmentUid) + propagateEnrollmentUpdate(enrollment) + } + } + } + + override fun propagateTrackedEntityDataValueUpdate(dataValue: TrackedEntityDataValue?) { + setEventSyncState(dataValue!!.event()!!, getStateForUpdate) + } + + override fun propagateTrackedEntityAttributeUpdate(trackedEntityAttributeValue: TrackedEntityAttributeValue?) { + trackedEntityAttributeValue!!.trackedEntityInstance()?.let { trackedEntityInstanceUid -> + val enrollments = enrollmentStore.selectByTrackedEntityInstanceAndAttribute( + trackedEntityInstanceUid, + trackedEntityAttributeValue.trackedEntityAttribute()!! + ) + enrollments.forEach { + enrollmentStore.setSyncState(it.uid(), getStateForUpdate(it.syncState())) + refreshEnrollmentAggregatedSyncState(it.uid()) + refreshEnrollmentLastUpdated(it.uid()) + } + setTeiSyncState(trackedEntityInstanceUid, getStateForUpdate) + } + } + + override fun propagateNoteCreation(note: Note?) { + if (note!!.noteType() == Note.NoteType.ENROLLMENT_NOTE) { + setEnrollmentSyncState(note.enrollment()!!, getStateForUpdate) + } else if (note.noteType() == Note.NoteType.EVENT_NOTE) { + setEventSyncState(note.event()!!, getStateForUpdate) + } + } + + override fun propagateRelationshipUpdate(item: RelationshipItem?) { + if (item != null) { + if (item.hasTrackedEntityInstance()) { + val tei = trackedEntityInstanceStore.selectByUid(item.elementUid()) + propagateTrackedEntityInstanceUpdate(tei) + } else if (item.hasEnrollment()) { + val enrollment = enrollmentStore.selectByUid(item.elementUid()) + propagateEnrollmentUpdate(enrollment) + } else if (item.hasEvent()) { + val event = eventStore.selectByUid(item.elementUid()) + propagateEventUpdate(event) + } + } + } + + private fun setTeiSyncState(trackedEntityInstanceUid: String?, getState: (State?) -> State) { + trackedEntityInstanceStore.selectByUid(trackedEntityInstanceUid!!)?.let { instance -> + trackedEntityInstanceStore.setSyncState(trackedEntityInstanceUid, getState(instance.syncState())) + propagateTrackedEntityInstanceUpdate(instance) + } + } + + private fun setEnrollmentSyncState(enrollmentUid: String, getState: (State?) -> State) { + enrollmentStore.selectByUid(enrollmentUid)?.let { enrollment -> + enrollmentStore.setSyncState(enrollmentUid, getState(enrollment.syncState())) + propagateEnrollmentUpdate(enrollment) + } + } + + private fun setEventSyncState(eventUid: String, getState: (State?) -> State) { + eventStore.selectByUid(eventUid)?.let { event -> + eventStore.setSyncState(eventUid, getState(event.syncState())) + propagateEventUpdate(event) + } + } + + private fun refreshEventLastUpdated(eventUid: String) { + eventStore.selectByUid(eventUid)?.let { event -> + val now = Date() + val updatedEvent = event.toBuilder() + .lastUpdated(getMaxDate(event.lastUpdated(), now)) + .lastUpdatedAtClient(getMaxDate(event.lastUpdatedAtClient(), now)) + .build() + eventStore.update(updatedEvent) + } + } + + private fun refreshEnrollmentLastUpdated(enrollmentUid: String) { + enrollmentStore.selectByUid(enrollmentUid)?.let { enrollment -> + val now = Date() + val updatedEnrollment = enrollment.toBuilder() + .lastUpdated(getMaxDate(enrollment.lastUpdated(), now)) + .lastUpdatedAtClient(getMaxDate(enrollment.lastUpdatedAtClient(), now)) + .build() + enrollmentStore.update(updatedEnrollment) + } + } + + private fun refreshTrackedEntityInstanceLastUpdated(trackedEntityInstanceUid: String) { + trackedEntityInstanceStore.selectByUid(trackedEntityInstanceUid)?.let { instance -> + val now = Date() + val updatedInstance = instance.toBuilder() + .lastUpdated(getMaxDate(instance.lastUpdated(), now)) + .lastUpdatedAtClient(getMaxDate(instance.lastUpdatedAtClient(), now)) + .build() + trackedEntityInstanceStore.update(updatedInstance) + } + } + + override fun resetUploadingEnrollmentAndEventStates(trackedEntityInstanceUid: String?) { + if (trackedEntityInstanceUid == null) { + return + } + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE, trackedEntityInstanceUid) + .build() + val enrollments = enrollmentStore.selectWhere(whereClause) + for (enrollment in enrollments) { + if (State.UPLOADING == enrollment.syncState()) { + enrollmentStore.setSyncState(enrollment.uid(), State.TO_UPDATE) + resetUploadingEventStates(enrollment.uid()) + } + } + } + + override fun resetUploadingEventStates(enrollmentUid: String?) { + if (enrollmentUid == null) { + return + } + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(EventTableInfo.Columns.ENROLLMENT, enrollmentUid) + .build() + val events = eventStore.selectWhere(whereClause) + for (event in events) { + if (State.UPLOADING == event.syncState()) { + eventStore.setSyncState(event.uid(), State.TO_UPDATE) + } + } + } + + private fun getMaxDate(existing: Date?, today: Date?): Date? { + return if (existing == null) { + today + } else if (today == null || existing.after(today)) { + existing + } else { + today + } + } + + private val getStateForUpdate = { existingState: State? -> + if (State.TO_POST == existingState || State.RELATIONSHIP == existingState) { + existingState + } else { + State.TO_UPDATE + } + } + + override fun refreshTrackedEntityInstanceAggregatedSyncState(trackedEntityInstanceUid: String) { + trackedEntityInstanceStore.selectByUid(trackedEntityInstanceUid)?.let { instance -> + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE, trackedEntityInstanceUid) + .build() + val enrollmentStates = enrollmentStore.selectAggregatedSyncStateWhere(whereClause) + + val relationships = relationshipStore.getRelationshipsByItem( + RelationshipHelper.teiItem(trackedEntityInstanceUid), + RelationshipConstraintType.FROM + ) + val relationshipStates = relationships.map { it.syncState()!! } + + val teiAggregatedSyncState = + getAggregatedSyncState(enrollmentStates + relationshipStates + instance.syncState()!!) + trackedEntityInstanceStore.setAggregatedSyncState(trackedEntityInstanceUid, teiAggregatedSyncState) + } + } + + override fun refreshEnrollmentAggregatedSyncState(enrollmentUid: String) { + enrollmentStore.selectByUid(enrollmentUid)?.let { enrollment -> + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(EventTableInfo.Columns.ENROLLMENT, enrollmentUid) + .build() + val eventStates = eventStore.selectAggregatedSyncStateWhere(whereClause) + + val relationships = relationshipStore.getRelationshipsByItem( + RelationshipHelper.enrollmentItem(enrollmentUid), + RelationshipConstraintType.FROM + ) + val relationshipStates = relationships.map { it.syncState()!! } + + val enrollmentAggregatedSyncState = + getAggregatedSyncState(eventStates + relationshipStates + enrollment.syncState()!!) + enrollmentStore.setAggregatedSyncState(enrollmentUid, enrollmentAggregatedSyncState) + } + } + + override fun refreshEventAggregatedSyncState(eventUid: String) { + eventStore.selectByUid(eventUid)?.let { event -> + val relationships = relationshipStore.getRelationshipsByItem( + RelationshipHelper.eventItem(eventUid), + RelationshipConstraintType.FROM + ) + val relationshipStates = relationships.map { it.syncState()!! } + + val eventAggregatedSyncState = getAggregatedSyncState(relationshipStates + event.syncState()!!) + eventStore.setAggregatedSyncState(eventUid, eventAggregatedSyncState) + } + } + + override fun refreshAggregatedSyncStates(uidHolder: DataStateUidHolder) { + uidHolder.events.forEach { + refreshEventAggregatedSyncState(it) + } + + uidHolder.enrollments.forEach { + refreshEnrollmentAggregatedSyncState(it) + } + + uidHolder.trackedEntities.forEach { + refreshTrackedEntityInstanceAggregatedSyncState(it) + } + } + + override fun getRelatedUids( + trackedEntityInstanceUids: List, + enrollmentUids: List, + eventUids: List, + relationshipUids: List + ): DataStateUidHolder { + val enrollmentsFromEvents = eventStore.selectByUids(eventUids).mapNotNull { it.enrollment() } + + val enrollments = enrollmentStore.selectByUids(enrollmentUids + enrollmentsFromEvents) + + val trackedEntitiesFromEnrollments = enrollments.mapNotNull { it.trackedEntityInstance() } + + val relationshipItems = relationshipUids.mapNotNull { + relationshipItemStore.getForRelationshipUidAndConstraintType(it, RelationshipConstraintType.FROM) + } + + return DataStateUidHolder( + events = eventUids + + relationshipItems.filter { it.hasEvent() }.map { it.elementUid() }, + enrollments = enrollmentUids + + enrollmentsFromEvents + + relationshipItems.filter { it.hasEnrollment() }.map { it.elementUid() }, + trackedEntities = trackedEntityInstanceUids + + trackedEntitiesFromEnrollments + + relationshipItems.filter { it.hasTrackedEntityInstance() }.map { it.elementUid() } + ) + } + + private fun getAggregatedSyncState(states: List): State { + return when { + states.contains(State.RELATIONSHIP) -> State.RELATIONSHIP + states.contains(State.ERROR) -> State.ERROR + states.contains(State.WARNING) -> State.WARNING + states.contains(State.UPLOADING) || + states.contains(State.TO_POST) || + states.contains(State.TO_UPDATE) -> State.TO_UPDATE + states.contains(State.SENT_VIA_SMS) -> State.SENT_VIA_SMS + states.contains(State.SYNCED_VIA_SMS) -> State.SYNCED_VIA_SMS + else -> State.SYNCED + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStateUidHolder.kt b/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStateUidHolder.kt new file mode 100644 index 0000000000..1870b270ad --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/internal/DataStateUidHolder.kt @@ -0,0 +1,7 @@ +package org.hisp.dhis.android.core.common.internal + +data class DataStateUidHolder( + val trackedEntities: List, + val enrollments: List, + val events: List +) diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/BooleanFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/BooleanFailure.kt new file mode 100644 index 0000000000..4e7f786b18 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/BooleanFailure.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class BooleanFailure : Throwable() { + object OneIsNotTrueException : BooleanFailure() + object ZeroIsNotFalseException : BooleanFailure() + object BooleanMalformedException : BooleanFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/CoordinateFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/CoordinateFailure.kt new file mode 100644 index 0000000000..c23b438fc2 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/CoordinateFailure.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class CoordinateFailure : Throwable() { + object CoordinateMalformedException : CoordinateFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/NewTrackerImporterEventPayload.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/DateFailure.kt similarity index 88% rename from core/src/main/java/org/hisp/dhis/android/core/event/internal/NewTrackerImporterEventPayload.kt rename to core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/DateFailure.kt index f4a058e5cc..ec07cf21a7 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/NewTrackerImporterEventPayload.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/DateFailure.kt @@ -1,32 +1,33 @@ -/* - * Copyright (c) 2004-2021, 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.event.internal - -import org.hisp.dhis.android.core.event.NewTrackerImporterEvent - -internal data class NewTrackerImporterEventPayload(val events: List) +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class DateFailure : Throwable() { + object ParseException : DateFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/DateTimeFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/DateTimeFailure.kt new file mode 100644 index 0000000000..4f1642b507 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/DateTimeFailure.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class DateTimeFailure : Throwable() { + object ParseException : DateTimeFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/EmailFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/EmailFailure.kt new file mode 100644 index 0000000000..cbb3493ffd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/EmailFailure.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class EmailFailure : Throwable() { + object MalformedEmailException : EmailFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerFailure.kt new file mode 100644 index 0000000000..c3367d644c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerFailure.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class IntegerFailure : Throwable() { + object NumberFormatException : IntegerFailure() + object IntegerOverflow : IntegerFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerNegativeFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerNegativeFailure.kt new file mode 100644 index 0000000000..561b523538 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerNegativeFailure.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class IntegerNegativeFailure : Throwable() { + object NumberFormatException : IntegerNegativeFailure() + object IntegerOverflow : IntegerNegativeFailure() + object ValueIsZero : IntegerNegativeFailure() + object ValueIsPositive : IntegerNegativeFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerPositiveFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerPositiveFailure.kt new file mode 100644 index 0000000000..ff3a4d2cc8 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerPositiveFailure.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class IntegerPositiveFailure : Throwable() { + object NumberFormatException : IntegerPositiveFailure() + object IntegerOverflow : IntegerPositiveFailure() + object ValueIsZero : IntegerPositiveFailure() + object ValueIsNegative : IntegerPositiveFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerZeroOrPositiveFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerZeroOrPositiveFailure.kt new file mode 100644 index 0000000000..54114c16bd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/IntegerZeroOrPositiveFailure.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class IntegerZeroOrPositiveFailure : Throwable() { + object NumberFormatException : IntegerZeroOrPositiveFailure() + object IntegerOverflow : IntegerZeroOrPositiveFailure() + object ValueIsNegative : IntegerZeroOrPositiveFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/LetterFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/LetterFailure.kt new file mode 100644 index 0000000000..f04b65f447 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/LetterFailure.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class LetterFailure : Throwable() { + object StringIsNotALetterException : LetterFailure() + object MoreThanOneLetterException : LetterFailure() + object EmptyStringException : LetterFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/LongTextFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/LongTextFailure.kt new file mode 100644 index 0000000000..adba1bb8d6 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/LongTextFailure.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class LongTextFailure : Throwable() { + // Long texts do not need validation and cannot produce errors +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/NumberFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/NumberFailure.kt new file mode 100644 index 0000000000..681cdd7fda --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/NumberFailure.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class NumberFailure : Throwable() { + object ScientificNotationException : NumberFailure() + object NumberFormatException : NumberFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/PercentageFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/PercentageFailure.kt new file mode 100644 index 0000000000..9aceb4e551 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/PercentageFailure.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class PercentageFailure : Throwable() { + object NumberFormatException : PercentageFailure() + object ValueGreaterThan100 : PercentageFailure() + object ValueIsNegative : PercentageFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/PhoneNumberFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/PhoneNumberFailure.kt new file mode 100644 index 0000000000..4b291084dd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/PhoneNumberFailure.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class PhoneNumberFailure : Throwable() { + object MalformedPhoneNumberException : PhoneNumberFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TextFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TextFailure.kt new file mode 100644 index 0000000000..d9c05df377 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TextFailure.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class TextFailure : Throwable() { + object TooLargeTextException : TextFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TimeFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TimeFailure.kt new file mode 100644 index 0000000000..aa3fb58e59 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TimeFailure.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class TimeFailure : Throwable() { + object ParseException : TimeFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TrueOnlyFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TrueOnlyFailure.kt new file mode 100644 index 0000000000..1112c01fff --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/TrueOnlyFailure.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class TrueOnlyFailure : Throwable() { + object OneIsNotTrueException : TrueOnlyFailure() + object FalseIsNotAValidValueException : TrueOnlyFailure() + object BooleanMalformedException : TrueOnlyFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UidFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UidFailure.kt new file mode 100644 index 0000000000..7e64fc6c22 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UidFailure.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class UidFailure : Throwable() { + object MoreThanElevenCharsException : UidFailure() + object LessThanElevenCharsException : UidFailure() + object MalformedUidException : UidFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UnitIntervalFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UnitIntervalFailure.kt new file mode 100644 index 0000000000..90e8ed6061 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UnitIntervalFailure.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class UnitIntervalFailure : Throwable() { + object ScientificNotationException : UnitIntervalFailure() + object NumberFormatException : UnitIntervalFailure() + object GreaterThanOneException : UnitIntervalFailure() + object SmallerThanZeroException : UnitIntervalFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UrlFailure.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UrlFailure.kt new file mode 100644 index 0000000000..88c5af8a85 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/failures/UrlFailure.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.failures + +sealed class UrlFailure : Throwable() { + object MalformedUrlException : UrlFailure() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/BooleanValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/BooleanValidator.kt new file mode 100644 index 0000000000..bee08e9d5d --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/BooleanValidator.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.BooleanFailure + +object BooleanValidator : ValueTypeValidator { + + override fun validate(value: String): Result { + return when (value) { + "0" -> { + Result.Failure(BooleanFailure.ZeroIsNotFalseException) + } + "1" -> { + Result.Failure(BooleanFailure.OneIsNotTrueException) + } + "true", "false" -> { + Result.Success(value) + } + else -> { + Result.Failure(BooleanFailure.BooleanMalformedException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/CoordinateValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/CoordinateValidator.kt new file mode 100644 index 0000000000..aeba8d1e50 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/CoordinateValidator.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.CoordinateFailure + +object CoordinateValidator : ValueTypeValidator { + + private val COORDINATE_PATTERN = + "^\\[[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?),\\s*[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)]\$" + .toRegex() + + override fun validate(value: String): Result { + return when (value.matches(COORDINATE_PATTERN)) { + true -> { + Result.Success(value) + } + else -> { + Result.Failure(CoordinateFailure.CoordinateMalformedException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateTimeValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateTimeValidator.kt new file mode 100644 index 0000000000..c488e99032 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateTimeValidator.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.DateTimeFailure + +object DateTimeValidator : ValueTypeValidator { + + private val DATE_TIME_PATTERN = + "^(\\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01]))T(([0-1]?[0-9]|2[0-3]):[0-5][0-9])\$".toRegex() + + override fun validate(value: String): Result { + return when (value.matches(DATE_TIME_PATTERN)) { + true -> { + Result.Success(value) + } + else -> { + Result.Failure(DateTimeFailure.ParseException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateValidator.kt new file mode 100644 index 0000000000..21b211785e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateValidator.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.DateFailure + +object DateValidator : ValueTypeValidator { + + private val DATE_PATTERN = "^\\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])\$".toRegex() + + override fun validate(value: String): Result { + return when (value.matches(DATE_PATTERN)) { + true -> { + Result.Success(value) + } + else -> { + Result.Failure(DateFailure.ParseException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/EmailValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/EmailValidator.kt new file mode 100644 index 0000000000..428783b844 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/EmailValidator.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.EmailFailure + +object EmailValidator : ValueTypeValidator { + + private val EMAIL_PATTERN = ( + "^[a-zA-Z0-9.!#\$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?" + + "(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\$" + ).toRegex() + + override fun validate(value: String): Result { + return when (value.matches(EMAIL_PATTERN)) { + true -> { + Result.Success(value) + } + else -> { + Result.Failure(EmailFailure.MalformedEmailException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerNegativeValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerNegativeValidator.kt new file mode 100644 index 0000000000..c3ac66d395 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerNegativeValidator.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerNegativeFailure + +object IntegerNegativeValidator : IntegerValidatorBase() { + override fun validate(value: String): Result { + return try { + val convertedValue = value.toInt() + when { + convertedValue == 0 -> { + Result.Failure(IntegerNegativeFailure.ValueIsZero) + } + convertedValue > 0 -> { + Result.Failure(IntegerNegativeFailure.ValueIsPositive) + } + else -> { + Result.Success(value) + } + } + } catch (e: NumberFormatException) { + catchOverflowFailure( + value, IntegerNegativeFailure.IntegerOverflow, + IntegerNegativeFailure.NumberFormatException + ) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerPositiveValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerPositiveValidator.kt new file mode 100644 index 0000000000..c7c20d631d --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerPositiveValidator.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerPositiveFailure + +object IntegerPositiveValidator : IntegerValidatorBase() { + override fun validate(value: String): Result { + return try { + val convertedValue = value.toInt() + when { + convertedValue == 0 -> { + Result.Failure(IntegerPositiveFailure.ValueIsZero) + } + convertedValue < 0 -> { + Result.Failure(IntegerPositiveFailure.ValueIsNegative) + } + else -> { + Result.Success(value) + } + } + } catch (e: NumberFormatException) { + catchOverflowFailure( + value, IntegerPositiveFailure.IntegerOverflow, + IntegerPositiveFailure.NumberFormatException + ) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidator.kt new file mode 100644 index 0000000000..d0e91d562a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidator.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerFailure + +object IntegerValidator : IntegerValidatorBase() { + override fun validate(value: String): Result { + return try { + value.toInt() + Result.Success(value) + } catch (e: NumberFormatException) { + catchOverflowFailure(value, IntegerFailure.IntegerOverflow, IntegerFailure.NumberFormatException) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidatorBase.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidatorBase.kt new file mode 100644 index 0000000000..efe23bc9c0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidatorBase.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result + +abstract class IntegerValidatorBase : ValueTypeValidator { + fun catchOverflowFailure(value: String, overflowFailure: T, formatFailure: T): Result { + return try { + val convertedValue = value.toLong() + if (convertedValue > Integer.MAX_VALUE || convertedValue < Integer.MIN_VALUE) { + return Result.Failure(overflowFailure) + } + Result.Failure(formatFailure) + } catch (e: NumberFormatException) { + Result.Failure(formatFailure) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerZeroOrPositiveValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerZeroOrPositiveValidator.kt new file mode 100644 index 0000000000..d6502d6893 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerZeroOrPositiveValidator.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerZeroOrPositiveFailure + +object IntegerZeroOrPositiveValidator : IntegerValidatorBase() { + override fun validate(value: String): Result { + return try { + val convertedValue = value.toInt() + when { + convertedValue < 0 -> { + Result.Failure(IntegerZeroOrPositiveFailure.ValueIsNegative) + } + else -> { + Result.Success(value) + } + } + } catch (e: NumberFormatException) { + catchOverflowFailure( + value, IntegerZeroOrPositiveFailure.IntegerOverflow, + IntegerZeroOrPositiveFailure.NumberFormatException + ) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LetterValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LetterValidator.kt new file mode 100644 index 0000000000..fe587712cc --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LetterValidator.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.LetterFailure + +object LetterValidator : ValueTypeValidator { + + private val LETTER_PATTERN = "[a-zA-Z]".toRegex() + + override fun validate(value: String): Result { + val charArray = value.toCharArray() + return when { + charArray.isEmpty() -> { + Result.Failure(LetterFailure.EmptyStringException) + } + charArray.size > 1 -> { + Result.Failure(LetterFailure.MoreThanOneLetterException) + } + value.matches(LETTER_PATTERN) -> { + Result.Success(value) + } + else -> { + Result.Failure(LetterFailure.StringIsNotALetterException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LongTextValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LongTextValidator.kt new file mode 100644 index 0000000000..980784d165 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LongTextValidator.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.LongTextFailure + +object LongTextValidator : ValueTypeValidator { + override fun validate(value: String): Result { + return Result.Success(value) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/NumberValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/NumberValidator.kt new file mode 100644 index 0000000000..312c54d6be --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/NumberValidator.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.NumberFailure + +object NumberValidator : ValueTypeValidator { + + val SCIENTIFIC_NOTATION_PATTERN = "[+\\-]?(?:0|[1-9]\\d*)(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)".toRegex() + + override fun validate(value: String): Result { + return try { + value.toDouble() + if (value.matches(SCIENTIFIC_NOTATION_PATTERN)) { + Result.Failure(NumberFailure.ScientificNotationException) + } else { + Result.Success(value) + } + } catch (e: NumberFormatException) { + Result.Failure(NumberFailure.NumberFormatException) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PercentageValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PercentageValidator.kt new file mode 100644 index 0000000000..016a84f848 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PercentageValidator.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.PercentageFailure + +object PercentageValidator : ValueTypeValidator { + private const val ONE_HUNDRED_PERCENT = 100 + private const val ZERO_PERCENT = 0 + override fun validate(value: String): Result { + return try { + val convertedValue = value.toInt() + when { + convertedValue > ONE_HUNDRED_PERCENT -> { + Result.Failure(PercentageFailure.ValueGreaterThan100) + } + convertedValue < ZERO_PERCENT -> { + Result.Failure(PercentageFailure.ValueIsNegative) + } + else -> { + Result.Success(value) + } + } + } catch (e: NumberFormatException) { + Result.Failure(PercentageFailure.NumberFormatException) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PhoneNumberValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PhoneNumberValidator.kt new file mode 100644 index 0000000000..f75b68c4f3 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PhoneNumberValidator.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.PhoneNumberFailure + +object PhoneNumberValidator : ValueTypeValidator { + + private val PHONE_PATTERN = "^[0-9+(][0-9+\\-() ]{2,18}[0-9]$".toRegex() + + override fun validate(value: String): Result { + return when (value.matches(PHONE_PATTERN)) { + true -> { + Result.Success(value) + } + else -> { + Result.Failure(PhoneNumberFailure.MalformedPhoneNumberException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TextValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TextValidator.kt new file mode 100644 index 0000000000..c3856fd5a7 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TextValidator.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.TextFailure + +object TextValidator : ValueTypeValidator { + private const val MAX_CHARS = 50000 + + override fun validate(value: String): Result { + return if (value.count() > MAX_CHARS) { + Result.Failure(TextFailure.TooLargeTextException) + } else { + Result.Success(value) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TimeValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TimeValidator.kt new file mode 100644 index 0000000000..dcf8b3bd07 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TimeValidator.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.TimeFailure + +object TimeValidator : ValueTypeValidator { + + private val TIME_PATTERN = "^([0-1]?[0-9]|2[0-3]):[0-5][0-9]\$".toRegex() + + override fun validate(value: String): Result { + return when { + value.matches(TIME_PATTERN) -> { + Result.Success(value) + } + else -> { + Result.Failure(TimeFailure.ParseException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TrueOnlyValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TrueOnlyValidator.kt new file mode 100644 index 0000000000..5882d69cb1 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TrueOnlyValidator.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.TrueOnlyFailure + +object TrueOnlyValidator : ValueTypeValidator { + + override fun validate(value: String): Result { + return when (value) { + "false" -> { + Result.Failure(TrueOnlyFailure.FalseIsNotAValidValueException) + } + "1" -> { + Result.Failure(TrueOnlyFailure.OneIsNotTrueException) + } + "true" -> { + Result.Success(value) + } + else -> { + Result.Failure(TrueOnlyFailure.BooleanMalformedException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UidValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UidValidator.kt new file mode 100644 index 0000000000..487dc3d447 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UidValidator.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.UidFailure + +object UidValidator : ValueTypeValidator { + + private val UID_PATTERN = "^[a-zA-Z0-9]{11}\$".toRegex() + private const val NUMBER_OF_UID_CHARS = 11 + + override fun validate(value: String): Result { + return when { + value.matches(UID_PATTERN) -> { + Result.Success(value) + } + value.length < NUMBER_OF_UID_CHARS -> { + Result.Failure(UidFailure.LessThanElevenCharsException) + } + value.length > NUMBER_OF_UID_CHARS -> { + Result.Failure(UidFailure.MoreThanElevenCharsException) + } + else -> { + Result.Failure(UidFailure.MalformedUidException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UnitIntervalValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UnitIntervalValidator.kt new file mode 100644 index 0000000000..606a4d1f4e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UnitIntervalValidator.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.UnitIntervalFailure + +object UnitIntervalValidator : ValueTypeValidator { + + override fun validate(value: String): Result { + return try { + val convertedValue = value.toDouble() + when { + value.matches(NumberValidator.SCIENTIFIC_NOTATION_PATTERN) -> { + Result.Failure(UnitIntervalFailure.ScientificNotationException) + } + convertedValue > 1 -> { + Result.Failure(UnitIntervalFailure.GreaterThanOneException) + } + convertedValue < 0 -> { + Result.Failure(UnitIntervalFailure.SmallerThanZeroException) + } + else -> { + Result.Success(value) + } + } + } catch (e: NumberFormatException) { + Result.Failure(UnitIntervalFailure.NumberFormatException) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UrlValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UrlValidator.kt new file mode 100644 index 0000000000..44fa402233 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UrlValidator.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.valuetype.validation.failures.UrlFailure + +object UrlValidator : ValueTypeValidator { + + private val URL_PATTERN = "^(http|https):\\/\\/[a-z0-9]+([-.]{1}[a-z0-9]+)*\\.[a-z]{2,6}(:[0-9]{1,5})?(\\/.*)?\$" + .toRegex() + + override fun validate(value: String): Result { + return when (value.matches(URL_PATTERN)) { + true -> { + Result.Success(value) + } + else -> { + Result.Failure(UrlFailure.MalformedUrlException) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValueTypeValidator.kt b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValueTypeValidator.kt new file mode 100644 index 0000000000..eeced85940 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValueTypeValidator.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.arch.helpers.Result + +interface ValueTypeValidator { + fun validate(value: String): Result +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistration.java b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistration.java index 68e3e2f342..a9cfe4e1c7 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistration.java +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistration.java @@ -94,7 +94,7 @@ public static Builder builder() { public abstract static class Builder extends BaseDeletableDataObject.Builder { public Builder() { - state(State.SYNCED); + syncState(State.SYNCED); } public abstract Builder period(@NonNull String period); diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationCollectionRepository.java index 9ecf0c2482..e9928386d3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationCollectionRepository.java @@ -99,7 +99,7 @@ public DataSetCompleteRegistrationObjectRepository value(final String period, @Override public Observable upload() { return Observable.fromCallable(() -> - byState().in(State.uploadableStatesIncludingError()).blockingGetWithoutChildren() + bySyncState().in(State.uploadableStatesIncludingError()).blockingGetWithoutChildren() ).flatMap(postCall::uploadDataSetCompleteRegistrations); } @@ -137,8 +137,18 @@ public BooleanFilterConnector b return cf.bool(Columns.DELETED); } + /** + * @deprecated Use {@link #bySyncState()} instead. + * + * @return + */ + @Deprecated public EnumFilterConnector byState() { - return cf.enumC(Columns.STATE); + return bySyncState(); + } + + public EnumFilterConnector bySyncState() { + return cf.enumC(Columns.SYNC_STATE); } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationObjectRepository.java b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationObjectRepository.java index ab55858a12..270cd8e396 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationObjectRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationObjectRepository.java @@ -100,13 +100,14 @@ public void blockingSet() { .attributeOptionCombo(attributeOptionCombo) .date(new Date()) .storedBy(username) - .state(State.TO_POST) + .syncState(State.TO_POST) .deleted(false) .build()); } else { DataSetCompleteRegistration newRecord = dataSetCompleteRegistration.toBuilder() .deleted(false) - .state(dataSetCompleteRegistration.state() == State.TO_POST ? State.TO_POST : State.TO_UPDATE) + .syncState(dataSetCompleteRegistration.syncState() == State.TO_POST ? + State.TO_POST : State.TO_UPDATE) .build(); dataSetCompleteRegistrationStore.updateWhere(newRecord); } @@ -129,12 +130,12 @@ public void blockingDelete() throws D2Error { "because no longer exists") .build(); } else { - if (dataSetCompleteRegistration.state() == State.TO_POST) { + if (dataSetCompleteRegistration.syncState() == State.TO_POST) { dataSetCompleteRegistrationStore.deleteWhere(dataSetCompleteRegistration); } else { DataSetCompleteRegistration deletedRecord = dataSetCompleteRegistration.toBuilder() .deleted(true) - .state(State.TO_UPDATE) + .syncState(State.TO_UPDATE) .build(); dataSetCompleteRegistrationStore.updateWhere(deletedRecord); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationTableInfo.java index 559be1ccbc..70daf587ea 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataSetCompleteRegistrationTableInfo.java @@ -68,7 +68,7 @@ public String[] all() { ATTRIBUTE_OPTION_COMBO, DATE, STORED_BY, - STATE, + SYNC_STATE, DELETED); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetCompleteRegistrationStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetCompleteRegistrationStoreImpl.java index ab11686c66..d01f7d8b75 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetCompleteRegistrationStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetCompleteRegistrationStoreImpl.java @@ -52,7 +52,7 @@ final class DataSetCompleteRegistrationStoreImpl extends w.bind(4, dataSetCompleteRegistration.attributeOptionCombo()); w.bind(5, dataSetCompleteRegistration.date()); w.bind(6, dataSetCompleteRegistration.storedBy()); - w.bind(7, dataSetCompleteRegistration.state()); + w.bind(7, dataSetCompleteRegistration.syncState()); w.bind(8, dataSetCompleteRegistration.deleted()); }; @@ -94,7 +94,7 @@ public static DataSetCompleteRegistrationStoreImpl create(DatabaseAdapter databa @Override public void setState(DataSetCompleteRegistration dataSetCompleteRegistration, State newState) { DataSetCompleteRegistration updatedDataSetCompleteRegistration - = dataSetCompleteRegistration.toBuilder().state(newState).build(); + = dataSetCompleteRegistration.toBuilder().syncState(newState).build(); updateWhere(updatedDataSetCompleteRegistration); } @@ -123,7 +123,7 @@ public boolean removeNotPresentAndSynced(Collection dataSetUids, "%" + rootOrgunitUid + "%"); whereClause.appendInSubQuery(DataSetCompleteRegistrationTableInfo.Columns.ORGANISATION_UNIT, subQuery); - whereClause.appendKeyStringValue(DataSetCompleteRegistrationTableInfo.Columns.STATE, State.SYNCED); + whereClause.appendKeyStringValue(DataSetCompleteRegistrationTableInfo.Columns.SYNC_STATE, State.SYNCED); return deleteWhere(whereClause.build()); } @@ -137,7 +137,7 @@ public boolean isBeingUpload(DataSetCompleteRegistration dscr) { dscr.organisationUnit()) .appendKeyStringValue(DataSetCompleteRegistrationTableInfo.Columns.ATTRIBUTE_OPTION_COMBO, dscr.attributeOptionCombo()) - .appendKeyStringValue(DataSetCompleteRegistrationTableInfo.Columns.STATE, State.UPLOADING) + .appendKeyStringValue(DataSetCompleteRegistrationTableInfo.Columns.SYNC_STATE, State.UPLOADING) .build(); return selectWhere(whereClause).size() > 0; } diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetInstanceSQLStatementBuilder.java b/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetInstanceSQLStatementBuilder.java index 196ef89c8f..a574a612a5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetInstanceSQLStatementBuilder.java +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetInstanceSQLStatementBuilder.java @@ -131,8 +131,8 @@ public class DataSetInstanceSQLStatementBuilder implements ReadOnlySQLStatementB private static final String LAST_UPDATED = "MAX(" + LAST_UPDATED_VALUES + ", COALESCE(" + COMPLETION_DATE + ", 0))"; - private static final String VALUE_STATE = DATAVALUE_TABLE_ALIAS + "." + DataColumns.STATE; - private static final String COMPLETION_STATE = COMPLETE_TABLE_ALIAS + "." + DataColumns.STATE; + private static final String VALUE_STATE = DATAVALUE_TABLE_ALIAS + "." + DataColumns.SYNC_STATE; + private static final String COMPLETION_STATE = COMPLETE_TABLE_ALIAS + "." + DataColumns.SYNC_STATE; private static final String SELECT_VALUE_STATE_ORDERING = " MAX(CASE " + "WHEN " + VALUE_STATE + " IN ('" + State.SYNCED + "','" + State.SYNCED_VIA_SMS + "') THEN 1 " + diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetInstanceSummarySQLStatementBuilder.java b/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetInstanceSummarySQLStatementBuilder.java index 04ec96f363..c12522a81e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetInstanceSummarySQLStatementBuilder.java +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetInstanceSummarySQLStatementBuilder.java @@ -29,7 +29,6 @@ package org.hisp.dhis.android.core.dataset.internal; import org.hisp.dhis.android.core.arch.db.sqlorder.internal.SQLOrderType; -import org.hisp.dhis.android.core.common.DataColumns; import org.hisp.dhis.android.core.common.DeletableDataColumns; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.dataset.DataSetTableInfo; @@ -44,7 +43,7 @@ public class DataSetInstanceSummarySQLStatementBuilder extends DataSetInstanceSQ private static final String DS_LIST_TABLE_ALIAS = "dslist"; private static final String DS_INSTANCE_ALIAS = "dsinstance"; - private static final String STATE = DataColumns.STATE; + private static final String STATE = STATE_ALIAS; private static final String SELECT_STATE_ORDERING = " MAX(CASE " + "WHEN " + STATE + " IN ('" + State.SYNCED + "','" + State.SYNCED_VIA_SMS + "') THEN 1 " + diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/DataStoreModule.kt b/core/src/main/java/org/hisp/dhis/android/core/datastore/DataStoreModule.kt new file mode 100644 index 0000000000..fbab9349f7 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/DataStoreModule.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004-2021, 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.datastore + +interface DataStoreModule { + fun localDataStore(): LocalDataStoreCollectionRepository +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/KeyValuePair.java b/core/src/main/java/org/hisp/dhis/android/core/datastore/KeyValuePair.java new file mode 100644 index 0000000000..1a9bc3f43f --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/KeyValuePair.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004-2021, 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.datastore; + +import android.database.Cursor; + +import androidx.annotation.Nullable; + +import com.google.auto.value.AutoValue; + +import org.hisp.dhis.android.core.common.CoreObject; + +@AutoValue +public abstract class KeyValuePair implements CoreObject { + + @Nullable + public abstract String key(); + + @Nullable + public abstract String value(); + + public static KeyValuePair create(Cursor cursor) { + return $AutoValue_KeyValuePair.createFromCursor(cursor); + } + + public abstract Builder toBuilder(); + + public static Builder builder() { + return new AutoValue_KeyValuePair.Builder(); + } + + @AutoValue.Builder + public static abstract class Builder { + public abstract Builder id(Long id); + + public abstract Builder key(String key); + + public abstract Builder value(String value); + + public abstract KeyValuePair build(); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreCollectionRepository.java new file mode 100644 index 0000000000..29a1f03085 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreCollectionRepository.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2004-2021, 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.datastore; + +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore; +import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; +import org.hisp.dhis.android.core.arch.repositories.collection.internal.ReadOnlyCollectionRepositoryImpl; +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 java.util.Map; + +import javax.inject.Inject; + +import dagger.Reusable; + +@Reusable +public final class LocalDataStoreCollectionRepository + extends ReadOnlyCollectionRepositoryImpl { + + private final ObjectWithoutUidStore store; + + @Inject + LocalDataStoreCollectionRepository(final ObjectWithoutUidStore store, + final Map> childrenAppenders, + final RepositoryScope scope) { + super(store, childrenAppenders, scope, new FilterConnectorFactory<>(scope, + s -> new LocalDataStoreCollectionRepository(store, childrenAppenders, s))); + this.store = store; + } + + public LocalDataStoreObjectRepository value(String key) { + RepositoryScope updatedScope = byKey().eq(key).scope; + return new LocalDataStoreObjectRepository(store, childrenAppenders, updatedScope, key); + } + + public StringFilterConnector byKey() { + return cf.string(LocalDataStoreTableInfo.Columns.KEY); + } + + public StringFilterConnector byValue() { + return cf.string(LocalDataStoreTableInfo.Columns.VALUE); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreObjectRepository.java b/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreObjectRepository.java new file mode 100644 index 0000000000..58b3d61864 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreObjectRepository.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004-2021, 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.datastore; + +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore; +import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; +import org.hisp.dhis.android.core.arch.repositories.object.ReadWriteValueObjectRepository; +import org.hisp.dhis.android.core.arch.repositories.object.internal.ReadWriteWithValueObjectRepositoryImpl; +import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope; +import org.hisp.dhis.android.core.maintenance.D2Error; + +import java.util.Map; + +import io.reactivex.Completable; + +public final class LocalDataStoreObjectRepository + extends ReadWriteWithValueObjectRepositoryImpl + implements ReadWriteValueObjectRepository { + + private final String key; + + LocalDataStoreObjectRepository( + final ObjectWithoutUidStore store, + final Map> childrenAppenders, + final RepositoryScope scope, + final String key) { + super(store, childrenAppenders, scope, s -> + new LocalDataStoreObjectRepository(store, childrenAppenders, s, key)); + this.key = key; + } + + @Override + public Completable set(String value) { + return Completable.fromAction(() -> blockingSet(value)); + } + + public void blockingSet(String value) throws D2Error { + KeyValuePair pair = KeyValuePair.builder().key(key).value(value).build(); + setObject(pair); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreTableInfo.kt new file mode 100644 index 0000000000..83ea923fc3 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/LocalDataStoreTableInfo.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2021, 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.datastore + +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 LocalDataStoreTableInfo { + + @JvmField + val TABLE_INFO: TableInfo = object : TableInfo() { + override fun name(): String { + return "LocalDataStore" + } + + override fun columns(): CoreColumns { + return Columns() + } + } + + class Columns : CoreColumns() { + override fun all(): Array { + return CollectionsHelper.appendInNewArray( + super.all(), + KEY, + VALUE + ) + } + + override fun whereUpdate(): Array { + return CollectionsHelper.appendInNewArray( + super.all(), + KEY + ) + } + + companion object { + const val KEY = "key" + const val VALUE = "value" + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/DataStoreModuleImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/DataStoreModuleImpl.kt new file mode 100644 index 0000000000..27c5f38e27 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/DataStoreModuleImpl.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-2021, 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.datastore.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.datastore.DataStoreModule +import org.hisp.dhis.android.core.datastore.LocalDataStoreCollectionRepository + +@Reusable +class DataStoreModuleImpl @Inject internal constructor( + private val localDataStore: LocalDataStoreCollectionRepository +) : + DataStoreModule { + override fun localDataStore(): LocalDataStoreCollectionRepository { + return localDataStore + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/DataStorePackageDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/DataStorePackageDIModule.kt new file mode 100644 index 0000000000..230924bf12 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/DataStorePackageDIModule.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004-2021, 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.datastore.internal + +import dagger.Module +import dagger.Provides +import dagger.Reusable +import org.hisp.dhis.android.core.datastore.DataStoreModule + +@Module(includes = [LocalDataStoreEntityDIModule::class]) +class DataStorePackageDIModule { + @Provides + @Reusable + fun module(impl: DataStoreModuleImpl): DataStoreModule { + return impl + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreEntityDIModule.kt new file mode 100644 index 0000000000..c02d005abd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreEntityDIModule.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2021, 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.datastore.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.ObjectWithoutUidStore +import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender +import org.hisp.dhis.android.core.datastore.KeyValuePair + +@Module +internal class LocalDataStoreEntityDIModule { + + @Provides + @Reusable + fun store(databaseAdapter: DatabaseAdapter): ObjectWithoutUidStore { + return LocalDataStoreStore.create(databaseAdapter) + } + + @Provides + @Reusable + fun childrenAppenders(databaseAdapter: DatabaseAdapter): Map> { + return emptyMap() + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreModuleWiper.kt b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreModuleWiper.kt new file mode 100644 index 0000000000..a863c7cf08 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreModuleWiper.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004-2021, 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.datastore.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.datastore.LocalDataStoreTableInfo +import org.hisp.dhis.android.core.wipe.internal.ModuleWiper +import org.hisp.dhis.android.core.wipe.internal.TableWiper + +@Reusable +class LocalDataStoreModuleWiper @Inject internal constructor(private val tableWiper: TableWiper) : ModuleWiper { + override fun wipeMetadata() { + // No metadata to wipe + } + + override fun wipeData() { + tableWiper.wipeTables( + LocalDataStoreTableInfo.TABLE_INFO + ) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreStore.kt b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreStore.kt new file mode 100644 index 0000000000..e016ca61b4 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datastore/internal/LocalDataStoreStore.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2004-2021, 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.datastore.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.StatementBinder +import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapper +import org.hisp.dhis.android.core.arch.db.stores.binders.internal.WhereStatementBinder +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory.objectWithoutUidStore +import org.hisp.dhis.android.core.datastore.KeyValuePair +import org.hisp.dhis.android.core.datastore.LocalDataStoreTableInfo + +@Suppress("MagicNumber") +internal object LocalDataStoreStore { + + private val BINDER: StatementBinder = StatementBinder { o, w -> + w.bind(1, o.key()) + w.bind(2, o.value()) + } + + private val WHERE_UPDATE_BINDER = WhereStatementBinder { o: KeyValuePair, w: StatementWrapper -> + w.bind(3, o.key()) + } + + private val WHERE_DELETE_BINDER = WhereStatementBinder { o: KeyValuePair, w: StatementWrapper -> + w.bind(1, o.key()) + } + + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): ObjectWithoutUidStore { + return objectWithoutUidStore( + databaseAdapter, + LocalDataStoreTableInfo.TABLE_INFO, + BINDER, + WHERE_UPDATE_BINDER, + WHERE_DELETE_BINDER + ) { cursor: Cursor -> KeyValuePair.create(cursor) } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValue.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValue.java index 243666f3c3..333441cd14 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValue.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValue.java @@ -113,7 +113,7 @@ public static DataValue.Builder builder() { public abstract static class Builder extends BaseDeletableDataObject.Builder { public Builder() { - state(State.SYNCED); + syncState(State.SYNCED); } public abstract DataValue.Builder dataElement(@NonNull String dataElement); diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueCollectionRepository.java index 5145f0340e..79d2e35f04 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueCollectionRepository.java @@ -73,7 +73,7 @@ public final class DataValueCollectionRepository @Override public Observable upload() { return Observable.fromCallable(() -> - byState().in(State.uploadableStatesIncludingError()).blockingGetWithoutChildren() + bySyncState().in(State.uploadableStatesIncludingError()).blockingGetWithoutChildren() ).flatMap(postCall::uploadDataValues); } @@ -147,8 +147,18 @@ public BooleanFilterConnector byFollowUp() { return cf.bool(Columns.FOLLOW_UP); } + /** + * @deprecated Use {@link #bySyncState()} instead. + * + * @return + */ + @Deprecated public EnumFilterConnector byState() { - return cf.enumC(Columns.STATE); + return bySyncState(); + } + + public EnumFilterConnector bySyncState() { + return cf.enumC(Columns.SYNC_STATE); } public BooleanFilterConnector byDeleted() { diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflict.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflict.java new file mode 100644 index 0000000000..a2b19de9d3 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflict.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue; + +import android.database.Cursor; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.gabrielittner.auto.value.cursor.ColumnAdapter; +import com.google.auto.value.AutoValue; + +import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbDateColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.ImportStatusColumnAdapter; +import org.hisp.dhis.android.core.common.BaseObject; +import org.hisp.dhis.android.core.imports.ImportStatus; + +import java.util.Date; + +@AutoValue +public abstract class DataValueConflict extends BaseObject { + + @Nullable + public abstract String conflict(); + + @Nullable + public abstract String value(); + + @Nullable + public abstract String attributeOptionCombo(); + + @Nullable + public abstract String categoryOptionCombo(); + + @Nullable + public abstract String dataElement(); + + @Nullable + public abstract String period(); + + @Nullable + public abstract String orgUnit(); + + @Nullable + public abstract String errorCode(); + + @Nullable + public abstract String displayDescription(); + + @Nullable + @ColumnAdapter(ImportStatusColumnAdapter.class) + public abstract ImportStatus status(); + + @Nullable + @ColumnAdapter(DbDateColumnAdapter.class) + public abstract Date created(); + + @NonNull + public static DataValueConflict create(Cursor cursor) { + return AutoValue_DataValueConflict.createFromCursor(cursor); + } + + public static Builder builder() { + return new $$AutoValue_DataValueConflict.Builder(); + } + + public abstract Builder toBuilder(); + + @AutoValue.Builder + public static abstract class Builder extends BaseObject.Builder { + public abstract Builder conflict(String conflict); + + public abstract Builder value(String value); + + public abstract Builder attributeOptionCombo(String attributeOptionCombo); + + public abstract Builder categoryOptionCombo(String categoryOptionCombo); + + public abstract Builder dataElement(String dataElement); + + public abstract Builder period(String period); + + public abstract Builder orgUnit(String orgUnit); + + public abstract Builder errorCode(String errorCode); + + public abstract Builder displayDescription(String displayDescription); + + public abstract Builder status(ImportStatus status); + + public abstract Builder created(Date created); + + public abstract DataValueConflict build(); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictTableInfo.kt new file mode 100644 index 0000000000..6b71154021 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictTableInfo.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue + +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 DataValueConflictTableInfo { + + @JvmField + val TABLE_INFO: TableInfo = object : TableInfo() { + override fun name(): String { + return "DataValueConflict" + } + + override fun columns(): CoreColumns { + return Columns() + } + } + + class Columns : CoreColumns() { + override fun all(): Array { + return CollectionsHelper.appendInNewArray( + super.all(), + CONFLICT, + VALUE, + ATTRIBUTE_OPTION_COMBO, + CATEGORY_OPTION_COMBO, + DATA_ELEMENT, + PERIOD, + ORG_UNIT, + ERROR_CODE, + DISPLAY_DESCRIPTION, + STATUS, + CREATED + ) + } + + companion object { + const val CONFLICT = "conflict" + const val VALUE = "value" + const val ATTRIBUTE_OPTION_COMBO = "attributeOptionCombo" + const val CATEGORY_OPTION_COMBO = "categoryOptionCombo" + const val DATA_ELEMENT = "dataElement" + const val PERIOD = "period" + const val ORG_UNIT = "orgUnit" + const val ERROR_CODE = "errorCode" + const val DISPLAY_DESCRIPTION = "displayDescription" + const val STATUS = "status" + const val CREATED = "created" + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueObjectRepository.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueObjectRepository.java index 92eed12f76..a7c511d591 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueObjectRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueObjectRepository.java @@ -95,10 +95,10 @@ public Completable delete() { @Override public void blockingDelete() throws D2Error { DataValue dataValue = blockingGetWithoutChildren(); - if (dataValue.state() == State.TO_POST) { + if (dataValue.syncState() == State.TO_POST) { super.delete(dataValue); } else { - setObject(dataValue.toBuilder().deleted(true).state(State.TO_UPDATE).build()); + setObject(dataValue.toBuilder().deleted(true).syncState(State.TO_UPDATE).build()); } } @@ -106,13 +106,13 @@ private DataValue.Builder setBuilder() { Date date = new Date(); if (blockingExists()) { DataValue dataValue = blockingGetWithoutChildren(); - State state = dataValue.state() == State.TO_POST ? State.TO_POST : State.TO_UPDATE; + State state = dataValue.syncState() == State.TO_POST ? State.TO_POST : State.TO_UPDATE; return dataValue.toBuilder() - .state(state) + .syncState(state) .lastUpdated(date); } else { return DataValue.builder() - .state(State.TO_POST) + .syncState(State.TO_POST) .created(date) .lastUpdated(date) .followUp(Boolean.FALSE) diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueTableInfo.java index 37fa7f955e..f6094575e5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueTableInfo.java @@ -78,7 +78,7 @@ public String[] all() { LAST_UPDATED, COMMENT, FOLLOW_UP, - STATE, + SYNC_STATE, DELETED); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictDIModule.kt new file mode 100644 index 0000000000..b199f5c451 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictDIModule.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.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.ObjectStore +import org.hisp.dhis.android.core.datavalue.DataValueConflict + +@Module +internal class DataValueConflictDIModule { + + @Provides + @Reusable + fun store(databaseAdapter: DatabaseAdapter): ObjectStore { + return DataValueConflictStore.create(databaseAdapter) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictParser.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictParser.kt new file mode 100644 index 0000000000..1a23229708 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictParser.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.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.dataelement.DataElement +import org.hisp.dhis.android.core.dataset.DataSet +import org.hisp.dhis.android.core.datavalue.DataValue +import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.datavalue.internal.conflicts.InvalidDataElementTypeConflict +import org.hisp.dhis.android.core.datavalue.internal.conflicts.PastExpiryDateConflict +import org.hisp.dhis.android.core.datavalue.internal.conflicts.PeriodAfterLatestOpenFutureConflict +import org.hisp.dhis.android.core.imports.internal.ImportConflict + +@Reusable +internal class DataValueConflictParser @Inject constructor( + dataElementStore: IdentifiableObjectStore, + dataValueStore: DataValueStore, + dataSetStore: IdentifiableObjectStore +) { + + private val conflicts = listOf( + InvalidDataElementTypeConflict(dataElementStore), + PastExpiryDateConflict(dataValueStore, dataSetStore), + PeriodAfterLatestOpenFutureConflict(dataElementStore) + ) + + fun getDataValueConflicts( + conflict: ImportConflict, + dataValues: List + ): List { + return conflicts.find { + it.matches(conflict) + }?.getDataValues(conflict, dataValues) ?: emptyList() + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStore.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictStore.kt similarity index 62% rename from core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStore.java rename to core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictStore.kt index 9fdf485a0f..2d5175cf72 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueConflictStore.kt @@ -26,37 +26,35 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.imports.internal; +package org.hisp.dhis.android.core.datavalue.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.internal.ObjectStore; -import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory; -import org.hisp.dhis.android.core.imports.TrackerImportConflict; -import org.hisp.dhis.android.core.imports.TrackerImportConflictTableInfo; +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.internal.ObjectStore +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory +import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.datavalue.DataValueConflictTableInfo -public final class TrackerImportConflictStore { - - private static final StatementBinder BINDER = (o, w) -> { - w.bind(1, o.conflict()); - w.bind(2, o.value()); - w.bind(3, o.trackedEntityInstance()); - w.bind(4, o.enrollment()); - w.bind(5, o.event()); - w.bind(6, o.trackedEntityAttribute()); - w.bind(7, o.dataElement()); - w.bind(8, o.tableReference()); - w.bind(9, o.errorCode()); - w.bind(10, o.status()); - w.bind(11, o.created()); - w.bind(12, o.displayDescription()); - }; - - private TrackerImportConflictStore() { +@Suppress("MagicNumber") +internal object DataValueConflictStore { + private val BINDER = StatementBinder { o, w -> + w.bind(1, o.conflict()) + w.bind(2, o.value()) + w.bind(3, o.attributeOptionCombo()) + w.bind(4, o.categoryOptionCombo()) + w.bind(5, o.dataElement()) + w.bind(6, o.period()) + w.bind(7, o.orgUnit()) + w.bind(8, o.errorCode()) + w.bind(9, o.displayDescription()) + w.bind(10, o.status()) + w.bind(11, o.created()) } - public static ObjectStore create(DatabaseAdapter databaseAdapter) { - return StoreFactory.objectStore(databaseAdapter, TrackerImportConflictTableInfo.TABLE_INFO, BINDER, - TrackerImportConflict::create); + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): ObjectStore { + return StoreFactory.objectStore(databaseAdapter, DataValueConflictTableInfo.TABLE_INFO, BINDER) { + DataValueConflict.create(it) + } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueHandler.kt index 0a88285ff6..a69634d29b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueHandler.kt @@ -85,7 +85,7 @@ internal class DataValueHandler(store: ObjectWithoutUidStore) : Objec get() { val whereClause = WhereClauseBuilder() .appendNotInKeyStringValues( - DataValueTableInfo.Columns.STATE, + DataValueTableInfo.Columns.SYNC_STATE, Arrays.asList(State.SYNCED.name, State.SYNCED_VIA_SMS.name) ) .build() diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.kt index 3be37abbba..13bb86803f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.kt @@ -28,19 +28,25 @@ package org.hisp.dhis.android.core.datavalue.internal import dagger.Reusable -import java.util.ArrayList -import java.util.HashSet -import java.util.regex.Pattern import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.State.ERROR +import org.hisp.dhis.android.core.common.State.SYNCED +import org.hisp.dhis.android.core.common.State.WARNING import org.hisp.dhis.android.core.datavalue.DataValue +import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.datavalue.DataValueConflictTableInfo import org.hisp.dhis.android.core.imports.ImportStatus import org.hisp.dhis.android.core.imports.internal.DataValueImportSummary import org.hisp.dhis.android.core.imports.internal.ImportConflict @Reusable internal class DataValueImportHandler @Inject constructor( - private val dataValueStore: DataValueStore + private val dataValueStore: DataValueStore, + private val dataValueConflictParser: DataValueConflictParser, + private val dataValueConflictStore: ObjectStore ) { fun handleImportSummary( @@ -52,80 +58,100 @@ internal class DataValueImportHandler @Inject constructor( } val state = when (dataValueImportSummary.importStatus()) { - ImportStatus.ERROR -> State.ERROR - ImportStatus.WARNING -> State.WARNING - else -> State.SYNCED + ImportStatus.ERROR -> ERROR + ImportStatus.WARNING -> WARNING + else -> SYNCED } - if (state == State.WARNING) { - handleDataValueWarnings(dataValueSet, dataValueImportSummary) + deleteDataValueConflicts(dataValueSet.dataValues) + + if (state == WARNING) { + handleDataValueWarnings(dataValueSet.dataValues, dataValueImportSummary) } else { setStateToDataValues(state, dataValueSet.dataValues) } } + private fun deleteDataValueConflicts(dataValues: List) { + dataValues.forEach { dataValue -> + val whereClause = WhereClauseBuilder() + .appendKeyStringValue( + DataValueConflictTableInfo.Columns.ATTRIBUTE_OPTION_COMBO, + dataValue.attributeOptionCombo() + ) + .appendKeyStringValue( + DataValueConflictTableInfo.Columns.CATEGORY_OPTION_COMBO, + dataValue.categoryOptionCombo() + ) + .appendKeyStringValue( + DataValueConflictTableInfo.Columns.DATA_ELEMENT, + dataValue.dataElement() + ) + .appendKeyStringValue( + DataValueConflictTableInfo.Columns.PERIOD, + dataValue.period() + ) + .appendKeyStringValue( + DataValueConflictTableInfo.Columns.ORG_UNIT, + dataValue.organisationUnit() + ).build() + dataValueConflictStore.deleteWhereIfExists(whereClause) + } + } + private fun handleDataValueWarnings( - dataValueSet: DataValueSet, + dataValues: List, dataValueImportSummary: DataValueImportSummary ) { - getValuesWithConflicts(dataValueSet, dataValueImportSummary)?.let { dataValueConflicts -> - setDataValueStates(dataValueSet, dataValueConflicts) - } ?: setStateToDataValues(State.WARNING, dataValueSet.dataValues) + getValuesWithConflicts( + dataValues, + dataValueImportSummary.importConflicts() + )?.let { dataValueConflicts -> + setDataValueStates(dataValues, dataValueConflicts) + } ?: setStateToDataValues(WARNING, dataValues) } private fun getValuesWithConflicts( - dataValueSet: DataValueSet, - dataValueImportSummary: DataValueImportSummary + dataValues: List, + importConflicts: List? ): Set? { - val dataValueConflicts: MutableSet = HashSet() - dataValueImportSummary.importConflicts()?.forEach { importConflict -> - getDataValues(importConflict, dataValueSet.dataValues).let { dataValues -> - if (dataValues.isEmpty()) { - return null - } - dataValueConflicts.addAll(dataValues) + val dataValueImportConflicts: MutableList = mutableListOf() + importConflicts?.forEach { importConflict -> + + val valuesPerConflict = dataValueConflictParser + .getDataValueConflicts(importConflict, dataValues) + + if (valuesPerConflict.isEmpty()) { + return null } + dataValueImportConflicts.addAll(valuesPerConflict) } - return dataValueConflicts + + dataValueConflictStore.insert(dataValueImportConflicts) + + return dataValueImportConflicts.mapNotNull { dataValueConflict -> + dataValues.find { dataValue -> + dataValue.attributeOptionCombo().equals(dataValueConflict.attributeOptionCombo()) && + dataValue.categoryOptionCombo().equals(dataValueConflict.categoryOptionCombo()) + } + }.toSet() } private fun setDataValueStates( - dataValueSet: DataValueSet, + dataValues: List, dataValueConflicts: Set ) { - val syncedValues = dataValueSet.dataValues.filter { dataValue -> + val syncedValues = dataValues.filter { dataValue -> !dataValueConflicts.contains(dataValue) } - setStateToDataValues(State.WARNING, dataValueConflicts) - setStateToDataValues(State.SYNCED, syncedValues) - } - - private fun getDataValues( - importConflict: ImportConflict, - dataValues: Collection - ): List { - val patternStr = "(?<=:\\s)[a-zA-Z0-9]{11}" - val pattern = Pattern.compile(patternStr) - val matcher = pattern.matcher(importConflict.value()) - - val foundDataValues: MutableList = ArrayList() - - if (matcher.find()) { - val value = importConflict.`object`() - val dataElementUid = matcher.group(0) - for (dataValue in dataValues) { - if (dataValue.value() == value && dataValue.dataElement() == dataElementUid) { - foundDataValues.add(dataValue) - } - } - } - return foundDataValues + setStateToDataValues(WARNING, dataValueConflicts) + setStateToDataValues(SYNCED, syncedValues) } private fun setStateToDataValues(state: State, dataValues: Collection) { for (dataValue in dataValues) { if (dataValueStore.isDataValueBeingUpload(dataValue)) { - if (state == State.SYNCED && dataValueStore.isDeleted(dataValue)) { + if (state == SYNCED && dataValueStore.isDeleted(dataValue)) { dataValueStore.deleteWhere(dataValue) } else { dataValueStore.setState(dataValue, state) diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleWiper.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleWiper.kt similarity index 69% rename from core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleWiper.java rename to core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleWiper.kt index 2f9fb66a28..2432cf5698 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleWiper.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleWiper.kt @@ -25,38 +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.datavalue.internal -package org.hisp.dhis.android.core.datavalue.internal; - -import org.hisp.dhis.android.core.datavalue.DataValueTableInfo; -import org.hisp.dhis.android.core.domain.aggregated.data.internal.AggregatedDataSyncTableInfo; -import org.hisp.dhis.android.core.wipe.internal.ModuleWiper; -import org.hisp.dhis.android.core.wipe.internal.TableWiper; - -import javax.inject.Inject; - -import dagger.Reusable; +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.datavalue.DataValueConflictTableInfo +import org.hisp.dhis.android.core.datavalue.DataValueTableInfo +import org.hisp.dhis.android.core.domain.aggregated.data.internal.AggregatedDataSyncTableInfo +import org.hisp.dhis.android.core.wipe.internal.ModuleWiper +import org.hisp.dhis.android.core.wipe.internal.TableWiper @Reusable -public final class DataValueModuleWiper implements ModuleWiper { - - private final TableWiper tableWiper; - - @Inject - DataValueModuleWiper(TableWiper tableWiper) { - this.tableWiper = tableWiper; - } - - @Override - public void wipeMetadata() { +class DataValueModuleWiper @Inject internal constructor(private val tableWiper: TableWiper) : ModuleWiper { + override fun wipeMetadata() { // No metadata to wipe } - @Override - public void wipeData() { + override fun wipeData() { tableWiper.wipeTables( - DataValueTableInfo.TABLE_INFO, - AggregatedDataSyncTableInfo.TABLE_INFO - ); + DataValueTableInfo.TABLE_INFO, + AggregatedDataSyncTableInfo.TABLE_INFO, + DataValueConflictTableInfo.TABLE_INFO + ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueStore.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueStore.java index d4c0df825a..702d5a3f78 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueStore.java @@ -36,6 +36,7 @@ import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStoreImpl; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.datavalue.DataValue; +import org.hisp.dhis.android.core.datavalue.DataValueByDataSetQueryHelper; import org.hisp.dhis.android.core.datavalue.DataValueTableInfo; import java.util.Collection; @@ -57,7 +58,7 @@ public class DataValueStore extends ObjectWithoutUidStoreImpl { w.bind(9, dataValue.lastUpdated()); w.bind(10, dataValue.comment()); w.bind(11, dataValue.followUp()); - w.bind(12, dataValue.state()); + w.bind(12, dataValue.syncState()); w.bind(13, dataValue.deleted()); }; @@ -94,17 +95,17 @@ public static DataValueStore create(DatabaseAdapter databaseAdapter) { Collection getDataValuesWithState(State state) { String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(Columns.STATE, state.name()).build(); + .appendKeyStringValue(Columns.SYNC_STATE, state.name()).build(); return selectWhere(whereClause); } /** * @param dataValue DataValue element you want to update - * @param newState The new state to be set for the DataValue + * @param newState The new state to be set for the DataValue */ public void setState(DataValue dataValue, State newState) { - DataValue updatedDataValue = dataValue.toBuilder().state(newState).build(); + DataValue updatedDataValue = dataValue.toBuilder().syncState(newState).build(); updateWhere(updatedDataValue); } @@ -115,7 +116,7 @@ public boolean exists(DataValue dataValue) { boolean isDataValueBeingUpload(DataValue dataValue) { String whereClause = uniqueWhereClauseBuilder(dataValue) - .appendKeyStringValue(Columns.STATE, State.UPLOADING) + .appendKeyStringValue(Columns.SYNC_STATE, State.UPLOADING) .build(); return selectWhere(whereClause).size() > 0; } @@ -135,4 +136,13 @@ private WhereClauseBuilder uniqueWhereClauseBuilder(DataValue dataValue) { .appendKeyStringValue(Columns.CATEGORY_OPTION_COMBO, dataValue.categoryOptionCombo()) .appendKeyStringValue(Columns.ATTRIBUTE_OPTION_COMBO, dataValue.attributeOptionCombo()); } + + public Boolean existsInDataSet(DataValue dataValue, String dataSetUid) { + WhereClauseBuilder whereClauseBuilder = uniqueWhereClauseBuilder(dataValue) + .appendInSubQuery( + DataValueByDataSetQueryHelper.getKey(), + DataValueByDataSetQueryHelper.whereClause(dataSetUid) + ); + return selectWhere(whereClauseBuilder.build()).size() > 0; + } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/DataValueImportConflictItem.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/DataValueImportConflictItem.kt new file mode 100644 index 0000000000..796ddb3504 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/DataValueImportConflictItem.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal.conflicts + +import java.util.Date +import org.hisp.dhis.android.core.datavalue.DataValue +import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.imports.ImportStatus +import org.hisp.dhis.android.core.imports.internal.ImportConflict + +internal interface DataValueImportConflictItem { + val regex: Regex + + fun getDataValues(conflict: ImportConflict, dataValues: List): List + + fun matches(conflict: ImportConflict): Boolean { + return regex.matches(conflict.value()) + } + + fun getConflictBuilder( + dataValue: DataValue, + conflict: ImportConflict, + displayDescription: String + ): DataValueConflict.Builder { + return DataValueConflict.builder() + .conflict(conflict.value()) + .value(dataValue.value()) + .attributeOptionCombo(dataValue.attributeOptionCombo()) + .categoryOptionCombo(dataValue.categoryOptionCombo()) + .dataElement(dataValue.dataElement()) + .orgUnit(dataValue.organisationUnit()) + .period(dataValue.period()) + .status(ImportStatus.WARNING) + .displayDescription(displayDescription) + .created(Date()) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/InvalidDataElementTypeConflict.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/InvalidDataElementTypeConflict.kt new file mode 100644 index 0000000000..c02d9adb48 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/InvalidDataElementTypeConflict.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal.conflicts + +import java.util.ArrayList +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.datavalue.DataValue +import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.imports.internal.ImportConflict + +internal class InvalidDataElementTypeConflict( + private val dataElementStore: IdentifiableObjectStore +) : DataValueImportConflictItem { + + override val regex: Regex + get() = Regex(".*, must match data element type: (\\w{11})") + + override fun getDataValues(conflict: ImportConflict, dataValues: List): List { + val foundDataValuesConflicts: MutableList = ArrayList() + val value = conflict.`object`() + val dataElementUid = regex.find(conflict.value())?.groupValues?.get(1) + dataValues.forEach { dataValue -> + if (dataValue.value() == value && dataValue.dataElement() == dataElementUid) { + foundDataValuesConflicts.add( + getConflictBuilder( + dataValue = dataValue, + conflict = conflict, + displayDescription = getDisplayDescription(conflict, value, dataValue.dataElement()) + ).build() + ) + } + } + + return foundDataValuesConflicts + } + + private fun getDisplayDescription(conflict: ImportConflict, value: String, dataElementUid: String?) = + dataElementUid?.let { + val dataElementType = dataElementStore.selectByUid(it)?.valueType().toString() + "DataValue $value must match with data element type $dataElementType" + } ?: conflict.value() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PastExpiryDateConflict.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PastExpiryDateConflict.kt new file mode 100644 index 0000000000..697677fce5 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PastExpiryDateConflict.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal.conflicts + +import java.util.ArrayList +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.dataset.DataSet +import org.hisp.dhis.android.core.datavalue.DataValue +import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.datavalue.internal.DataValueStore +import org.hisp.dhis.android.core.imports.internal.ImportConflict + +internal class PastExpiryDateConflict( + private val dataValueStore: DataValueStore, + private val dataSetStore: IdentifiableObjectStore +) : DataValueImportConflictItem { + + override val regex: Regex + get() = Regex("Current date is past expiry days for period (\\d+) and data set: (\\w{11})") + + override fun getDataValues(conflict: ImportConflict, dataValues: List): List { + val foundDataValuesConflicts: MutableList = ArrayList() + val period = conflict.`object`() + val dataSetUid = regex.find(conflict.value())?.groupValues?.get(2) + dataValues.forEach { dataValue -> + if (dataValue.period() == period && dataValueStore.existsInDataSet(dataValue, dataSetUid)) { + foundDataValuesConflicts.add( + getConflictBuilder( + dataValue = dataValue, + conflict = conflict, + displayDescription = getDisplayDescription(conflict, dataValue, dataSetUid!!) + ).build() + ) + } + } + + return foundDataValuesConflicts + } + + private fun getDisplayDescription(conflict: ImportConflict, dataValue: DataValue, dataSetUid: String) = + dataSetStore.selectByUid(dataSetUid)?.let { dataSet -> + "Current date is past expiry days for period ${dataValue.period()} and data set: ${dataSet.displayName()}" + } ?: conflict.value() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PeriodAfterLatestOpenFutureConflict.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PeriodAfterLatestOpenFutureConflict.kt new file mode 100644 index 0000000000..16dc17e608 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PeriodAfterLatestOpenFutureConflict.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal.conflicts + +import java.util.ArrayList +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.datavalue.DataValue +import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.imports.internal.ImportConflict + +@Suppress("MagicNumber") +internal class PeriodAfterLatestOpenFutureConflict( + private val dataElementStore: IdentifiableObjectStore +) : DataValueImportConflictItem { + + override val regex: Regex + get() = Regex("Period: (\\d+) is after latest open future period: (\\d+) for data element: (\\w{11})") + + override fun getDataValues(conflict: ImportConflict, dataValues: List): List { + val foundDataValuesConflicts: MutableList = ArrayList() + val period = conflict.`object`() + val dataElementUid = regex.find(conflict.value())?.groupValues?.get(3) + dataValues.forEach { dataValue -> + if (dataValue.period() == period && dataValue.dataElement() == dataElementUid) { + foundDataValuesConflicts.add( + getConflictBuilder( + dataValue = dataValue, + conflict = conflict, + displayDescription = getDisplayDescription(conflict, period, dataValue.dataElement()) + ).build() + ) + } + } + + return foundDataValuesConflicts + } + + private fun getDisplayDescription(conflict: ImportConflict, period: String, dataElementUid: String?) = + dataElementUid?.let { + val dataElementType = dataElementStore.selectByUid(it)?.valueType().toString() + "Period $period is after latest open future period for data element: $dataElementType" + } ?: conflict.value() +} 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 3fe9d8572d..6efc42a19c 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 @@ -60,6 +60,8 @@ import org.hisp.dhis.android.core.systeminfo.SystemInfo import org.hisp.dhis.android.core.systeminfo.internal.SystemInfoModuleDownloader import org.hisp.dhis.android.core.user.User import org.hisp.dhis.android.core.user.internal.UserModuleDownloader +import org.hisp.dhis.android.core.visualization.Visualization +import org.hisp.dhis.android.core.visualization.internal.VisualizationModuleDownloader @Suppress("LongParameterList") @Reusable @@ -72,6 +74,7 @@ class MetadataCall @Inject internal constructor( private val programDownloader: ProgramModuleDownloader, private val organisationUnitModuleDownloader: OrganisationUnitModuleDownloader, private val dataSetDownloader: DataSetModuleDownloader, + private val visualizationDownloader: VisualizationModuleDownloader, private val constantModuleDownloader: ConstantModuleDownloader, private val smsModule: SmsModule, private val databaseAdapter: DatabaseAdapter, @@ -135,6 +138,9 @@ class MetadataCall @Inject internal constructor( }, categoryDownloader.downloadMetadata().toSingle { progressManager.increaseProgress(Category::class.java, false) + }, + visualizationDownloader.downloadMetadata().map { + progressManager.increaseProgress(Visualization::class.java, false) } ).toObservable() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/Enrollment.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/Enrollment.java index 3431964569..96efcb00b4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/Enrollment.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/Enrollment.java @@ -43,15 +43,19 @@ import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbDateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbGeometryColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.EnrollmentStatusColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.StateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreCoordinatesColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreEventListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNoteListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreRelationshipListColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreStateColumnAdapter; import org.hisp.dhis.android.core.arch.helpers.CoordinateHelper; import org.hisp.dhis.android.core.common.BaseDeletableDataObject; import org.hisp.dhis.android.core.common.Coordinates; +import org.hisp.dhis.android.core.common.DataColumns; import org.hisp.dhis.android.core.common.Geometry; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.enrollment.internal.EnrollmentFields; import org.hisp.dhis.android.core.event.Event; import org.hisp.dhis.android.core.note.Note; @@ -61,6 +65,7 @@ import java.util.List; @AutoValue +@SuppressWarnings("PMD.ExcessiveImports") @JsonDeserialize(builder = AutoValue_Enrollment.Builder.class) public abstract class Enrollment extends BaseDeletableDataObject implements ObjectWithUidInterface { @@ -79,12 +84,12 @@ public abstract class Enrollment extends BaseDeletableDataObject implements Obje public abstract Date lastUpdated(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date createdAtClient(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date lastUpdatedAtClient(); @@ -154,6 +159,21 @@ public abstract class Enrollment extends BaseDeletableDataObject implements Obje @ColumnAdapter(IgnoreRelationshipListColumnAdapter.class) abstract List relationships(); + @Nullable + @ColumnName(DataColumns.AGGREGATED_SYNC_STATE) + @ColumnAdapter(StateColumnAdapter.class) + public abstract State aggregatedSyncState(); + + /** + * @deprecated Use {@link #aggregatedSyncState()} instead. + */ + @Deprecated + @Nullable + @ColumnAdapter(IgnoreStateColumnAdapter.class) + public State state() { + return aggregatedSyncState(); + } + public static Builder builder() { return new $$AutoValue_Enrollment.Builder(); } @@ -211,6 +231,16 @@ public abstract static class Builder extends BaseDeletableDataObject.Builder relationships); + public abstract Builder aggregatedSyncState(State aggregatedSyncState); + + /** + * @deprecated Use {@link #aggregatedSyncState(State)} and {@link #syncState(State)} instead. + */ + @Deprecated + public Builder state(State state) { + return aggregatedSyncState(state).syncState(state); + } + abstract Enrollment autoBuild(); // Auxiliary fields to access values diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentCollectionRepository.java index 666221b5cf..cdcd2f2efb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentCollectionRepository.java @@ -72,6 +72,11 @@ public final class EnrollmentCollectionRepository extends ReadWriteWithUidCollec this.dataStatePropagator = dataStatePropagator; } + @Override + protected void propagateState(Enrollment enrollment) { + dataStatePropagator.propagateEnrollmentUpdate(enrollment); + } + @Override public EnrollmentObjectRepository uid(String uid) { RepositoryScope updatedScope = RepositoryScopeHelper.withUidFilterItem(scope, uid); @@ -134,8 +139,20 @@ public StringFilterConnector byGeometryCoordinat return cf.string(EnrollmentTableInfo.Columns.GEOMETRY_COORDINATES); } + public EnumFilterConnector byAggregatedSyncState() { + return cf.enumC(DataColumns.AGGREGATED_SYNC_STATE); + } + + /** + * Use {@link #byAggregatedSyncState()} instead. + */ + @Deprecated public EnumFilterConnector byState() { - return cf.enumC(DataColumns.STATE); + return byAggregatedSyncState(); + } + + public EnumFilterConnector bySyncState() { + return cf.enumC(DataColumns.SYNC_STATE); } public BooleanFilterConnector byDeleted() { diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentObjectRepository.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentObjectRepository.java index d408339d4b..10752ebbb1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentObjectRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentObjectRepository.java @@ -90,11 +90,12 @@ public Unit setGeometry(Geometry geometry) throws D2Error { private Enrollment.Builder updateBuilder() { Enrollment enrollment = blockingGetWithoutChildren(); Date updateDate = new Date(); - State state = enrollment.state(); + State state = enrollment.aggregatedSyncState(); state = state == State.TO_POST ? state : State.TO_UPDATE; return enrollment.toBuilder() - .state(state) + .syncState(state) + .aggregatedSyncState(state) .lastUpdated(updateDate) .lastUpdatedAtClient(updateDate); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentService.kt b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentService.kt index 695a0ac9fd..38a3eb6508 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentService.kt @@ -30,10 +30,14 @@ package org.hisp.dhis.android.core.enrollment import dagger.Reusable import io.reactivex.Single import javax.inject.Inject +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.EventCollectionRepository import org.hisp.dhis.android.core.organisationunit.OrganisationUnit import org.hisp.dhis.android.core.organisationunit.OrganisationUnitCollectionRepository import org.hisp.dhis.android.core.program.AccessLevel import org.hisp.dhis.android.core.program.ProgramCollectionRepository +import org.hisp.dhis.android.core.program.ProgramStage +import org.hisp.dhis.android.core.program.ProgramStageCollectionRepository import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceCollectionRepository @Reusable @@ -41,7 +45,9 @@ class EnrollmentService @Inject constructor( private val enrollmentRepository: EnrollmentCollectionRepository, private val trackedEntityInstanceRepository: TrackedEntityInstanceCollectionRepository, private val programRepository: ProgramCollectionRepository, - private val organisationUnitRepository: OrganisationUnitCollectionRepository + private val organisationUnitRepository: OrganisationUnitCollectionRepository, + private val eventCollectionRepository: EventCollectionRepository, + private val programStagesCollectionRepository: ProgramStageCollectionRepository ) { /** @@ -104,4 +110,31 @@ class EnrollmentService @Inject constructor( .uid(tei.organisationUnit()) .blockingExists() } + + fun blockingGetAllowEventCreation(enrollmentUid: String, stagesToHide: List): Boolean { + val programStages = eventCollectionRepository.byEnrollmentUid().eq(enrollmentUid) + .byDeleted().isFalse.get() + .toFlowable().flatMapIterable { events: List? -> events } + .map { event: Event -> event.programStage() } + .toList() + .flatMap { currentProgramStagesUids: List -> + val repository = programStagesCollectionRepository.byProgramUid().eq( + enrollmentRepository.uid(enrollmentUid).blockingGet().program() + ).byAccessDataWrite().isTrue + + repository.get().toFlowable() + .flatMapIterable { stages: List? -> stages } + .filter { programStage: ProgramStage -> + !currentProgramStagesUids.contains(programStage.uid()) || + programStage.repeatable()!! + } + .toList() + }.blockingGet() + + return programStages.find { !stagesToHide.contains(it.uid()) } != null + } + + fun allowEventCreation(enrollmentUid: String, stagesToHide: List): Single { + return Single.fromCallable { blockingGetAllowEventCreation(enrollmentUid, stagesToHide) } + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentTableInfo.java index 08efea9239..c70634ebd5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentTableInfo.java @@ -85,7 +85,8 @@ public String[] all() { TRACKED_ENTITY_INSTANCE, GEOMETRY_TYPE, GEOMETRY_COORDINATES, - STATE, + SYNC_STATE, + AGGREGATED_SYNC_STATE, DELETED ); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/NewTrackerImporterEnrollment.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/NewTrackerImporterEnrollment.java index 8b25bbeb81..0425c6f8fe 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/NewTrackerImporterEnrollment.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/NewTrackerImporterEnrollment.java @@ -32,7 +32,6 @@ 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; @@ -43,12 +42,15 @@ import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbDateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbGeometryColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.EnrollmentStatusColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.StateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNewTrackerImporterEventListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNewTrackerImporterNoteListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreRelationshipListColumnAdapter; import org.hisp.dhis.android.core.common.BaseDeletableDataObject; +import org.hisp.dhis.android.core.common.DataColumns; import org.hisp.dhis.android.core.common.Geometry; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.enrollment.internal.EnrollmentFields; import org.hisp.dhis.android.core.event.NewTrackerImporterEvent; import org.hisp.dhis.android.core.note.NewTrackerImporterNote; @@ -62,7 +64,7 @@ public abstract class NewTrackerImporterEnrollment extends BaseDeletableDataObject implements ObjectWithUidInterface { @Override - @JsonProperty() + @JsonProperty("enrollment") public abstract String uid(); @Nullable @@ -76,12 +78,12 @@ public abstract class NewTrackerImporterEnrollment extends BaseDeletableDataObje public abstract Date updatedAt(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date createdAtClient(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date updatedAtClient(); @@ -119,14 +121,19 @@ public abstract class NewTrackerImporterEnrollment extends BaseDeletableDataObje public abstract EnrollmentStatus status(); @Nullable - @JsonIgnore() - public abstract String trackedEntityInstance(); + @JsonProperty() + public abstract String trackedEntity(); @Nullable @JsonProperty() @ColumnAdapter(DbGeometryColumnAdapter.class) public abstract Geometry geometry(); + @Nullable + @ColumnName(DataColumns.AGGREGATED_SYNC_STATE) + @ColumnAdapter(StateColumnAdapter.class) + public abstract State aggregatedSyncState(); + @Nullable @JsonProperty() @ColumnAdapter(IgnoreNewTrackerImporterEventListColumnAdapter.class) @@ -157,7 +164,7 @@ public static NewTrackerImporterEnrollment create(Cursor cursor) { public abstract static class Builder extends BaseDeletableDataObject.Builder { public abstract Builder id(Long id); - @JsonProperty() + @JsonProperty("enrollment") public abstract Builder uid(String uid); public abstract Builder createdAt(Date createdAt); @@ -184,10 +191,12 @@ public abstract static class Builder extends BaseDeletableDataObject.Builder events); public abstract Builder notes(List notes); diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/NewTrackerImporterEnrollmentTransformer.kt b/core/src/main/java/org/hisp/dhis/android/core/enrollment/NewTrackerImporterEnrollmentTransformer.kt index b4e9c0ba51..ba91ddb07b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/NewTrackerImporterEnrollmentTransformer.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/NewTrackerImporterEnrollmentTransformer.kt @@ -46,9 +46,10 @@ internal class NewTrackerImporterEnrollmentTransformer : Transformer relationshipOrphanCleaner(EnrollmentRela @Reusable OrphanCleaner eventOrphanCleaner(DatabaseAdapter databaseAdapter) { return new DataOrphanCleanerImpl<>(EventTableInfo.TABLE_INFO.name(), EventTableInfo.Columns.ENROLLMENT, - DataColumns.STATE, databaseAdapter); + DataColumns.SYNC_STATE, databaseAdapter); } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentHandler.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentHandler.java index 2e70f233f1..e0f15aaad0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentHandler.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentHandler.java @@ -90,12 +90,12 @@ final class EnrollmentHandler extends IdentifiableDataHandlerImpl { @Override protected Enrollment addRelationshipState(Enrollment o) { - return o.toBuilder().state(State.RELATIONSHIP).build(); + return o.toBuilder().aggregatedSyncState(State.RELATIONSHIP).syncState(State.RELATIONSHIP).build(); } @Override protected Enrollment addSyncedState(Enrollment o) { - return o.toBuilder().state(State.SYNCED).build(); + return o.toBuilder().aggregatedSyncState(State.SYNCED).syncState(State.SYNCED).build(); } @Override @@ -121,7 +121,8 @@ protected void afterObjectHandled(Enrollment enrollment, HandleAction action, Bo if (action != HandleAction.Delete) { eventHandler.handleMany(EnrollmentInternalAccessor.accessEvents(enrollment), event -> event.toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) + .aggregatedSyncState(State.SYNCED) .build(), overwrite); diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandler.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandler.java index 081de35cca..e69de29bb2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandler.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandler.java @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.enrollment.internal; - -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; -import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction; -import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper; -import org.hisp.dhis.android.core.common.DataColumns; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.common.internal.DataStatePropagator; -import org.hisp.dhis.android.core.enrollment.Enrollment; -import org.hisp.dhis.android.core.enrollment.EnrollmentInternalAccessor; -import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo; -import org.hisp.dhis.android.core.event.Event; -import org.hisp.dhis.android.core.event.internal.EventImportHandler; -import org.hisp.dhis.android.core.imports.TrackerImportConflict; -import org.hisp.dhis.android.core.imports.TrackerImportConflictTableInfo; -import org.hisp.dhis.android.core.imports.internal.BaseImportSummaryHelper; -import org.hisp.dhis.android.core.imports.internal.EnrollmentImportSummary; -import org.hisp.dhis.android.core.imports.internal.EventImportSummaries; -import org.hisp.dhis.android.core.imports.internal.ImportConflict; -import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser; -import org.hisp.dhis.android.core.note.Note; -import org.hisp.dhis.android.core.note.NoteTableInfo; -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStore; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import javax.inject.Inject; - -import dagger.Reusable; - -import static org.hisp.dhis.android.core.arch.db.stores.internal.StoreUtils.getState; - -@Reusable -@SuppressWarnings("PMD.ExcessiveImports") -public class EnrollmentImportHandler { - private final EnrollmentStore enrollmentStore; - private final TrackedEntityInstanceStore trackedEntityInstanceStore; - private final IdentifiableObjectStore noteStore; - private final EventImportHandler eventImportHandler; - private final ObjectStore trackerImportConflictStore; - private final TrackerImportConflictParser trackerImportConflictParser; - private final DataStatePropagator dataStatePropagator; - - @Inject - public EnrollmentImportHandler(@NonNull EnrollmentStore enrollmentStore, - @NonNull TrackedEntityInstanceStore trackedEntityInstanceStore, - @NonNull IdentifiableObjectStore noteStore, - @NonNull EventImportHandler eventImportHandler, - @NonNull ObjectStore trackerImportConflictStore, - @NonNull TrackerImportConflictParser trackerImportConflictParser, - @NonNull DataStatePropagator dataStatePropagator) { - this.enrollmentStore = enrollmentStore; - this.trackedEntityInstanceStore = trackedEntityInstanceStore; - this.noteStore = noteStore; - this.eventImportHandler = eventImportHandler; - this.trackerImportConflictStore = trackerImportConflictStore; - this.trackerImportConflictParser = trackerImportConflictParser; - this.dataStatePropagator = dataStatePropagator; - } - - public void handleEnrollmentImportSummary(List enrollmentImportSummaries, - List enrollments, - String teiUid) { - State parentState = null; - - if (enrollmentImportSummaries != null) { - for (EnrollmentImportSummary enrollmentImportSummary : enrollmentImportSummaries) { - String enrollmentUid = enrollmentImportSummary == null ? null : enrollmentImportSummary.reference(); - - if (enrollmentUid == null) { - continue; - } - - State state = getState(enrollmentImportSummary.status()); - deleteEnrollmentConflicts(enrollmentUid); - - HandleAction handleAction = enrollmentStore.setStateOrDelete(enrollmentUid, state); - - if (state.equals(State.ERROR) || state.equals(State.WARNING)) { - parentState = parentState == State.ERROR ? State.ERROR : state; - dataStatePropagator.resetUploadingEventStates(enrollmentUid); - } - - if (handleAction != HandleAction.Delete) { - storeEnrollmentImportConflicts(enrollmentImportSummary, teiUid); - handleNoteImportSummary(enrollmentUid, state); - handleEventImportSummaries(enrollmentImportSummary, enrollments, teiUid); - } - - } - } - - List processedEnrollments = BaseImportSummaryHelper.getReferences(enrollmentImportSummaries); - for (Enrollment enrollment : enrollments) { - if (!processedEnrollments.contains(enrollment.uid())) { - State state = State.TO_UPDATE; - enrollmentStore.setStateOrDelete(enrollment.uid(), state); - parentState = parentState == State.ERROR || parentState == State.WARNING ? parentState : state; - dataStatePropagator.resetUploadingEventStates(enrollment.uid()); - - deleteEnrollmentConflicts(enrollment.uid()); - } - } - - updateParentState(parentState, teiUid); - } - - private void handleEventImportSummaries(EnrollmentImportSummary enrollmentImportSummary, - List enrollments, - String teiUid) { - - if (enrollmentImportSummary.events() != null) { - EventImportSummaries eventImportSummaries = enrollmentImportSummary.events(); - - if (eventImportSummaries.importSummaries() != null) { - eventImportHandler.handleEventImportSummaries( - eventImportSummaries.importSummaries(), - getEvents(enrollmentImportSummary.reference(), enrollments), - enrollmentImportSummary.reference(), - teiUid); - } - } - } - - private void handleNoteImportSummary(String enrollmentUid, State state) { - State newNoteState = state.equals(State.SYNCED) ? State.SYNCED : State.TO_POST; - String whereClause = new WhereClauseBuilder() - .appendInKeyStringValues( - DataColumns.STATE, EnumHelper.asStringList(State.uploadableStatesIncludingError())) - .appendKeyStringValue(NoteTableInfo.Columns.ENROLLMENT, enrollmentUid).build(); - List notes = noteStore.selectWhere(whereClause); - for (Note note : notes) { - noteStore.update(note.toBuilder().state(newNoteState).build()); - } - } - - private void storeEnrollmentImportConflicts(EnrollmentImportSummary enrollmentImportSummary, - String teiUid) { - List trackerImportConflicts = new ArrayList<>(); - if (enrollmentImportSummary.description() != null) { - trackerImportConflicts.add(getConflictBuilder(teiUid, enrollmentImportSummary) - .conflict(enrollmentImportSummary.description()) - .displayDescription(enrollmentImportSummary.description()) - .value(enrollmentImportSummary.reference()) - .build()); - } - - if (enrollmentImportSummary.conflicts() != null) { - for (ImportConflict importConflict : enrollmentImportSummary.conflicts()) { - trackerImportConflicts.add(trackerImportConflictParser - .getEnrollmentConflict(importConflict, getConflictBuilder(teiUid, enrollmentImportSummary))); - } - } - - for (TrackerImportConflict trackerImportConflict : trackerImportConflicts) { - trackerImportConflictStore.insert(trackerImportConflict); - } - } - - - private void updateParentState(State parentState, String teiUid) { - if (parentState != null && teiUid != null) { - trackedEntityInstanceStore.setState(teiUid, parentState); - } - } - - private void deleteEnrollmentConflicts(String enrollmentUid) { - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(TrackerImportConflictTableInfo.Columns.ENROLLMENT, enrollmentUid) - .appendKeyStringValue( - TrackerImportConflictTableInfo.Columns.TABLE_REFERENCE, - EnrollmentTableInfo.TABLE_INFO.name()) - .build(); - trackerImportConflictStore.deleteWhereIfExists(whereClause); - } - - private List getEvents(String enrollmentUid, - List enrollments) { - for (Enrollment enrollment : enrollments) { - if (enrollmentUid.equals(enrollment.uid())) { - return EnrollmentInternalAccessor.accessEvents(enrollment); - } - } - return Collections.emptyList(); - } - - private TrackerImportConflict.Builder getConflictBuilder(String trackedEntityInstanceUid, - EnrollmentImportSummary enrollmentImportSummary) { - return TrackerImportConflict.builder() - .trackedEntityInstance(trackedEntityInstanceUid) - .enrollment(enrollmentImportSummary.reference()) - .tableReference(EnrollmentTableInfo.TABLE_INFO.name()) - .status(enrollmentImportSummary.status()) - .created(new Date()); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandler.kt new file mode 100644 index 0000000000..85f025b8f1 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandler.kt @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2004-2021, 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.enrollment.internal + +import dagger.Reusable +import java.util.* +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreUtils.getSyncState +import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.internal.DataStatePropagator +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.EnrollmentInternalAccessor +import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.internal.EventImportHandler +import org.hisp.dhis.android.core.imports.TrackerImportConflict +import org.hisp.dhis.android.core.imports.internal.BaseImportSummaryHelper.getReferences +import org.hisp.dhis.android.core.imports.internal.EnrollmentImportSummary +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore +import org.hisp.dhis.android.core.tracker.importer.internal.JobReportEnrollmentHandler + +@Reusable +internal class EnrollmentImportHandler @Inject constructor( + private val enrollmentStore: EnrollmentStore, + private val eventImportHandler: EventImportHandler, + private val trackerImportConflictStore: TrackerImportConflictStore, + private val trackerImportConflictParser: TrackerImportConflictParser, + private val jobReportEnrollmentHandler: JobReportEnrollmentHandler, + private val dataStatePropagator: DataStatePropagator +) { + + fun handleEnrollmentImportSummary( + enrollmentImportSummaries: List?, + enrollments: List, + teiUid: String + ) { + + enrollmentImportSummaries?.filterNotNull()?.forEach { enrollmentImportSummary -> + enrollmentImportSummary.reference()?.let { enrollmentUid -> + + val syncState = getSyncState(enrollmentImportSummary.status()) + trackerImportConflictStore.deleteEnrollmentConflicts(enrollmentUid) + + val handleAction = enrollmentStore.setSyncStateOrDelete(enrollmentUid, syncState) + + if (syncState == State.ERROR || syncState == State.WARNING) { + dataStatePropagator.resetUploadingEventStates(enrollmentUid) + } + + if (handleAction !== HandleAction.Delete) { + jobReportEnrollmentHandler.handleEnrollmentNotes(enrollmentUid, syncState) + storeEnrollmentImportConflicts(enrollmentImportSummary, teiUid) + + handleEventImportSummaries(enrollmentImportSummary, enrollments, teiUid) + } + } + } + + val processedEnrollments = getReferences(enrollmentImportSummaries) + + enrollments.filterNot { processedEnrollments.contains(it.uid()) }.forEach { enrollment -> + val state = State.TO_UPDATE + trackerImportConflictStore.deleteEnrollmentConflicts(enrollment.uid()) + enrollmentStore.setSyncStateOrDelete(enrollment.uid(), state) + dataStatePropagator.resetUploadingEventStates(enrollment.uid()) + } + + dataStatePropagator.refreshTrackedEntityInstanceAggregatedSyncState(teiUid) + } + + private fun handleEventImportSummaries( + enrollmentImportSummary: EnrollmentImportSummary, + enrollments: List, + teiUid: String + ) { + enrollmentImportSummary.events()?.importSummaries()?.let { importSummaries -> + val enrollmentUid = enrollmentImportSummary.reference()!! + eventImportHandler.handleEventImportSummaries( + importSummaries, + getEvents(enrollmentUid, enrollments), + enrollmentUid, + teiUid + ) + } + } + + private fun storeEnrollmentImportConflicts( + enrollmentImportSummary: EnrollmentImportSummary, + teiUid: String + ) { + val trackerImportConflicts: MutableList = ArrayList() + + if (enrollmentImportSummary.description() != null) { + trackerImportConflicts.add( + getConflictBuilder(teiUid, enrollmentImportSummary) + .conflict(enrollmentImportSummary.description()) + .displayDescription(enrollmentImportSummary.description()) + .value(enrollmentImportSummary.reference()) + .build() + ) + } + + enrollmentImportSummary.conflicts()?.forEach { importConflict -> + trackerImportConflicts.add( + trackerImportConflictParser + .getEnrollmentConflict(importConflict, getConflictBuilder(teiUid, enrollmentImportSummary)) + ) + } + + trackerImportConflicts.forEach { trackerImportConflictStore.insert(it) } + } + + private fun getEvents( + enrollmentUid: String, + enrollments: List + ): List { + return enrollments.find { it.uid() == enrollmentUid }?.let { + EnrollmentInternalAccessor.accessEvents(it) + } ?: listOf() + } + + private fun getConflictBuilder( + trackedEntityInstanceUid: String, + enrollmentImportSummary: EnrollmentImportSummary + ): TrackerImportConflict.Builder { + return TrackerImportConflict.builder() + .trackedEntityInstance(trackedEntityInstanceUid) + .enrollment(enrollmentImportSummary.reference()) + .tableReference(EnrollmentTableInfo.TABLE_INFO.name()) + .status(enrollmentImportSummary.status()) + .created(Date()) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentProjectionTransformer.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentProjectionTransformer.java index f09da8ae7c..5b480a9586 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentProjectionTransformer.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentProjectionTransformer.java @@ -46,7 +46,8 @@ public Enrollment transform(EnrollmentCreateProjection projection) { return Enrollment.builder() .uid(generatedUid) - .state(State.TO_POST) + .aggregatedSyncState(State.TO_POST) + .syncState(State.TO_POST) .created(creationDate) .lastUpdated(creationDate) .createdAtClient(creationDate) diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStore.kt b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStore.kt new file mode 100644 index 0000000000..6c5c4212e4 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStore.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2004-2021, 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.enrollment.internal + +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStore +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.Enrollment + +internal interface EnrollmentStore : IdentifiableDeletableDataObjectStore { + + fun queryEnrollmentsToPost(): Map> + + fun queryMissingRelationshipsUids(): List + + fun setAggregatedSyncState(uid: String, state: State): Int + + fun selectAggregatedSyncStateWhere(whereClause: String): List + + fun selectByTrackedEntityInstanceAndAttribute(teiUid: String, attributeUid: String): List +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreImpl.java deleted file mode 100644 index e84b7d0958..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreImpl.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.enrollment.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.SQLStatementBuilderImpl; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinder; -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStoreImpl; -import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper; -import org.hisp.dhis.android.core.common.DataColumns; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.enrollment.Enrollment; -import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo; -import org.hisp.dhis.android.core.event.EventTableInfo; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import kotlin.jvm.functions.Function1; - -public final class EnrollmentStoreImpl - extends IdentifiableDeletableDataObjectStoreImpl implements EnrollmentStore { - - private static final StatementBinder BINDER = (o, w) -> { - w.bind(1, o.uid()); - w.bind(2, o.created()); - w.bind(3, o.lastUpdated()); - w.bind(4, o.createdAtClient()); - w.bind(5, o.lastUpdatedAtClient()); - w.bind(6, o.organisationUnit()); - w.bind(7, o.program()); - w.bind(8, o.enrollmentDate()); - w.bind(9, o.incidentDate()); - w.bind(10, o.completedDate()); - w.bind(11, o.followUp()); - w.bind(12, o.status()); - w.bind(13, o.trackedEntityInstance()); - w.bind(14, o.geometry() == null ? null : o.geometry().type()); - w.bind(15, o.geometry() == null ? null : o.geometry().coordinates()); - w.bind(16, o.state()); - w.bind(17, o.deleted()); - }; - - private EnrollmentStoreImpl(DatabaseAdapter databaseAdapter, - SQLStatementBuilderImpl builder, - StatementBinder binder, - Function1 objectFactory) { - super(databaseAdapter, builder, binder, objectFactory); - } - - @Override - public Map> queryEnrollmentsToPost() { - String enrollmentsToPostQuery = new WhereClauseBuilder() - .appendInKeyStringValues(DataColumns.STATE, - EnumHelper.asStringList(State.uploadableStatesIncludingError())).build(); - - List enrollmentList = selectWhere(enrollmentsToPostQuery); - - Map> enrollmentMap = new HashMap<>(); - for (Enrollment enrollment : enrollmentList) { - addEnrollmentToMap(enrollmentMap, enrollment); - } - - return enrollmentMap; - } - - @Override - public List queryMissingRelationshipsUids() { - String whereRelationshipsClause = new WhereClauseBuilder() - .appendKeyStringValue(DataColumns.STATE, State.RELATIONSHIP) - .appendIsNullValue(EventTableInfo.Columns.ORGANISATION_UNIT) - .build(); - - return selectUidsWhere(whereRelationshipsClause); - } - - private void addEnrollmentToMap(Map> enrollmentMap, Enrollment enrollment) { - if (enrollmentMap.get(enrollment.trackedEntityInstance()) == null) { - enrollmentMap.put(enrollment.trackedEntityInstance(), new ArrayList<>()); - } - - enrollmentMap.get(enrollment.trackedEntityInstance()).add(enrollment); - } - - public static EnrollmentStore create(DatabaseAdapter databaseAdapter) { - SQLStatementBuilderImpl statementBuilder = new SQLStatementBuilderImpl( - EnrollmentTableInfo.TABLE_INFO.name(), - EnrollmentTableInfo.TABLE_INFO.columns()); - - return new EnrollmentStoreImpl( - databaseAdapter, - statementBuilder, - BINDER, - Enrollment::create - ); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreImpl.kt new file mode 100644 index 0000000000..eaf7ec69a4 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStoreImpl.kt @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2004-2021, 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.enrollment.internal + +import android.content.ContentValues +import android.database.Cursor +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +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.IdentifiableDeletableDataObjectStoreImpl +import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper +import org.hisp.dhis.android.core.common.DataColumns +import org.hisp.dhis.android.core.common.IdentifiableColumns +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo +import org.hisp.dhis.android.core.event.EventTableInfo +import org.hisp.dhis.android.core.program.ProgramTrackedEntityAttributeTableInfo + +internal class EnrollmentStoreImpl private constructor( + databaseAdapter: DatabaseAdapter, + builder: SQLStatementBuilderImpl, + binder: StatementBinder, + objectFactory: Function1 +) : IdentifiableDeletableDataObjectStoreImpl(databaseAdapter, builder, binder, objectFactory), + EnrollmentStore { + + override fun queryEnrollmentsToPost(): Map> { + val enrollmentsToPostQuery = WhereClauseBuilder() + .appendInKeyStringValues( + DataColumns.AGGREGATED_SYNC_STATE, + EnumHelper.asStringList(State.uploadableStatesIncludingError().toList()) + ).build() + val enrollmentList: List = selectWhere(enrollmentsToPostQuery) + + return enrollmentList.groupBy { it.trackedEntityInstance()!! } + } + + override fun queryMissingRelationshipsUids(): List { + val whereRelationshipsClause = WhereClauseBuilder() + .appendKeyStringValue(DataColumns.AGGREGATED_SYNC_STATE, State.RELATIONSHIP) + .appendIsNullValue(EventTableInfo.Columns.ORGANISATION_UNIT) + .build() + + return selectUidsWhere(whereRelationshipsClause) + } + + override fun setAggregatedSyncState(uid: String, state: State): Int { + val updates = ContentValues() + updates.put(DataColumns.AGGREGATED_SYNC_STATE, state.toString()) + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(IdentifiableColumns.UID, uid) + .build() + + return updateWhere(updates, whereClause) + } + + override fun selectAggregatedSyncStateWhere(whereClause: String): List { + val statesStr = selectStringColumnsWhereClause(DataColumns.AGGREGATED_SYNC_STATE, whereClause) + + return statesStr.map { State.valueOf(it) } + } + + override fun selectByTrackedEntityInstanceAndAttribute( + teiUid: String, + attributeUid: String + ): List { + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE, teiUid) + .appendInSubQuery( + EnrollmentTableInfo.Columns.PROGRAM, + "SELECT ${ProgramTrackedEntityAttributeTableInfo.Columns.PROGRAM} " + + "FROM ${ProgramTrackedEntityAttributeTableInfo.TABLE_INFO.name()} " + + "WHERE ${ProgramTrackedEntityAttributeTableInfo.Columns.TRACKED_ENTITY_ATTRIBUTE} = " + + "'$attributeUid'" + ).build() + + return selectWhere(whereClause) + } + + companion object { + private val BINDER = StatementBinder { o: Enrollment, w: StatementWrapper -> + w.bind(1, o.uid()) + w.bind(2, o.created()) + w.bind(3, o.lastUpdated()) + w.bind(4, o.createdAtClient()) + w.bind(5, o.lastUpdatedAtClient()) + w.bind(6, o.organisationUnit()) + w.bind(7, o.program()) + w.bind(8, o.enrollmentDate()) + w.bind(9, o.incidentDate()) + w.bind(10, o.completedDate()) + w.bind(11, o.followUp()) + w.bind(12, o.status()) + w.bind(13, o.trackedEntityInstance()) + w.bind(14, if (o.geometry() == null) null else o.geometry()!!.type()) + w.bind(15, if (o.geometry() == null) null else o.geometry()!!.coordinates()) + w.bind(16, o.syncState()) + w.bind(17, o.aggregatedSyncState()) + w.bind(18, o.deleted()) + } + + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): EnrollmentStore { + val statementBuilder = SQLStatementBuilderImpl( + EnrollmentTableInfo.TABLE_INFO.name(), + EnrollmentTableInfo.TABLE_INFO.columns() + ) + return EnrollmentStoreImpl( + databaseAdapter, + statementBuilder, + BINDER + ) { cursor: Cursor? -> Enrollment.create(cursor) } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/Event.java b/core/src/main/java/org/hisp/dhis/android/core/event/Event.java index 8c0e2ecbcd..022d5bfc98 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/Event.java +++ b/core/src/main/java/org/hisp/dhis/android/core/event/Event.java @@ -32,25 +32,29 @@ 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.gabrielittner.auto.value.cursor.ColumnName; import com.google.auto.value.AutoValue; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbDateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbGeometryColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.EventStatusColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.StateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreCoordinatesColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNoteListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreRelationshipListColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreStateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreTrackedEntityDataValueListColumnAdapter; import org.hisp.dhis.android.core.arch.helpers.CoordinateHelper; import org.hisp.dhis.android.core.common.BaseDeletableDataObject; import org.hisp.dhis.android.core.common.Coordinates; +import org.hisp.dhis.android.core.common.DataColumns; import org.hisp.dhis.android.core.common.Geometry; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.event.internal.EventFields; import org.hisp.dhis.android.core.note.Note; import org.hisp.dhis.android.core.relationship.Relationship; @@ -61,7 +65,7 @@ @AutoValue @JsonDeserialize(builder = AutoValue_Event.Builder.class) -@SuppressWarnings({"PMD.GodClass"}) +@SuppressWarnings({"PMD.GodClass", "PMD.ExcessivePublicCount", "PMD.ExcessiveImports"}) public abstract class Event extends BaseDeletableDataObject implements ObjectWithUidInterface { @Override @@ -83,12 +87,12 @@ public abstract class Event extends BaseDeletableDataObject implements ObjectWit public abstract Date lastUpdated(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date createdAtClient(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date lastUpdatedAtClient(); @@ -161,6 +165,21 @@ public abstract class Event extends BaseDeletableDataObject implements ObjectWit @ColumnAdapter(IgnoreRelationshipListColumnAdapter.class) abstract List relationships(); + @Nullable + @ColumnName(DataColumns.AGGREGATED_SYNC_STATE) + @ColumnAdapter(StateColumnAdapter.class) + public abstract State aggregatedSyncState(); + + /** + * @deprecated Use {@link #aggregatedSyncState()} instead. + */ + @Deprecated + @Nullable + @ColumnAdapter(IgnoreStateColumnAdapter.class) + public State state() { + return aggregatedSyncState(); + } + public static Builder builder() { return new $$AutoValue_Event.Builder(); } @@ -222,6 +241,16 @@ public abstract static class Builder extends BaseDeletableDataObject.Builder relationships); + public abstract Builder aggregatedSyncState(State aggregatedSyncState); + + /** + * @deprecated Use {@link #aggregatedSyncState(State)} and {@link #syncState(State)} instead. + */ + @Deprecated + public Builder state(State state) { + return aggregatedSyncState(state).syncState(state); + } + abstract Event autoBuild(); // Auxiliary fields to access values diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/EventCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/event/EventCollectionRepository.java index e1cd072580..d4fa80a083 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/EventCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/event/EventCollectionRepository.java @@ -50,6 +50,7 @@ import org.hisp.dhis.android.core.event.internal.EventStore; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitTableInfo; import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueTableInfo; +import org.hisp.dhis.android.core.tracker.importer.internal.JobQueryCall; import java.util.Collections; import java.util.List; @@ -72,6 +73,7 @@ public final class EventCollectionRepository private final EventStore store; private final DataStatePropagator dataStatePropagator; + private final JobQueryCall jobQueryCall; @Inject EventCollectionRepository(final EventStore store, @@ -79,21 +81,26 @@ public final class EventCollectionRepository final RepositoryScope scope, final EventPostParentCall postCall, final Transformer transformer, - final DataStatePropagator dataStatePropagator) { + final DataStatePropagator dataStatePropagator, + final JobQueryCall jobQueryCall) { super(store, childrenAppenders, scope, transformer, new FilterConnectorFactory<>(scope, s -> new EventCollectionRepository( - store, childrenAppenders, s, postCall, transformer, dataStatePropagator))); + store, childrenAppenders, s, postCall, transformer, dataStatePropagator, jobQueryCall))); this.store = store; this.postCall = postCall; this.dataStatePropagator = dataStatePropagator; + this.jobQueryCall = jobQueryCall; } @Override public Observable upload() { - return Observable.fromCallable(() -> byState().in(State.uploadableStates()) - .byEnrollmentUid().isNull() - .blockingGetWithoutChildren()) - .flatMap(postCall::uploadEvents); + return Observable.concat( + jobQueryCall.queryPendingJobs(), + Observable.fromCallable(() -> byAggregatedSyncState().in(State.uploadableStates()) + .byEnrollmentUid().isNull() + .blockingGetWithoutChildren()) + .flatMap(postCall::uploadEvents) + ); } @Override @@ -101,6 +108,11 @@ public void blockingUpload() { upload().blockingSubscribe(); } + @Override + protected void propagateState(Event event) { + dataStatePropagator.propagateEventUpdate(event); + } + @Override public EventObjectRepository uid(String uid) { RepositoryScope updatedScope = RepositoryScopeHelper.withUidFilterItem(scope, uid); @@ -167,8 +179,22 @@ public DateFilterConnector byDueDate() { return cf.simpleDate(Columns.DUE_DATE); } + /** + * @deprecated Use {@link #bySyncState()} instead. + * + * @return + */ + @Deprecated public EnumFilterConnector byState() { - return cf.enumC(Columns.STATE); + return bySyncState(); + } + + public EnumFilterConnector bySyncState() { + return cf.enumC(Columns.SYNC_STATE); + } + + public EnumFilterConnector byAggregatedSyncState() { + return cf.enumC(Columns.AGGREGATED_SYNC_STATE); } public StringFilterConnector byAttributeOptionComboUid() { diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/EventObjectRepository.java b/core/src/main/java/org/hisp/dhis/android/core/event/EventObjectRepository.java index 2163f9762f..7322afe8f5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/EventObjectRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/event/EventObjectRepository.java @@ -94,11 +94,12 @@ public Unit setAssignedUser(String assignedUser) throws D2Error { private Event.Builder updateBuilder() { Event event = blockingGetWithoutChildren(); Date updateDate = new Date(); - State state = event.state(); + State state = event.syncState(); state = state == State.TO_POST ? state : State.TO_UPDATE; return event.toBuilder() - .state(state) + .syncState(state) + .aggregatedSyncState(state) .lastUpdated(updateDate) .lastUpdatedAtClient(updateDate); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/EventTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/event/EventTableInfo.java index 41f34399f2..189546edd9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/EventTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/event/EventTableInfo.java @@ -87,7 +87,8 @@ public String[] all() { EVENT_DATE, COMPLETE_DATE, DUE_DATE, - STATE, + SYNC_STATE, + AGGREGATED_SYNC_STATE, ATTRIBUTE_OPTION_COMBO, DELETED, ASSIGNED_USER diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/NewTrackerImporterEvent.java b/core/src/main/java/org/hisp/dhis/android/core/event/NewTrackerImporterEvent.java index 85dadcde56..f85b436203 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/NewTrackerImporterEvent.java +++ b/core/src/main/java/org/hisp/dhis/android/core/event/NewTrackerImporterEvent.java @@ -32,22 +32,25 @@ 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.gabrielittner.auto.value.cursor.ColumnName; import com.google.auto.value.AutoValue; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbDateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbGeometryColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.EventStatusColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.StateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNewTackerImporterTrackedEntityDataValueListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNewTrackerImporterNoteListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreRelationshipListColumnAdapter; import org.hisp.dhis.android.core.common.BaseDeletableDataObject; +import org.hisp.dhis.android.core.common.DataColumns; import org.hisp.dhis.android.core.common.Geometry; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.event.internal.EventFields; import org.hisp.dhis.android.core.note.NewTrackerImporterNote; import org.hisp.dhis.android.core.relationship.Relationship; @@ -58,7 +61,7 @@ @AutoValue @JsonDeserialize(builder = AutoValue_NewTrackerImporterEvent.Builder.class) -@SuppressWarnings({"PMD.GodClass"}) +@SuppressWarnings({"PMD.GodClass", "PMD.ExcessivePublicCount"}) public abstract class NewTrackerImporterEvent extends BaseDeletableDataObject implements ObjectWithUidInterface { @Override @@ -80,12 +83,12 @@ public abstract class NewTrackerImporterEvent extends BaseDeletableDataObject im public abstract Date updatedAt(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date createdAtClient(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date updatedAtClient(); @@ -149,6 +152,11 @@ public abstract class NewTrackerImporterEvent extends BaseDeletableDataObject im @ColumnAdapter(IgnoreRelationshipListColumnAdapter.class) abstract List relationships(); + @Nullable + @ColumnName(DataColumns.AGGREGATED_SYNC_STATE) + @ColumnAdapter(StateColumnAdapter.class) + public abstract State aggregatedSyncState(); + public static Builder builder() { return new $$AutoValue_NewTrackerImporterEvent.Builder(); } @@ -206,6 +214,8 @@ public abstract Builder trackedEntityDataValues( public abstract Builder relationships(List relationships); + public abstract Builder aggregatedSyncState(State aggregatedSyncState); + public abstract Geometry geometry(); public abstract NewTrackerImporterEvent build(); diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/NewTrackerImporterEventTransformer.kt b/core/src/main/java/org/hisp/dhis/android/core/event/NewTrackerImporterEventTransformer.kt index 0a69898b7c..bd5c5687bb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/NewTrackerImporterEventTransformer.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/NewTrackerImporterEventTransformer.kt @@ -50,7 +50,8 @@ internal class NewTrackerImporterEventTransformer : Transformer noteStore; - private final TrackedEntityInstanceStore trackedEntityInstanceStore; - private final ObjectStore trackerImportConflictStore; - private final TrackerImportConflictParser trackerImportConflictParser; - - @Inject - public EventImportHandler(@NonNull EventStore eventStore, - @NonNull EnrollmentStore enrollmentStore, - @NonNull IdentifiableObjectStore noteStore, - @NonNull TrackedEntityInstanceStore trackedEntityInstanceStore, - @NonNull ObjectStore trackerImportConflictStore, - @NonNull TrackerImportConflictParser trackerImportConflictParser) { - this.eventStore = eventStore; - this.enrollmentStore = enrollmentStore; - this.noteStore = noteStore; - this.trackedEntityInstanceStore = trackedEntityInstanceStore; - this.trackerImportConflictStore = trackerImportConflictStore; - this.trackerImportConflictParser = trackerImportConflictParser; - } - - public void handleEventImportSummaries(List eventImportSummaries, - List events, - String enrollmentUid, - String teiUid) { - State parentState = null; - - if (eventImportSummaries != null) { - for (EventImportSummary eventImportSummary : eventImportSummaries) { - String eventUid = eventImportSummary == null ? null : eventImportSummary.reference(); - - if (eventUid == null) { - continue; - } - - State state = getState(eventImportSummary.status()); - deleteEventConflicts(eventUid); - - HandleAction handleAction = eventStore.setStateOrDelete(eventUid, state); - - if (state == State.ERROR || state == State.WARNING) { - parentState = parentState == State.ERROR ? State.ERROR : state; - } - - if (handleAction != HandleAction.Delete) { - storeEventImportConflicts(eventImportSummary, teiUid, enrollmentUid); - handleNoteImportSummary(eventUid, state); - } - } - } - - List processedEvents = BaseImportSummaryHelper.getReferences(eventImportSummaries); - for (Event event : events) { - if (!processedEvents.contains(event.uid())) { - State state = State.TO_UPDATE; - eventStore.setStateOrDelete(event.uid(), state); - parentState = parentState == State.ERROR || parentState == State.WARNING ? parentState : state; - - deleteEventConflicts(event.uid()); - } - } - - updateParentState(parentState, teiUid, enrollmentUid); - } - - private void storeEventImportConflicts(EventImportSummary importSummary, - String teiUid, - String enrollmentUid) { - List trackerImportConflicts = new ArrayList<>(); - if (importSummary.description() != null) { - trackerImportConflicts.add(getConflictBuilder(teiUid, enrollmentUid, importSummary) - .conflict(importSummary.description()) - .displayDescription(importSummary.description()) - .value(importSummary.reference()) - .build()); - } - - if (importSummary.conflicts() != null) { - for (ImportConflict importConflict : importSummary.conflicts()) { - trackerImportConflicts.add(trackerImportConflictParser - .getEventConflict(importConflict, getConflictBuilder(teiUid, enrollmentUid, importSummary))); - } - - } - - for (TrackerImportConflict trackerImportConflict : trackerImportConflicts) { - trackerImportConflictStore.insert(trackerImportConflict); - } - } - - private void updateParentState(State parentState, String teiUid, String enrollmentUid) { - if (parentState != null) { - if (teiUid != null) { - trackedEntityInstanceStore.setState(teiUid, parentState); - } - if (enrollmentUid != null) { - enrollmentStore.setState(enrollmentUid, parentState); - } - } - } - - private void deleteEventConflicts(String eventUid) { - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(TrackerImportConflictTableInfo.Columns.EVENT, eventUid) - .appendKeyStringValue( - TrackerImportConflictTableInfo.Columns.TABLE_REFERENCE, - EventTableInfo.TABLE_INFO.name()) - .build(); - trackerImportConflictStore.deleteWhereIfExists(whereClause); - } - - private void handleNoteImportSummary(String eventUid, State state) { - State newNoteState = state.equals(State.SYNCED) ? State.SYNCED : State.TO_POST; - String whereClause = new WhereClauseBuilder() - .appendInKeyStringValues( - DataColumns.STATE, EnumHelper.asStringList(State.uploadableStatesIncludingError())) - .appendKeyStringValue(NoteTableInfo.Columns.EVENT, eventUid).build(); - List notes = noteStore.selectWhere(whereClause); - for (Note note : notes) { - noteStore.update(note.toBuilder().state(newNoteState).build()); - } - } - - private TrackerImportConflict.Builder getConflictBuilder(String trackedEntityInstanceUid, - String enrollmentUid, - EventImportSummary eventImportSummary) { - return TrackerImportConflict.builder() - .trackedEntityInstance(trackedEntityInstanceUid) - .enrollment(enrollmentUid) - .event(eventImportSummary.reference()) - .tableReference(EventTableInfo.TABLE_INFO.name()) - .status(eventImportSummary.status()) - .created(new Date()); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventImportHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventImportHandler.kt new file mode 100644 index 0000000000..6b1b04ddde --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventImportHandler.kt @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2004-2021, 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.event.internal + +import dagger.Reusable +import java.util.* +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreUtils.getSyncState +import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.internal.DataStatePropagator +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.EventTableInfo +import org.hisp.dhis.android.core.imports.TrackerImportConflict +import org.hisp.dhis.android.core.imports.internal.BaseImportSummaryHelper.getReferences +import org.hisp.dhis.android.core.imports.internal.EventImportSummary +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore +import org.hisp.dhis.android.core.tracker.importer.internal.JobReportEventHandler + +@Reusable +internal class EventImportHandler @Inject constructor( + private val eventStore: EventStore, + private val trackerImportConflictStore: TrackerImportConflictStore, + private val trackerImportConflictParser: TrackerImportConflictParser, + private val jobReportEventHandler: JobReportEventHandler, + private val dataStatePropagator: DataStatePropagator +) { + + fun handleEventImportSummaries( + eventImportSummaries: List?, + events: List, + enrollmentUid: String?, + teiUid: String? + ) { + eventImportSummaries?.filterNotNull()?.forEach { eventImportSummary -> + eventImportSummary.reference()?.let { eventUid -> + + val state = getSyncState(eventImportSummary.status()) + trackerImportConflictStore.deleteEventConflicts(eventUid) + + val handleAction = eventStore.setSyncStateOrDelete(eventUid, state) + + if (handleAction !== HandleAction.Delete) { + jobReportEventHandler.handleEventNotes(eventUid, state) + storeEventImportConflicts(eventImportSummary, teiUid, enrollmentUid) + + dataStatePropagator.refreshEventAggregatedSyncState(eventUid) + } + } + } + + val processedEvents = getReferences(eventImportSummaries) + + events.filterNot { processedEvents.contains(it.uid()) }.forEach { event -> + val state = State.TO_UPDATE + trackerImportConflictStore.deleteEventConflicts(event.uid()) + eventStore.setSyncStateOrDelete(event.uid(), state) + } + + enrollmentUid?.let { + dataStatePropagator.refreshEnrollmentAggregatedSyncState(it) + } + } + + private fun storeEventImportConflicts( + importSummary: EventImportSummary, + teiUid: String?, + enrollmentUid: String? + ) { + val trackerImportConflicts: MutableList = ArrayList() + + if (importSummary.description() != null) { + trackerImportConflicts.add( + getConflictBuilder(teiUid, enrollmentUid, importSummary) + .conflict(importSummary.description()) + .displayDescription(importSummary.description()) + .value(importSummary.reference()) + .build() + ) + } + + importSummary.conflicts()?.forEach { importConflict -> + trackerImportConflicts.add( + trackerImportConflictParser + .getEventConflict(importConflict, getConflictBuilder(teiUid, enrollmentUid, importSummary)) + ) + } + + trackerImportConflicts.forEach { trackerImportConflictStore.insert(it) } + } + + private fun getConflictBuilder( + trackedEntityInstanceUid: String?, + enrollmentUid: String?, + eventImportSummary: EventImportSummary + ): TrackerImportConflict.Builder { + return TrackerImportConflict.builder() + .trackedEntityInstance(trackedEntityInstanceUid) + .enrollment(enrollmentUid) + .event(eventImportSummary.reference()) + .tableReference(EventTableInfo.TABLE_INFO.name()) + .status(eventImportSummary.status()) + .created(Date()) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostNoteStore.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostNoteStore.kt index 1c5f5e2c2a..50aef2dd83 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostNoteStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostNoteStore.kt @@ -44,7 +44,7 @@ internal class EventPostNoteStore @Inject internal constructor( fun queryNotes(): List { val whereNotesClause = WhereClauseBuilder() .appendInKeyStringValues( - DataColumns.STATE, State.uploadableStatesIncludingError().map { it.name } + DataColumns.SYNC_STATE, State.uploadableStatesIncludingError().map { it.name } ) .appendKeyStringValue(NoteTableInfo.Columns.NOTE_TYPE, Note.NoteType.EVENT_NOTE) .build() diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostStateManager.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostStateManager.kt index 09b6636b06..a7ea02c9e0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostStateManager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostStateManager.kt @@ -41,7 +41,7 @@ internal class EventPostStateManager @Inject internal constructor( fun markObjectsAs(events: Collection, forcedState: State?) where T : ObjectWithUidInterface, T : DataObject { for (e in events) { - eventStore.setState(e.uid(), DataStateHelper.forcedOrOwn(e, forcedState)) + eventStore.setSyncState(e.uid(), DataStateHelper.forcedOrOwn(e, forcedState)) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventProjectionTransformer.java b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventProjectionTransformer.java index 4249672b8c..7806841a55 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventProjectionTransformer.java +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventProjectionTransformer.java @@ -46,7 +46,8 @@ public Event transform(EventCreateProjection projection) { return Event.builder() .uid(generatedUid) - .state(State.TO_POST) + .aggregatedSyncState(State.TO_POST) + .syncState(State.TO_POST) .created(creationDate) .lastUpdated(creationDate) .createdAtClient(creationDate) diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStore.java b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStore.kt similarity index 64% rename from core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStore.java rename to core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStore.kt index 3b711d880a..b2d2fac0c6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStore.kt @@ -25,30 +25,32 @@ * (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.event.internal -package org.hisp.dhis.android.core.event.internal; +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStore +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.event.Event -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStore; -import org.hisp.dhis.android.core.event.Event; +internal interface EventStore : IdentifiableDeletableDataObjectStore { + fun queryEventsAttachedToEnrollmentToPost(): Map> -import java.util.List; -import java.util.Map; + fun querySingleEventsToPost(): List -public interface EventStore extends IdentifiableDeletableDataObjectStore { + fun querySingleEvents(): List - Map> queryEventsAttachedToEnrollmentToPost(); + fun queryOrderedForEnrollmentAndProgramStage( + enrollmentUid: String, + programStageUid: String, + includeDeleted: Boolean + ): List - List querySingleEventsToPost(); + fun countEventsForEnrollment(enrollmentUid: String, includeDeleted: Boolean): Int - List querySingleEvents(); + fun countTeisWhereEvents(whereClause: String): Int - List queryOrderedForEnrollmentAndProgramStage(String enrollmentUid, - String programStageUid, - Boolean includeDeleted); + fun queryMissingRelationshipsUids(): List - Integer countEventsForEnrollment(String enrollmentUid, Boolean includeDeleted); + fun setAggregatedSyncState(uid: String, state: State): Int - int countTeisWhereEvents(String whereClause); - - List queryMissingRelationshipsUids(); + fun selectAggregatedSyncStateWhere(whereClause: String): List } diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStoreImpl.java deleted file mode 100644 index 7079151eb4..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStoreImpl.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.event.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.SQLStatementBuilderImpl; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinder; -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStoreImpl; -import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper; -import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper; -import org.hisp.dhis.android.core.common.DataColumns; -import org.hisp.dhis.android.core.common.IdentifiableColumns; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo; -import org.hisp.dhis.android.core.event.Event; -import org.hisp.dhis.android.core.event.EventTableInfo; -import org.hisp.dhis.android.core.event.EventTableInfo.Columns; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import kotlin.jvm.functions.Function1; - -public final class EventStoreImpl extends IdentifiableDeletableDataObjectStoreImpl implements EventStore { - - private static final String QUERY_SINGLE_EVENTS = "SELECT Event.* FROM Event WHERE Event.enrollment IS NULL"; - - private static final StatementBinder BINDER = (o, w) -> { - w.bind(1, o.uid()); - w.bind(2, o.enrollment()); - w.bind(3, o.created()); - w.bind(4, o.lastUpdated()); - w.bind(5, o.createdAtClient()); - w.bind(6, o.lastUpdatedAtClient()); - w.bind(7, o.status()); - w.bind(8, o.geometry() == null ? null : o.geometry().type()); - w.bind(9, o.geometry() == null ? null : o.geometry().coordinates()); - w.bind(10, o.program()); - w.bind(11, o.programStage()); - w.bind(12, o.organisationUnit()); - w.bind(13, o.eventDate()); - w.bind(14, o.completedDate()); - w.bind(15, o.dueDate()); - w.bind(16, o.state()); - w.bind(17, o.attributeOptionCombo()); - w.bind(18, o.deleted()); - w.bind(19, o.assignedUser()); - }; - - private EventStoreImpl(DatabaseAdapter databaseAdapter, - SQLStatementBuilderImpl builder, - StatementBinder binder, - Function1 objectFactory) { - super(databaseAdapter, builder, binder, objectFactory); - } - - @Override - public Map> queryEventsAttachedToEnrollmentToPost() { - String eventsAttachedToEnrollmentsQuery = new WhereClauseBuilder() - .appendIsNotNullValue(Columns.ENROLLMENT) - .appendInKeyStringValues(Columns.STATE, EnumHelper.asStringList(State.uploadableStates())).build(); - - List eventList = selectWhere(eventsAttachedToEnrollmentsQuery); - - Map> eventsMap = new HashMap<>(); - for (Event event : eventList) { - addEventsToMap(eventsMap, event); - } - - return eventsMap; - } - - @Override - public List querySingleEventsToPost() { - String states = CollectionsHelper.commaAndSpaceSeparatedArrayValues( - CollectionsHelper.withSingleQuotationMarksArray(EnumHelper.asStringList(State.uploadableStates()))); - String singleEventsToPostQuery = QUERY_SINGLE_EVENTS + - " AND (Event.state IN (" + states + "))"; - return eventListFromQuery(singleEventsToPostQuery); - } - - @Override - public List querySingleEvents() { - return eventListFromQuery(QUERY_SINGLE_EVENTS); - } - - @Override - public List queryOrderedForEnrollmentAndProgramStage(String enrollmentUid, String programStageUid, - Boolean includeDeleted) { - WhereClauseBuilder whereClause = new WhereClauseBuilder() - .appendKeyStringValue(Columns.ENROLLMENT, enrollmentUid) - .appendKeyStringValue(Columns.PROGRAM_STAGE, programStageUid); - - if (!includeDeleted) { - whereClause.appendIsNullOrValue(Columns.DELETED, "0"); - } - - String query = "SELECT * FROM " + EventTableInfo.TABLE_INFO.name() + " " + - "WHERE " + whereClause.build() + - "ORDER BY " + Columns.EVENT_DATE + ", " + Columns.LAST_UPDATED; - - return eventListFromQuery(query); - } - - @Override - public Integer countEventsForEnrollment(String enrollmentUid, Boolean includeDeleted) { - WhereClauseBuilder whereClause = new WhereClauseBuilder() - .appendKeyStringValue(Columns.ENROLLMENT, enrollmentUid); - - if (!includeDeleted) { - whereClause.appendIsNullOrValue(Columns.DELETED, "0"); - } - - String query = "SELECT * FROM " + EventTableInfo.TABLE_INFO.name() + " " + - "WHERE " + whereClause.build(); - - List events = eventListFromQuery(query); - return events.size(); - } - - @Override - public int countTeisWhereEvents(String whereClause) { - String whereStatement = whereClause == null ? "" : " WHERE " + whereClause; - String query = "SELECT COUNT(DISTINCT a." + EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE + ") " + - "FROM " + EnrollmentTableInfo.TABLE_INFO.name() + " a " + - "INNER JOIN " + - "(SELECT DISTINCT " + Columns.ENROLLMENT + - " FROM " + EventTableInfo.TABLE_INFO.name() + whereStatement + ") b " + - "ON a." + IdentifiableColumns.UID + " = b." + Columns.ENROLLMENT; - - return processCount(getDatabaseAdapter().rawQuery(query)); - } - - @Override - public List queryMissingRelationshipsUids() { - String whereRelationshipsClause = new WhereClauseBuilder() - .appendKeyStringValue(DataColumns.STATE, State.RELATIONSHIP) - .appendIsNullValue(EventTableInfo.Columns.ORGANISATION_UNIT) - .build(); - - return selectUidsWhere(whereRelationshipsClause); - } - - private List eventListFromQuery(String query) { - List eventList = new ArrayList<>(); - Cursor cursor = getDatabaseAdapter().rawQuery(query); - addObjectsToCollection(cursor, eventList); - return eventList; - } - - private void addEventsToMap(Map> eventsMap, Event event) { - if (eventsMap.get(event.enrollment()) == null) { - eventsMap.put(event.enrollment(), new ArrayList<>()); - } - - eventsMap.get(event.enrollment()).add(event); - } - - public static EventStore create(DatabaseAdapter databaseAdapter) { - SQLStatementBuilderImpl statementBuilder = new SQLStatementBuilderImpl( - EventTableInfo.TABLE_INFO.name(), - EventTableInfo.TABLE_INFO.columns()); - - return new EventStoreImpl( - databaseAdapter, - statementBuilder, - BINDER, - Event::create - ); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStoreImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStoreImpl.kt new file mode 100644 index 0000000000..6bddf81a30 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventStoreImpl.kt @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2004-2021, 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.event.internal + +import android.content.ContentValues +import android.database.Cursor +import java.util.* +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +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.IdentifiableDeletableDataObjectStoreImpl +import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper +import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper.asStringList +import org.hisp.dhis.android.core.common.DataColumns +import org.hisp.dhis.android.core.common.IdentifiableColumns +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.State.Companion.uploadableStates +import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.EventTableInfo + +@Suppress("TooManyFunctions") +internal class EventStoreImpl private constructor( + databaseAdapter: DatabaseAdapter, + builder: SQLStatementBuilderImpl, + binder: StatementBinder, + objectFactory: Function1 +) : IdentifiableDeletableDataObjectStoreImpl(databaseAdapter, builder, binder, objectFactory), EventStore { + + override fun queryEventsAttachedToEnrollmentToPost(): Map> { + val eventsAttachedToEnrollmentsQuery = WhereClauseBuilder() + .appendIsNotNullValue(EventTableInfo.Columns.ENROLLMENT) + .appendInKeyStringValues( + EventTableInfo.Columns.SYNC_STATE, asStringList(uploadableStates().toList()) + ).build() + val eventList = selectWhere(eventsAttachedToEnrollmentsQuery) + val eventsMap: MutableMap> = HashMap() + for (event in eventList) { + addEventsToMap(eventsMap, event) + } + return eventsMap + } + + override fun querySingleEventsToPost(): List { + val states = CollectionsHelper.commaAndSpaceSeparatedArrayValues( + CollectionsHelper.withSingleQuotationMarksArray(asStringList(uploadableStates().toList())) + ) + val singleEventsToPostQuery = QUERY_SINGLE_EVENTS + + " AND (" + EventTableInfo.Columns.SYNC_STATE + " IN (" + states + "))" + return eventListFromQuery(singleEventsToPostQuery) + } + + override fun querySingleEvents(): List { + return eventListFromQuery(QUERY_SINGLE_EVENTS) + } + + override fun queryOrderedForEnrollmentAndProgramStage( + enrollmentUid: String, + programStageUid: String, + includeDeleted: Boolean + ): List { + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(EventTableInfo.Columns.ENROLLMENT, enrollmentUid) + .appendKeyStringValue(EventTableInfo.Columns.PROGRAM_STAGE, programStageUid) + if (!includeDeleted) { + whereClause.appendIsNullOrValue(EventTableInfo.Columns.DELETED, "0") + } + val query = "SELECT * FROM " + EventTableInfo.TABLE_INFO.name() + " " + + "WHERE " + whereClause.build() + + "ORDER BY " + EventTableInfo.Columns.EVENT_DATE + ", " + EventTableInfo.Columns.LAST_UPDATED + return eventListFromQuery(query) + } + + override fun countEventsForEnrollment(enrollmentUid: String, includeDeleted: Boolean): Int { + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(EventTableInfo.Columns.ENROLLMENT, enrollmentUid) + if (!includeDeleted) { + whereClause.appendIsNullOrValue(EventTableInfo.Columns.DELETED, "0") + } + val query = "SELECT * FROM " + EventTableInfo.TABLE_INFO.name() + " " + + "WHERE " + whereClause.build() + val events = eventListFromQuery(query) + return events.size + } + + override fun countTeisWhereEvents(whereClause: String): Int { + val whereStatement = if (whereClause == null) "" else " WHERE $whereClause" + val query = "SELECT COUNT(DISTINCT a." + EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE + ") " + + "FROM " + EnrollmentTableInfo.TABLE_INFO.name() + " a " + + "INNER JOIN " + + "(SELECT DISTINCT " + EventTableInfo.Columns.ENROLLMENT + + " FROM " + EventTableInfo.TABLE_INFO.name() + whereStatement + ") b " + + "ON a." + IdentifiableColumns.UID + " = b." + EventTableInfo.Columns.ENROLLMENT + return processCount(databaseAdapter.rawQuery(query)) + } + + override fun queryMissingRelationshipsUids(): List { + val whereRelationshipsClause = WhereClauseBuilder() + .appendKeyStringValue(DataColumns.SYNC_STATE, State.RELATIONSHIP) + .appendIsNullValue(EventTableInfo.Columns.ORGANISATION_UNIT) + .build() + return selectUidsWhere(whereRelationshipsClause) + } + + override fun setAggregatedSyncState(uid: String, state: State): Int { + val updates = ContentValues() + updates.put(DataColumns.AGGREGATED_SYNC_STATE, state.toString()) + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(IdentifiableColumns.UID, uid) + .build() + + return updateWhere(updates, whereClause) + } + + override fun selectAggregatedSyncStateWhere(whereClause: String): List { + val statesStr = selectStringColumnsWhereClause(DataColumns.AGGREGATED_SYNC_STATE, whereClause) + + return statesStr.map { State.valueOf(it) } + } + + private fun eventListFromQuery(query: String): List { + val eventList: MutableList = ArrayList() + val cursor = databaseAdapter.rawQuery(query) + addObjectsToCollection(cursor, eventList) + return eventList + } + + private fun addEventsToMap(eventsMap: MutableMap>, event: Event) { + event.enrollment()?.let { enrollmentUid -> + if (eventsMap[enrollmentUid] == null) { + eventsMap[enrollmentUid] = ArrayList() + } + eventsMap[enrollmentUid]!!.add(event) + } + } + + companion object { + private const val QUERY_SINGLE_EVENTS = "SELECT Event.* FROM Event WHERE Event.enrollment IS NULL" + private val BINDER = StatementBinder { o: Event, w: StatementWrapper -> + w.bind(1, o.uid()) + w.bind(2, o.enrollment()) + w.bind(3, o.created()) + w.bind(4, o.lastUpdated()) + w.bind(5, o.createdAtClient()) + w.bind(6, o.lastUpdatedAtClient()) + w.bind(7, o.status()) + w.bind(8, if (o.geometry() == null) null else o.geometry()!!.type()) + w.bind(9, if (o.geometry() == null) null else o.geometry()!!.coordinates()) + w.bind(10, o.program()) + w.bind(11, o.programStage()) + w.bind(12, o.organisationUnit()) + w.bind(13, o.eventDate()) + w.bind(14, o.completedDate()) + w.bind(15, o.dueDate()) + w.bind(16, o.syncState()) + w.bind(17, o.aggregatedSyncState()) + w.bind(18, o.attributeOptionCombo()) + w.bind(19, o.deleted()) + w.bind(20, o.assignedUser()) + } + + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): EventStore { + val statementBuilder = SQLStatementBuilderImpl( + EventTableInfo.TABLE_INFO.name(), + EventTableInfo.TABLE_INFO.columns() + ) + return EventStoreImpl( + databaseAdapter, + statementBuilder, + BINDER + ) { cursor: Cursor? -> Event.create(cursor) } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventTrackerImporterPostCall.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventTrackerImporterPostCall.kt index 3149719926..e19ee5ee0b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventTrackerImporterPostCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventTrackerImporterPostCall.kt @@ -30,13 +30,19 @@ package org.hisp.dhis.android.core.event.internal import dagger.Reusable import io.reactivex.Observable import io.reactivex.Single +import java.util.Date import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor import org.hisp.dhis.android.core.arch.call.D2Progress +import org.hisp.dhis.android.core.arch.handlers.internal.Handler import org.hisp.dhis.android.core.arch.helpers.internal.DataStateHelper import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.NewTrackerImporterEvent +import org.hisp.dhis.android.core.trackedentity.internal.NewTrackerImporterPayload +import org.hisp.dhis.android.core.tracker.importer.internal.* import org.hisp.dhis.android.core.tracker.importer.internal.JobQueryCall +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType.EVENT import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterService @Reusable @@ -45,25 +51,57 @@ internal class EventTrackerImporterPostCall @Inject internal constructor( private val stateManager: EventPostStateManager, private val service: TrackerImporterService, private val apiCallExecutor: APICallExecutor, - private val jobQueryCall: JobQueryCall + private val jobQueryCall: JobQueryCall, + private val jobObjectHandler: Handler ) { fun uploadEvents( events: List ): Observable { return Observable.defer { val eventsToPost = payloadGenerator.getEvents(events) - stateManager.markObjectsAs(eventsToPost, State.UPLOADING) + val partition = eventsToPost.partition { it.deleted()!! } + Observable.concat( + doPostCall(partition.first, IMPORT_STRATEGY_DELETE), + doPostCall(partition.second, IMPORT_STRATEGY_CREATE_AND_UPDATE) + ) + } + } + + private fun generateJobObjects(events: List, jobUid: String): List { + val lastUpdated = Date() + return events.map { + TrackerJobObject + .builder() + .trackerType(EVENT) + .objectUid(it.uid()) + .jobUid(jobUid) + .lastUpdated(lastUpdated) + .build() + } + } + + private fun doPostCall(events: List, importStrategy: String): Observable { + return if (events.isEmpty()) { + Observable.empty() + } else { + stateManager.markObjectsAs(events, State.UPLOADING) Single.fromCallable { - val eventPayload = NewTrackerImporterEventPayload(eventsToPost) - val res = apiCallExecutor.executeObjectCall(service.postEvents(eventPayload)) - val jobId = res.response().uid() - jobQueryCall.storeJob(jobId) - jobId + doPostCallInternal(events, importStrategy) }.doOnError { - stateManager.markObjectsAs(eventsToPost, DataStateHelper.errorIfOnline(it)) + stateManager.markObjectsAs(events, DataStateHelper.errorIfOnline(it)) }.flatMapObservable { jobQueryCall.queryJob(it) } } } + + private fun doPostCallInternal(events: List, importStrategy: String): String { + val eventPayload = NewTrackerImporterPayload(events = events.toMutableList()) + val res = apiCallExecutor.executeObjectCall( + service.postEvents(eventPayload, ATOMIC_MODE_OBJECT, importStrategy) + ) + val jobId = res.response().uid() + jobObjectHandler.handleMany(generateJobObjects(events, jobId)) + return jobId + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/OldEventPostCall.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/OldEventPostCall.kt index 5932d61ac1..ea28c5c54e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/OldEventPostCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/OldEventPostCall.kt @@ -37,6 +37,8 @@ import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.imports.internal.EventWebResponse import org.hisp.dhis.android.core.systeminfo.DHISVersionManager +import org.hisp.dhis.android.core.trackedentity.internal.OldTrackerImporterPostCall +import org.hisp.dhis.android.core.trackedentity.internal.TrackerPostStateManager @Reusable internal class OldEventPostCall @Inject internal constructor( @@ -45,20 +47,34 @@ internal class OldEventPostCall @Inject internal constructor( private val eventService: EventService, private val apiCallExecutor: APICallExecutor, private val eventImportHandler: EventImportHandler, - private val stateManager: EventPostStateManager + private val trackerPostStateManager: TrackerPostStateManager, + private val oldTrackerImporterPostCall: OldTrackerImporterPostCall ) { + fun uploadEvents(events: List): Observable { + return if (versionManager.is2_29) { + uploadEvents29(events) + } else { + oldTrackerImporterPostCall.uploadEvents(events) + } + } + @Suppress("TooGenericExceptionCaught") - fun uploadEvents(filteredEvents: List): Observable { + private fun uploadEvents29( + events: List + ): Observable { return Observable.defer { val eventPayload = EventPayload() - val eventsToPost = payloadGenerator.getEvents(filteredEvents) - stateManager.markObjectsAs(eventsToPost, State.UPLOADING) + val eventsToPost = payloadGenerator.getEvents(events) + trackerPostStateManager.setPayloadStates( + events = eventsToPost, + forcedState = State.UPLOADING + ) val progressManager = D2ProgressManager(1) eventPayload.events = eventsToPost - val strategy = if (versionManager.is2_29) "CREATE_AND_UPDATE" else "SYNC" + val strategy = "CREATE_AND_UPDATE" try { val webResponse = apiCallExecutor.executeObjectCallWithAcceptedErrorCodes( eventService.postEvents(eventPayload, strategy), @@ -69,18 +85,15 @@ internal class OldEventPostCall @Inject internal constructor( handleWebResponse(webResponse, eventsToPost) Observable.just(progressManager.increaseProgress(Event::class.java, true)) } catch (e: Exception) { - stateManager.markObjectsAs(eventsToPost, State.TO_UPDATE) + trackerPostStateManager.restorePayloadStates(events = eventsToPost) Observable.error(e) } } } private fun handleWebResponse(webResponse: EventWebResponse?, events: List) { - if (webResponse?.response() == null) { - return - } eventImportHandler.handleEventImportSummaries( - webResponse.response()!!.importSummaries(), + webResponse?.response()?.importSummaries(), events, null, null diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/search/EventCollectionRepositoryAdapter.kt b/core/src/main/java/org/hisp/dhis/android/core/event/search/EventCollectionRepositoryAdapter.kt index 0ecdc8d5d8..25aed980ef 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/search/EventCollectionRepositoryAdapter.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/search/EventCollectionRepositoryAdapter.kt @@ -86,7 +86,7 @@ internal class EventCollectionRepositoryAdapter @Inject constructor( repository = repository.byDeleted().isFalse } - scope.states()?.let { repository = repository.byState().`in`(it) } + scope.states()?.let { repository = repository.bySyncState().`in`(it) } scope.attributeOptionCombos()?.let { repository = repository.byAttributeOptionComboUid().`in`(it) } return repository diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/FileResourceCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/fileresource/FileResourceCollectionRepository.java index 6038234813..5ffe4cdc73 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/FileResourceCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/FileResourceCollectionRepository.java @@ -91,7 +91,7 @@ public final class FileResourceCollectionRepository @Override public Observable upload() { - return Observable.fromCallable(() -> byState().in(State.uploadableStates()) + return Observable.fromCallable(() -> bySyncState().in(State.uploadableStates()) .blockingGetWithoutChildren()) .flatMap(postCall::uploadFileResources); } @@ -156,7 +156,17 @@ public StringFilterConnector byPath() { return cf.string(Columns.PATH); } + /** + * @deprecated Use {@link #bySyncState()} instead. + * + * @return + */ + @Deprecated public EnumFilterConnector byState() { - return cf.enumC(Columns.STATE); + return bySyncState(); + } + + public EnumFilterConnector bySyncState() { + return cf.enumC(Columns.SYNC_STATE); } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/FileResourceTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/fileresource/FileResourceTableInfo.java index c75d404878..835f29f3e8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/FileResourceTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/FileResourceTableInfo.java @@ -70,7 +70,7 @@ public String[] all() { CONTENT_TYPE, CONTENT_LENGTH, PATH, - STATE); + SYNC_STATE); } @Override diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceCallFactory.java b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceCallFactory.java index 293327945c..29fb6a06a0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceCallFactory.java +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceCallFactory.java @@ -116,7 +116,7 @@ private void downloadAttributeValueFiles(final List } handler.handleMany(fileResources, fileResource -> fileResource.toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) .build()); } @@ -146,7 +146,7 @@ private void downloadDataValueFiles(final List trackedEn } handler.handleMany(fileResources, fileResource -> fileResource.toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) .build()); } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourcePostCall.java b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourcePostCall.java index d7304c1228..257f3aa3c1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourcePostCall.java +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourcePostCall.java @@ -207,7 +207,7 @@ private File updateFile(File file, FileResource fileResource, Context context) { private void updateFileResource(FileResource fileResource, FileResource downloadedFileResource, File file) { fileResourceStore.delete(fileResource.uid()); fileResourceHandler.handle(downloadedFileResource.toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) .path(file.getAbsolutePath()) .build()); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceProjectionTransformer.java b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceProjectionTransformer.java index cf5895b526..1e13e36d61 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceProjectionTransformer.java +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceProjectionTransformer.java @@ -43,7 +43,7 @@ public FileResource transform(File file) { Date creationDate = new Date(); return FileResource.builder() - .state(State.TO_POST) + .syncState(State.TO_POST) .name(file.getName()) .created(creationDate) .lastUpdated(creationDate) diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceStoreImpl.java index 1e4f2b971b..1c119a9fed 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceStoreImpl.java @@ -49,7 +49,7 @@ public final class FileResourceStoreImpl extends IdentifiableDataObjectStoreImpl w.bind(5, o.contentType()); w.bind(6, o.contentLength()); w.bind(7, o.path()); - w.bind(8, o.state()); + w.bind(8, o.syncState()); }; private FileResourceStoreImpl(DatabaseAdapter databaseAdapter, diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/TrackerImportConflictCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/imports/TrackerImportConflictCollectionRepository.java index 06be63f366..515b7951da 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/imports/TrackerImportConflictCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/imports/TrackerImportConflictCollectionRepository.java @@ -28,7 +28,6 @@ package org.hisp.dhis.android.core.imports; -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; import org.hisp.dhis.android.core.arch.repositories.collection.internal.ReadOnlyCollectionRepositoryImpl; import org.hisp.dhis.android.core.arch.repositories.filters.internal.DateFilterConnector; @@ -36,6 +35,7 @@ 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.imports.internal.TrackerImportConflictStore; import java.util.Map; @@ -49,7 +49,7 @@ public final class TrackerImportConflictCollectionRepository @Inject TrackerImportConflictCollectionRepository( - final ObjectStore store, + final TrackerImportConflictStore store, final Map> childrenAppenders, final RepositoryScope scope) { super(store, childrenAppenders, scope, new FilterConnectorFactory<>(scope, diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipImportSummaries.java b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipImportSummaries.java new file mode 100644 index 0000000000..86400ceaf2 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipImportSummaries.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2021, 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.imports.internal; + +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_RelationshipImportSummaries.Builder.class) +public abstract class RelationshipImportSummaries + extends BaseImportSummaries + implements ImportSummaries { + + @Override + @Nullable + @JsonProperty() + public abstract List importSummaries(); + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public static abstract class Builder extends BaseImportSummaries.Builder { + + public abstract Builder importSummaries(List importSummaries); + + public abstract RelationshipImportSummaries build(); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipImportSummary.java b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipImportSummary.java new file mode 100644 index 0000000000..1c365ec3e0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipImportSummary.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2004-2021, 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.imports.internal; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.google.auto.value.AutoValue; + +@AutoValue +@JsonDeserialize(builder = AutoValue_RelationshipImportSummary.Builder.class) +public abstract class RelationshipImportSummary extends BaseImportSummary { + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public static abstract class Builder extends BaseImportSummary.Builder { + + public abstract RelationshipImportSummary build(); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TEIWebResponseHandler.java b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipWebResponse.java similarity index 58% rename from core/src/main/java/org/hisp/dhis/android/core/imports/internal/TEIWebResponseHandler.java rename to core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipWebResponse.java index 2e7560122b..3584f38aa2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TEIWebResponseHandler.java +++ b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/RelationshipWebResponse.java @@ -28,37 +28,39 @@ package org.hisp.dhis.android.core.imports.internal; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceImportHandler; +import androidx.annotation.Nullable; -import javax.inject.Inject; +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 androidx.annotation.NonNull; +@AutoValue +@JsonDeserialize(builder = AutoValue_RelationshipWebResponse.Builder.class) +public abstract class RelationshipWebResponse extends WebResponse { -import java.util.List; + @Nullable + @JsonProperty() + public abstract RelationshipImportSummaries response(); -import dagger.Reusable; - -@Reusable -public final class TEIWebResponseHandler { - private final TrackedEntityInstanceImportHandler trackedEntityInstanceImportHandler; - - @Inject - public TEIWebResponseHandler(@NonNull TrackedEntityInstanceImportHandler trackedEntityInstanceImportHandler) { - this.trackedEntityInstanceImportHandler = trackedEntityInstanceImportHandler; + public static Builder builder() { + return new AutoValue_RelationshipWebResponse.Builder(); } - public void handleWebResponse(@NonNull TEIWebResponse webResponse, - @NonNull List instances) { - if (webResponse == null || webResponse.response() == null) { - return; - } - - TEIImportSummaries importSummaries = webResponse.response(); + public static RelationshipWebResponse empty() { + return builder() + .httpStatus("SUCCESS") + .httpStatusCode(200) + .message("Emtpy response") + .status("OK") + .build(); + } - trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( - importSummaries.importSummaries(), instances - ); + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public static abstract class Builder extends WebResponse.Builder { + public abstract Builder response(RelationshipImportSummaries response); + public abstract RelationshipWebResponse build(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TEIWebResponseHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TEIWebResponseHandler.kt new file mode 100644 index 0000000000..0b622faa50 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TEIWebResponseHandler.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004-2021, 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.imports.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance +import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceImportHandler + +@Reusable +internal class TEIWebResponseHandler @Inject constructor( + private val trackedEntityInstanceImportHandler: TrackedEntityInstanceImportHandler +) { + + fun handleWebResponse( + webResponse: TEIWebResponse?, + instances: List + ) { + webResponse?.response()?.let { response -> + trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( + response.importSummaries(), instances + ) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictEntityDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictEntityDIModule.java index ae119e7785..a251d4941f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictEntityDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictEntityDIModule.java @@ -29,7 +29,6 @@ package org.hisp.dhis.android.core.imports.internal; import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; import org.hisp.dhis.android.core.imports.TrackerImportConflict; @@ -45,8 +44,8 @@ public final class TrackerImportConflictEntityDIModule { @Provides @Reusable - public ObjectStore store(DatabaseAdapter databaseAdapter) { - return TrackerImportConflictStore.create(databaseAdapter); + public TrackerImportConflictStore store(DatabaseAdapter databaseAdapter) { + return TrackerImportConflictStoreImpl.create(databaseAdapter); } @Provides diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStore.kt b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStore.kt new file mode 100644 index 0000000000..ae3329888a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStore.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-2021, 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.imports.internal + +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore +import org.hisp.dhis.android.core.imports.TrackerImportConflict + +internal interface TrackerImportConflictStore : ObjectStore { + fun deleteEventConflicts(eventUid: String) + fun deleteEnrollmentConflicts(enrollmentUid: String) + fun deleteTrackedEntityConflicts(tackedEntityUid: String) +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStoreImpl.java new file mode 100644 index 0000000000..a330535812 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/imports/internal/TrackerImportConflictStoreImpl.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2004-2021, 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.imports.internal; + +import androidx.annotation.NonNull; + +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl; +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; +import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinder; +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStoreImpl; +import org.hisp.dhis.android.core.arch.db.tableinfos.TableInfo; +import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo; +import org.hisp.dhis.android.core.event.EventTableInfo; +import org.hisp.dhis.android.core.imports.TrackerImportConflict; +import org.hisp.dhis.android.core.imports.TrackerImportConflictTableInfo; +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceTableInfo; +import org.jetbrains.annotations.NotNull; + +public final class TrackerImportConflictStoreImpl extends ObjectStoreImpl + implements TrackerImportConflictStore { + + private static final StatementBinder BINDER = (o, w) -> { + w.bind(1, o.conflict()); + w.bind(2, o.value()); + w.bind(3, o.trackedEntityInstance()); + w.bind(4, o.enrollment()); + w.bind(5, o.event()); + w.bind(6, o.trackedEntityAttribute()); + w.bind(7, o.dataElement()); + w.bind(8, o.tableReference()); + w.bind(9, o.errorCode()); + w.bind(10, o.status()); + w.bind(11, o.created()); + w.bind(12, o.displayDescription()); + }; + + private TrackerImportConflictStoreImpl(DatabaseAdapter databaseAdapter, + SQLStatementBuilderImpl builder) { + super(databaseAdapter, builder, BINDER, TrackerImportConflict::create); + } + + public static TrackerImportConflictStore create(DatabaseAdapter databaseAdapter) { + SQLStatementBuilderImpl statementBuilder = new SQLStatementBuilderImpl( + TrackerImportConflictTableInfo.TABLE_INFO.name(), + TrackerImportConflictTableInfo.TABLE_INFO.columns()); + + return new TrackerImportConflictStoreImpl(databaseAdapter, statementBuilder); + } + + @Override + public void deleteEventConflicts(@NotNull String eventUid) { + deleteTypeConflicts(TrackerImportConflictTableInfo.Columns.EVENT, + EventTableInfo.TABLE_INFO, + eventUid); + } + + @Override + public void deleteEnrollmentConflicts(@NotNull String enrollmentUid) { + deleteTypeConflicts(TrackerImportConflictTableInfo.Columns.ENROLLMENT, + EnrollmentTableInfo.TABLE_INFO, + enrollmentUid); + } + + @Override + public void deleteTrackedEntityConflicts(@NonNull String teiUid) { + deleteTypeConflicts(TrackerImportConflictTableInfo.Columns.TRACKED_ENTITY_INSTANCE, + TrackedEntityInstanceTableInfo.TABLE_INFO, + teiUid); + } + + private void deleteTypeConflicts(String column, TableInfo tableInfo, String uid) { + String whereClause = new WhereClauseBuilder() + .appendKeyStringValue(column, uid) + .appendKeyStringValue( + TrackerImportConflictTableInfo.Columns.TABLE_REFERENCE, + tableInfo.name()) + .build(); + deleteWhereIfExists(whereClause); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java index f631587fe9..2e55cbff7a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java +++ b/core/src/main/java/org/hisp/dhis/android/core/maintenance/D2ErrorCode.java @@ -50,6 +50,7 @@ public enum D2ErrorCode { FILE_NOT_FOUND, FAIL_RESIZING_IMAGE, IMPOSSIBLE_TO_GENERATE_COORDINATES, + JOB_REPORT_NOT_AVAILABLE, LOGIN_USERNAME_NULL, LOGIN_PASSWORD_NULL, MIGHT_BE_RUNNING_LOW_ON_AVAILABLE_VALUES, 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 781d641650..5756684cc2 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 @@ -63,7 +63,7 @@ public class Dhis2MockServer { private static final String PROGRAM_SETTINGS_JSON = "settings/program_settings.json"; private static final String SYNCHRONIZATION_SETTTINGS_JSON = "settings/synchronization_settings.json"; private static final String APPEARANCE_SETTINGS_JSON = "settings/appearance_settings.json"; - private static final String ANALYTICS_SETTINGS_JSON = "settings/analytics_settings.json"; + private static final String ANALYTICS_SETTINGS_JSON = "settings/analytics_settings_v2.json"; private static final String USER_SETTINGS_JSON = "settings/user_settings.json"; private static final String PROGRAMS_JSON = "program/programs.json"; private static final String PROGRAM_STAGES_JSON = "program/program_stages.json"; @@ -86,6 +86,7 @@ public class Dhis2MockServer { private static final String CATEGORY_COMBOS_JSON = "category/category_combos.json"; private static final String CATEGORIES_JSON = "category/categories.json"; private static final String CATEGORY_OPTIONS_JSON = "category/category_options.json"; + private static final String VISUALIZATIONS_JSON = "visualization/visualizations.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/user.json"; @@ -229,6 +230,8 @@ public MockResponse dispatch(RecordedRequest request) { return createMockResponse(CATEGORIES_JSON); } else if (path.startsWith("/api/categoryOptions?")) { return createMockResponse(CATEGORY_OPTIONS_JSON); + } else if (path.startsWith("/api/visualizations?")) { + return createMockResponse(VISUALIZATIONS_JSON); } else if (path.startsWith("/api/organisationUnits?")) { return createMockResponse(ORGANISATION_UNITS_JSON); } else if (path.startsWith("/api/organisationUnitLevels?")) { @@ -306,6 +309,7 @@ public void enqueueMetadataResponses() { enqueueMockResponse(CATEGORY_COMBOS_JSON); enqueueMockResponse(CATEGORIES_JSON); enqueueMockResponse(CATEGORY_OPTIONS_JSON); + enqueueMockResponse(VISUALIZATIONS_JSON); } @NonNull diff --git a/core/src/main/java/org/hisp/dhis/android/core/note/NewTrackerImporterNoteTransformer.kt b/core/src/main/java/org/hisp/dhis/android/core/note/NewTrackerImporterNoteTransformer.kt index 0f3d2c18a2..fd9bcc5727 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/note/NewTrackerImporterNoteTransformer.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/note/NewTrackerImporterNoteTransformer.kt @@ -41,7 +41,7 @@ internal class NewTrackerImporterNoteTransformer : Transformer byStoredDate() { return cf.string(Columns.STORED_DATE); } + public EnumFilterConnector bySyncState() { + return cf.enumC(Columns.SYNC_STATE); + } + @Override public ReadOnlyObjectRepository uid(String uid) { RepositoryScope updatedScope = RepositoryScopeHelper.withUidFilterItem(scope, uid); diff --git a/core/src/main/java/org/hisp/dhis/android/core/note/NoteTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/note/NoteTableInfo.java index 4da0439d55..78a18f18f5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/note/NoteTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/note/NoteTableInfo.java @@ -70,7 +70,7 @@ public String[] all() { STORED_BY, STORED_DATE, IdentifiableColumns.UID, - STATE, + SYNC_STATE, DELETED ); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/note/internal/NoteProjectionTransformer.java b/core/src/main/java/org/hisp/dhis/android/core/note/internal/NoteProjectionTransformer.java index 8e2f5e3a83..4c4b911b53 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/note/internal/NoteProjectionTransformer.java +++ b/core/src/main/java/org/hisp/dhis/android/core/note/internal/NoteProjectionTransformer.java @@ -57,7 +57,7 @@ final class NoteProjectionTransformer implements Transformer buildUniqueCollection(Collection notes, Note.NoteType not NoteTableInfo.Columns.EVENT; String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(DataColumns.STATE, State.TO_POST) + .appendKeyStringValue(DataColumns.SYNC_STATE, State.TO_POST) .appendKeyStringValue(ownerColumn, ownerUid).build(); List toPostNotes = noteStore.selectWhere(whereClause); @@ -76,7 +76,7 @@ public Set buildUniqueCollection(Collection notes, Note.NoteType not for (Note note : notes) { uniqueNotes.add(note.toBuilder() .id(null) - .state(State.SYNCED) + .syncState(State.SYNCED) .build()); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCall.java b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCall.java index fa2bd84767..def77af6fd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCall.java +++ b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCall.java @@ -30,6 +30,7 @@ import androidx.annotation.NonNull; import org.hisp.dhis.android.core.arch.api.payload.internal.Payload; +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner; import org.hisp.dhis.android.core.arch.helpers.UidsHelper; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; import org.hisp.dhis.android.core.user.User; @@ -57,15 +58,18 @@ class OrganisationUnitCall { private final OrganisationUnitService organisationUnitService; private final OrganisationUnitHandler handler; private final OrganisationUnitDisplayPathTransformer pathTransformer; + private final CollectionCleaner collectionCleaner; @Inject OrganisationUnitCall(@NonNull OrganisationUnitService organisationUnitService, @NonNull OrganisationUnitHandler handler, - @NonNull OrganisationUnitDisplayPathTransformer pathTransformer) { + @NonNull OrganisationUnitDisplayPathTransformer pathTransformer, + @NonNull CollectionCleaner collectionCleaner) { this.organisationUnitService = organisationUnitService; this.handler = handler; this.pathTransformer = pathTransformer; + this.collectionCleaner = collectionCleaner; } public Single> download(final User user) { @@ -77,6 +81,7 @@ public Single> download(final User user) { return downloadSearchOrgUnits(rootSearchOrgUnits, user).flatMap(searchOrgUnits -> downloadDataCaptureOrgUnits(rootSearchOrgUnits, searchOrgUnits, user).map(dataCaptureOrgUnits -> { searchOrgUnits.addAll(dataCaptureOrgUnits); + collectionCleaner.deleteNotPresent(searchOrgUnits); return searchOrgUnits; })); }); diff --git a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitEntityDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitEntityDIModule.java index 9fafb6509b..e85ac64b6c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitEntityDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitEntityDIModule.java @@ -28,11 +28,14 @@ package org.hisp.dhis.android.core.organisationunit.internal; +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner; +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleanerImpl; 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.di.internal.IdentifiableStoreProvider; import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitTableInfo; import java.util.HashMap; import java.util.Map; @@ -57,6 +60,12 @@ public OrganisationUnitHandler handler(DatabaseAdapter databaseAdapter) { return OrganisationUnitHandlerImpl.create(databaseAdapter); } + @Provides + @Reusable + public CollectionCleaner collectionCleaner(DatabaseAdapter databaseAdapter) { + return new CollectionCleanerImpl<>(OrganisationUnitTableInfo.TABLE_INFO.name(), databaseAdapter); + } + @Provides @Reusable public OrganisationUnitDisplayPathTransformer pathTransformer() { diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationship.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationship.java new file mode 100644 index 0000000000..be6137df6c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationship.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2004-2021, 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.relationship; + +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.custom.internal.DbDateColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNewTrackerImporterRelationshipItemAdapter; +import org.hisp.dhis.android.core.common.BaseDeletableDataObject; +import org.hisp.dhis.android.core.common.ObjectWithUidInterface; + +import java.util.Date; + +@AutoValue +@JsonDeserialize(builder = AutoValue_NewTrackerImporterRelationship.Builder.class) +public abstract class NewTrackerImporterRelationship extends BaseDeletableDataObject implements ObjectWithUidInterface { + + @Override + @JsonProperty("relationship") + public abstract String uid(); + + @Nullable + @JsonProperty() + public abstract String relationshipType(); + + @Nullable + @JsonProperty() + public abstract String relationshipName(); + + @Nullable + @JsonProperty() + @ColumnAdapter(DbDateColumnAdapter.class) + public abstract Date createdAt(); + + @Nullable + @JsonProperty() + @ColumnAdapter(DbDateColumnAdapter.class) + public abstract Date updatedAt(); + + @Nullable + @JsonProperty() + public abstract Boolean bidirectional(); + + @Nullable + @JsonProperty() + @ColumnAdapter(IgnoreNewTrackerImporterRelationshipItemAdapter.class) + public abstract NewTrackerImporterRelationshipItem from(); + + @Nullable + @JsonProperty() + @ColumnAdapter(IgnoreNewTrackerImporterRelationshipItemAdapter.class) + public abstract NewTrackerImporterRelationshipItem to(); + + public static Builder builder() { + return new $$AutoValue_NewTrackerImporterRelationship.Builder(); + } + + public static NewTrackerImporterRelationship create(Cursor cursor) { + return $AutoValue_NewTrackerImporterRelationship.createFromCursor(cursor); + } + + public abstract Builder toBuilder(); + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public abstract static class Builder extends BaseDeletableDataObject.Builder { + public abstract Builder id(Long id); + + @JsonProperty("relationship") + public abstract Builder uid(String uid); + + public abstract Builder relationshipType(String relationshipType); + + public abstract Builder relationshipName(String relationshipName); + + public abstract Builder createdAt(Date createdAt); + + public abstract Builder updatedAt(Date lastUpdatedAt); + + public abstract Builder bidirectional(Boolean bidirectional); + + public abstract Builder from(NewTrackerImporterRelationshipItem from); + + public abstract Builder to(NewTrackerImporterRelationshipItem to); + + public abstract NewTrackerImporterRelationship build(); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationshipItem.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationshipItem.java new file mode 100644 index 0000000000..4151ea1200 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationshipItem.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2004-2021, 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.relationship; + +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.enums.internal.RelationshipConstraintTypeColumnAdapter; +import org.hisp.dhis.android.core.common.CoreObject; + +@AutoValue +@JsonDeserialize(builder = AutoValue_NewTrackerImporterRelationshipItem.Builder.class) +public abstract class NewTrackerImporterRelationshipItem implements CoreObject { + + @Nullable + @JsonIgnore() + public abstract String relationship(); + + @Nullable + @JsonIgnore() + @ColumnAdapter(RelationshipConstraintTypeColumnAdapter.class) + public abstract RelationshipConstraintType relationshipItemType(); + + @Nullable + @JsonProperty() + public abstract String trackedEntity(); + + @Nullable + @JsonProperty() + public abstract String enrollment(); + + @Nullable + @JsonProperty() + public abstract String event(); + + public static Builder builder() { + return new $$AutoValue_NewTrackerImporterRelationshipItem.Builder(); + } + + public static NewTrackerImporterRelationshipItem create(Cursor cursor) { + return $AutoValue_NewTrackerImporterRelationshipItem.createFromCursor(cursor); + } + + public abstract Builder toBuilder(); + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public abstract static class Builder { + + public abstract Builder id(Long id); + + public abstract Builder relationship(String relationship); + + public abstract Builder relationshipItemType(RelationshipConstraintType relationshipItemType); + + public abstract Builder trackedEntity(String relationship); + + public abstract Builder enrollment(String relationship); + + public abstract Builder event(String relationship); + + public abstract NewTrackerImporterRelationshipItem build(); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationshipTransformer.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationshipTransformer.kt new file mode 100644 index 0000000000..b263e396f2 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/NewTrackerImporterRelationshipTransformer.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004-2021, 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.relationship + +import org.hisp.dhis.android.core.arch.handlers.internal.Transformer + +internal class NewTrackerImporterRelationshipTransformer : Transformer { + override fun transform(o: Relationship): NewTrackerImporterRelationship { + return NewTrackerImporterRelationship.builder() + .id(o.id()) + .uid(o.uid()) + .relationshipType(o.relationshipType()) + .relationshipName(o.name()) + .createdAt(o.created()) + .updatedAt(o.lastUpdated()) + .from(getRelationshipItem(o.from())) + .to(getRelationshipItem(o.to())) + .deleted(o.deleted()) + .syncState(o.syncState()) + .build() + } + + private fun getRelationshipItem(item: RelationshipItem?): NewTrackerImporterRelationshipItem? { + return item?.let { + val builder = NewTrackerImporterRelationshipItem.builder() + .relationship(item.relationship()?.uid()) + .relationshipItemType(item.relationshipItemType()) + + when { + item.hasTrackedEntityInstance() -> builder.trackedEntity(item.elementUid()).build() + item.hasEnrollment() -> builder.enrollment(item.elementUid()).build() + item.hasEvent() -> builder.event(item.elementUid()).build() + else -> null + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipCollectionRepository.java index 4ec7d98255..59fa8dbce4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipCollectionRepository.java @@ -27,15 +27,19 @@ */ package org.hisp.dhis.android.core.relationship; +import static org.hisp.dhis.android.core.arch.helpers.CollectionsHelper.isDeleted; +import static org.hisp.dhis.android.core.relationship.RelationshipConstraintType.FROM; +import static org.hisp.dhis.android.core.relationship.RelationshipConstraintType.TO; + import androidx.annotation.NonNull; import org.hisp.dhis.android.core.arch.db.stores.internal.StoreWithState; import org.hisp.dhis.android.core.arch.helpers.UidGeneratorImpl; -import org.hisp.dhis.android.core.arch.helpers.UidsHelper; import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; import org.hisp.dhis.android.core.arch.repositories.collection.ReadWriteWithUidCollectionRepository; import org.hisp.dhis.android.core.arch.repositories.collection.internal.BaseReadOnlyWithUidCollectionRepositoryImpl; import org.hisp.dhis.android.core.arch.repositories.filters.internal.DateFilterConnector; +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.StringFilterConnector; import org.hisp.dhis.android.core.arch.repositories.object.ReadWriteObjectRepository; @@ -53,7 +57,6 @@ import org.hisp.dhis.android.core.relationship.internal.RelationshipStore; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; @@ -62,11 +65,6 @@ import dagger.Reusable; import io.reactivex.Single; -import static org.hisp.dhis.android.core.arch.helpers.CollectionsHelper.isDeleted; -import static org.hisp.dhis.android.core.relationship.RelationshipConstraintType.FROM; -import static org.hisp.dhis.android.core.relationship.RelationshipConstraintType.TO; -import static org.hisp.dhis.android.core.relationship.RelationshipHelper.areItemsEqual; - @Reusable @SuppressWarnings("PMD.ExcessiveImports") public class RelationshipCollectionRepository @@ -105,8 +103,6 @@ public Single add(Relationship relationship) { @Override public String blockingAdd(Relationship relationship) throws D2Error { Relationship relationshipWithUid; - RelationshipItem from = relationship.from(); - RelationshipItem to = relationship.to(); if (relationshipHandler.doesRelationshipExist(relationship)) { throw D2Error .builder() @@ -114,14 +110,15 @@ public String blockingAdd(Relationship relationship) throws D2Error { .errorCode(D2ErrorCode.CANT_CREATE_EXISTING_OBJECT) .errorDescription("Tried to create already existing Relationship: " + relationship) .build(); - } else if (from == null || !from.hasTrackedEntityInstance() || to == null || !to.hasTrackedEntityInstance()) { + } else if (relationship.from() == null || relationship.to() == null) { throw D2Error .builder() .errorComponent(D2ErrorComponent.SDK) .errorCode(D2ErrorCode.CANT_CREATE_EXISTING_OBJECT) - .errorDescription("Only TEI-TEI relationships creation supported") + .errorDescription("Relationship is missing either 'from' or 'to' component.") .build(); } else { + RelationshipItem from = relationship.from(); if (relationship.uid() == null) { String generatedUid = new UidGeneratorImpl().generate(); relationshipWithUid = relationship.toBuilder().uid(generatedUid).build(); @@ -130,11 +127,11 @@ public String blockingAdd(Relationship relationship) throws D2Error { } StoreWithState fromStore = storeSelector.getElementStore(from); - State fromState = fromStore.getState(from.elementUid()); + State fromState = fromStore.getSyncState(from.elementUid()); if (isUpdatableState(fromState)) { relationshipHandler.handle(relationshipWithUid, r -> r.toBuilder() - .state(State.TO_POST) + .syncState(State.TO_POST) .deleted(false) .build()); dataStatePropagator.propagateRelationshipUpdate(from); @@ -168,64 +165,46 @@ public List getByItem(@NonNull RelationshipItem searchItem) { public List getByItem(@NonNull RelationshipItem searchItem, Boolean includeDeleted) { - // TODO Create query to avoid retrieving the whole table - List relationshipItems = this.relationshipItemStore.selectAll(); - - List allRelationshipsFromDb = this.store.selectAll(); - List relationships = new ArrayList<>(); + List relationshipItems = this.relationshipItemStore.getByItem(searchItem); + for (RelationshipItem iterationItem : relationshipItems) { - if (areItemsEqual(searchItem, iterationItem)) { - Relationship relationshipFromDb = - UidsHelper.findByUid(allRelationshipsFromDb, iterationItem.relationship().uid()); - - if (relationshipFromDb == null) { - continue; - } - - if (!includeDeleted && isDeleted(relationshipFromDb)) { - continue; - } - - RelationshipConstraintType itemType = iterationItem.relationshipItemType(); - - RelationshipItem relatedItem = findRelatedTEI(relationshipItems, - iterationItem.relationship().uid(), itemType == FROM ? TO : FROM); - - if (relatedItem == null) { - continue; - } - - RelationshipItem from, to; - if (itemType == FROM) { - from = iterationItem; - to = relatedItem; - } else { - from = relatedItem; - to = iterationItem; - } - - Relationship relationship = relationshipFromDb.toBuilder() - .from(from) - .to(to) - .build(); + Relationship relationship = this.store.selectByUid(iterationItem.relationship().uid()); - relationships.add(relationship); + if (relationship == null) { + continue; } - } - return relationships; - } + if (!includeDeleted && isDeleted(relationship)) { + continue; + } + + RelationshipConstraintType relatedType = iterationItem.relationshipItemType() == FROM ? TO : FROM; - private RelationshipItem findRelatedTEI(Collection items, String relationshipUid, - RelationshipConstraintType type) { - for (RelationshipItem item : items) { - if (relationshipUid.equals(item.relationship().uid()) && item.relationshipItemType() == type) { - return item; + RelationshipItem relatedItem = this.relationshipItemStore + .getForRelationshipUidAndConstraintType(relationship.uid(), relatedType); + + if (relatedItem == null) { + continue; } + + RelationshipItem from, to; + if (iterationItem.relationshipItemType() == FROM) { + from = iterationItem; + to = relatedItem; + } else { + from = relatedItem; + to = iterationItem; + } + + relationships.add(relationship.toBuilder() + .from(from) + .to(to) + .build()); } - return null; + + return relationships; } public StringFilterConnector byUid() { @@ -248,6 +227,10 @@ public StringFilterConnector byRelationshipTyp return cf.string(RelationshipTableInfo.Columns.RELATIONSHIP_TYPE); } + public EnumFilterConnector bySyncState() { + return cf.enumC(RelationshipTableInfo.Columns.SYNC_STATE); + } + public RelationshipCollectionRepository withItems() { return cf.withChild(RelationshipFields.ITEMS); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipHelper.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipHelper.java index b8b286bdb8..6e8d755152 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipHelper.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipHelper.java @@ -69,11 +69,76 @@ public static RelationshipItem eventItem(String uid) { } public static Relationship teiToTeiRelationship(String fromUid, String toUid, String relationshipTypeUid) { + return relationship( + RelationshipHelper.teiItem(fromUid), + RelationshipHelper.teiItem(toUid), + relationshipTypeUid); + } + + public static Relationship teiToEnrollmentRelationship(String fromUid, String toUid, String relationshipTypeUid) { + return relationship( + RelationshipHelper.teiItem(fromUid), + RelationshipHelper.enrollmentItem(toUid), + relationshipTypeUid); + } + + public static Relationship teiToEventRelationship(String fromUid, String toUid, String relationshipTypeUid) { + return relationship( + RelationshipHelper.teiItem(fromUid), + RelationshipHelper.eventItem(toUid), + relationshipTypeUid); + } + + public static Relationship enrollmentToTeiRelationship(String fromUid, String toUid, String relationshipTypeUid) { + return relationship( + RelationshipHelper.enrollmentItem(fromUid), + RelationshipHelper.teiItem(toUid), + relationshipTypeUid); + } + + public static Relationship enrollmentToEnrollmentRelationship(String fromUid, + String toUid, + String relationshipTypeUid) { + return relationship( + RelationshipHelper.enrollmentItem(fromUid), + RelationshipHelper.enrollmentItem(toUid), + relationshipTypeUid); + } + + public static Relationship enrollmentToEventRelationship(String fromUid, String toUid, String relationshipTypeUid) { + return relationship( + RelationshipHelper.enrollmentItem(fromUid), + RelationshipHelper.eventItem(toUid), + relationshipTypeUid); + } + + public static Relationship eventToTeiRelationship(String fromUid, String toUid, String relationshipTypeUid) { + return relationship( + RelationshipHelper.eventItem(fromUid), + RelationshipHelper.teiItem(toUid), + relationshipTypeUid); + } + + public static Relationship eventToEnrollmentRelationship(String fromUid, String toUid, String relationshipTypeUid) { + return relationship( + RelationshipHelper.eventItem(fromUid), + RelationshipHelper.enrollmentItem(toUid), + relationshipTypeUid); + } + + public static Relationship eventToEventRelationship(String fromUid, String toUid, String relationshipTypeUid) { + return relationship( + RelationshipHelper.eventItem(fromUid), + RelationshipHelper.eventItem(toUid), + relationshipTypeUid); + } + + public static Relationship relationship(RelationshipItem from, RelationshipItem to, String type) { return Relationship.builder() .uid(new UidGeneratorImpl().generate()) - .from(RelationshipHelper.teiItem(fromUid)) - .to(RelationshipHelper.teiItem(toUid)) - .relationshipType(relationshipTypeUid) + .from(from) + .to(to) + .relationshipType(type) .build(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTableInfo.java index 54b31e431a..fb359e507d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTableInfo.java @@ -67,7 +67,7 @@ public String[] all() { CREATED, LAST_UPDATED, RELATIONSHIP_TYPE, - STATE, + SYNC_STATE, DELETED ); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepository.java deleted file mode 100644 index 2267d1ee38..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepository.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.relationship; - -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -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.scope.RepositoryScope; -import org.hisp.dhis.android.core.common.IdentifiableColumns; -import org.hisp.dhis.android.core.relationship.internal.RelationshipTypeFields; - -import java.util.Map; - -import javax.inject.Inject; - -import dagger.Reusable; - -@Reusable -public final class RelationshipTypeCollectionRepository - extends ReadOnlyIdentifiableCollectionRepositoryImpl { - - @Inject - RelationshipTypeCollectionRepository(final IdentifiableObjectStore store, - final Map> childrenAppenders, - final RepositoryScope scope) { - super(store, childrenAppenders, scope, new FilterConnectorFactory<>(scope, - s -> new RelationshipTypeCollectionRepository(store, childrenAppenders, s))); - } - - public RelationshipTypeCollectionRepository byConstraint(@NonNull RelationshipEntityType relationshipEntityType, - @NonNull String relationshipEntityUid) { - return cf.subQuery(IdentifiableColumns.UID).inTableWhere( - RelationshipConstraintTableInfo.TABLE_INFO.name(), - RelationshipConstraintTableInfo.Columns.RELATIONSHIP_TYPE, - constraintClauseBuilder(relationshipEntityType, relationshipEntityUid)); - } - - public RelationshipTypeCollectionRepository byConstraint( - @NonNull RelationshipEntityType relationshipEntityType, - @NonNull String relationshipEntityUid, - @NonNull RelationshipConstraintType relationshipConstraintType) { - return cf.subQuery(IdentifiableColumns.UID).inTableWhere( - RelationshipConstraintTableInfo.TABLE_INFO.name(), - RelationshipConstraintTableInfo.Columns.RELATIONSHIP_TYPE, - constraintClauseBuilder(relationshipEntityType, relationshipEntityUid).appendKeyStringValue( - RelationshipConstraintTableInfo.Columns.CONSTRAINT_TYPE, relationshipConstraintType)); - } - - public RelationshipTypeCollectionRepository withConstraints() { - return cf.withChild(RelationshipTypeFields.CONSTRAINTS); - } - - private WhereClauseBuilder constraintClauseBuilder(RelationshipEntityType relationshipEntityType, - String relationshipEntityUid) { - return new WhereClauseBuilder() - .appendKeyStringValue( - RelationshipConstraintTableInfo.Columns.RELATIONSHIP_ENTITY, relationshipEntityType) - .appendKeyStringValue(getRelationshipEntityColumn(relationshipEntityType), relationshipEntityUid); - } - - private String getRelationshipEntityColumn(@NonNull RelationshipEntityType relationshipEntityType) { - switch (relationshipEntityType) { - case TRACKED_ENTITY_INSTANCE: - return RelationshipConstraintTableInfo.Columns.TRACKED_ENTITY_TYPE; - case PROGRAM_INSTANCE: - return RelationshipConstraintTableInfo.Columns.PROGRAM; - case PROGRAM_STAGE_INSTANCE: - return RelationshipConstraintTableInfo.Columns.PROGRAM_STAGE; - default: - return null; - } - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepository.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepository.kt new file mode 100644 index 0000000000..4cac78f453 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/RelationshipTypeCollectionRepository.kt @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2004-2021, 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.relationship + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +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.BooleanFilterConnector +import org.hisp.dhis.android.core.arch.repositories.filters.internal.FilterConnectorFactory +import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope +import org.hisp.dhis.android.core.arch.repositories.scope.internal.FilterItemOperator +import org.hisp.dhis.android.core.common.IdentifiableColumns +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore +import org.hisp.dhis.android.core.event.internal.EventStore +import org.hisp.dhis.android.core.relationship.internal.RelationshipTypeCollectionRepositoryHelper.availableForEnrollmentRawQuery +import org.hisp.dhis.android.core.relationship.internal.RelationshipTypeCollectionRepositoryHelper.availableForEventRawQuery +import org.hisp.dhis.android.core.relationship.internal.RelationshipTypeCollectionRepositoryHelper.availableForTrackedEntityInstanceRawQuery +import org.hisp.dhis.android.core.relationship.internal.RelationshipTypeFields +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager +import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStore + +@Reusable +class RelationshipTypeCollectionRepository @Inject internal constructor( + store: IdentifiableObjectStore, + private val teiStore: TrackedEntityInstanceStore, + private val enrollmentStore: EnrollmentStore, + private val eventStore: EventStore, + private val dhisVersionManager: DHISVersionManager, + childrenAppenders: MutableMap>, + scope: RepositoryScope +) : ReadOnlyIdentifiableCollectionRepositoryImpl( + store, + childrenAppenders, + scope, + FilterConnectorFactory(scope) { s: RepositoryScope -> + RelationshipTypeCollectionRepository( + store, teiStore, enrollmentStore, eventStore, + dhisVersionManager, childrenAppenders, s + ) + } +) { + fun byBidirectional(): BooleanFilterConnector { + return cf.bool(RelationshipTypeTableInfo.Columns.BIDIRECTIONAL) + } + + @JvmOverloads + fun byConstraint( + relationshipEntityType: RelationshipEntityType, + relationshipEntityUid: String, + relationshipConstraintType: RelationshipConstraintType? = null + ): RelationshipTypeCollectionRepository { + return cf.subQuery(IdentifiableColumns.UID).inTableWhere( + RelationshipConstraintTableInfo.TABLE_INFO.name(), + RelationshipConstraintTableInfo.Columns.RELATIONSHIP_TYPE, + constraintClauseBuilder(relationshipEntityType, relationshipEntityUid).apply { + if (relationshipConstraintType != null) { + appendKeyStringValue( + RelationshipConstraintTableInfo.Columns.CONSTRAINT_TYPE, + relationshipConstraintType + ) + } + } + ) + } + + /** + * Filter RelationshipTypes by those that meets the requirements for this trackedEntityInstance: + * - the TEI might be assigned to the FROM component + * - or the TEI might be assigned to the TO component and the RelationshipType is bidirectional + */ + fun byAvailableForTrackedEntityInstance(trackedEntityInstanceUid: String): RelationshipTypeCollectionRepository { + return if (dhisVersionManager.is2_29) { + // All relationships are allowed for TEIs in 2.29 + this + } else { + val trackedEntityInstance = teiStore.selectByUid(trackedEntityInstanceUid) + cf.subQuery(IdentifiableColumns.UID).rawSubQuery( + FilterItemOperator.IN, + availableForTrackedEntityInstanceRawQuery(trackedEntityInstance) + ) + } + } + + /** + * Filter RelationshipTypes by those that meets the requirements for this enrollment: + * - the enrollment might be assigned to the FROM component + * - or the enrollment might be assigned to the TO component and the RelationshipType is bidirectional + */ + fun byAvailableForEnrollment(enrollmentUid: String): RelationshipTypeCollectionRepository { + return if (dhisVersionManager.is2_29) { + // Enrollment relationships are not supported in 2.29 + cf.string(IdentifiableColumns.UID).`in`(emptyList()) + } else { + val enrollment = enrollmentStore.selectByUid(enrollmentUid) + cf.subQuery(IdentifiableColumns.UID).rawSubQuery( + FilterItemOperator.IN, + availableForEnrollmentRawQuery(enrollment) + ) + } + } + + /** + * Filter RelationshipTypes by those that meets the requirements for this event: + * - the event might be assigned to the FROM component + * - or the event might be assigned to the TO component and the RelationshipType is bidirectional + */ + fun byAvailableForEvent(eventUid: String): RelationshipTypeCollectionRepository { + return if (dhisVersionManager.is2_29) { + // Event relationships are not supported in 2.29 + cf.string(IdentifiableColumns.UID).`in`(emptyList()) + } else { + val event = eventStore.selectByUid(eventUid) + cf.subQuery(IdentifiableColumns.UID).rawSubQuery( + FilterItemOperator.IN, + availableForEventRawQuery(event) + ) + } + } + + fun withConstraints(): RelationshipTypeCollectionRepository { + return cf.withChild(RelationshipTypeFields.CONSTRAINTS)!! + } + + private fun constraintClauseBuilder( + relationshipEntityType: RelationshipEntityType, + relationshipEntityUid: String + ): WhereClauseBuilder { + return WhereClauseBuilder() + .appendKeyStringValue( + RelationshipConstraintTableInfo.Columns.RELATIONSHIP_ENTITY, relationshipEntityType + ) + .appendKeyStringValue(getRelationshipEntityColumn(relationshipEntityType), relationshipEntityUid) + } + + private fun getRelationshipEntityColumn(relationshipEntityType: RelationshipEntityType): String { + return when (relationshipEntityType) { + RelationshipEntityType.TRACKED_ENTITY_INSTANCE -> + RelationshipConstraintTableInfo.Columns.TRACKED_ENTITY_TYPE + RelationshipEntityType.PROGRAM_INSTANCE -> + RelationshipConstraintTableInfo.Columns.PROGRAM + RelationshipEntityType.PROGRAM_STAGE_INSTANCE -> + RelationshipConstraintTableInfo.Columns.PROGRAM_STAGE + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDHISVersionManager.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDHISVersionManager.java index eb07cd685d..e445082eb5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDHISVersionManager.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDHISVersionManager.java @@ -83,7 +83,7 @@ Relationship229Compatible to229Compatible(Relationship relationship, String teiU .name(relationship.name()) .created(relationship.created()) .lastUpdated(relationship.lastUpdated()) - .state(relationship.state()) + .syncState(relationship.syncState()) .deleted(relationship.deleted()); if (versionManager.is2_29()) { @@ -108,7 +108,7 @@ public Relationship from229Compatible(Relationship229Compatible relationship229C .name(relationship229Compatible.name()) .created(relationship229Compatible.created()) .lastUpdated(relationship229Compatible.lastUpdated()) - .state(relationship229Compatible.state()) + .syncState(relationship229Compatible.syncState()) .deleted(relationship229Compatible.deleted()); if (versionManager.is2_29()) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDeleteCall.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDeleteCall.java deleted file mode 100644 index eaa7478353..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDeleteCall.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.relationship.internal; - -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.common.CoreColumns; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.imports.ImportStatus; -import org.hisp.dhis.android.core.imports.internal.RelationshipDeleteWebResponse; -import org.hisp.dhis.android.core.systeminfo.DHISVersionManager; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.inject.Inject; - -import dagger.Reusable; -import io.reactivex.Completable; - -import static org.hisp.dhis.android.core.arch.helpers.CollectionsHelper.isDeleted; - -@Reusable -public final class RelationshipDeleteCall { - - private final RelationshipService relationshipService; - - private final RelationshipStore relationshipStore; - - private final APICallExecutor apiCallExecutor; - - private final DHISVersionManager dhisVersionManager; - - @Inject - RelationshipDeleteCall(@NonNull RelationshipService relationshipService, - @NonNull RelationshipStore relationshipStore, - @NonNull APICallExecutor apiCallExecutor, - @NonNull DHISVersionManager dhisVersionManager) { - this.relationshipService = relationshipService; - this.relationshipStore = relationshipStore; - this.apiCallExecutor = apiCallExecutor; - this.dhisVersionManager = dhisVersionManager; - } - - @SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops"}) - public List postDeletedRelationships(List trackedEntityInstances) { - List withoutDeletedRelationships = new ArrayList<>(trackedEntityInstances.size()); - - for (TrackedEntityInstance instance : trackedEntityInstances) { - List relationships = TrackedEntityInstanceInternalAccessor - .accessRelationships(instance); - - if (relationships == null || relationships.isEmpty()) { - withoutDeletedRelationships.add(instance); - } else { - List nonDeletedRelationships = new ArrayList<>(); - for (Relationship229Compatible relationship : relationships) { - if (isDeleted(relationship)) { - deleteRelationship(relationship).blockingAwait(); - } else { - nonDeletedRelationships.add(relationship); - } - } - TrackedEntityInstance newInstance = TrackedEntityInstanceInternalAccessor - .insertRelationships(instance.toBuilder(), nonDeletedRelationships) - .build(); - withoutDeletedRelationships.add(newInstance); - } - } - return withoutDeletedRelationships; - } - - private Completable deleteRelationship(Relationship229Compatible relationship) { - return Completable.fromCallable(() -> { - if (dhisVersionManager.is2_29()) { - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(CoreColumns.ID, relationship.id()).build(); - relationshipStore.deleteWhereIfExists(whereClause); - return RelationshipDeleteWebResponse.empty(); - } else { - RelationshipDeleteWebResponse httpResponse = apiCallExecutor.executeObjectCallWithAcceptedErrorCodes( - relationshipService.deleteRelationship(relationship.uid()), - Collections.singletonList(404), - RelationshipDeleteWebResponse.class); - - ImportStatus status = httpResponse.response() == null ? null : httpResponse.response().status(); - - if (httpResponse.httpStatusCode() == 200 && ImportStatus.SUCCESS.equals(status) || - httpResponse.httpStatusCode() == 404) { - relationshipStore.delete(relationship.uid()); - } else { - // TODO Implement better handling - // The relationship is marked as error, but there is no handling in the TEI. The TEI is being posted - relationshipStore.setState(relationship.uid(), State.ERROR); - } - return httpResponse; - } - }); - } - -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipImportHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipImportHandler.kt new file mode 100644 index 0000000000..03b92c5ff7 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipImportHandler.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreUtils.getSyncState +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.internal.DataStatePropagator +import org.hisp.dhis.android.core.imports.internal.BaseImportSummaryHelper.getReferences +import org.hisp.dhis.android.core.imports.internal.RelationshipImportSummary +import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository + +@Reusable class RelationshipImportHandler @Inject internal constructor( + private val relationshipStore: RelationshipStore, + private val dataStatePropagator: DataStatePropagator, + private val relationshipRepository: RelationshipCollectionRepository +) { + + fun handleRelationshipImportSummaries( + importSummaries: List?, + relationships: List + ) { + + importSummaries?.filterNotNull()?.forEach { importSummary -> + importSummary.reference()?.let { relationshipUid -> + val relationship = relationshipRepository.withItems().uid(relationshipUid).blockingGet() + + val state = getSyncState(importSummary.status()) + + relationshipStore.setSyncStateOrDelete(relationshipUid, state) + dataStatePropagator.propagateRelationshipUpdate(relationship?.from()) + } + } + + processIgnoredRelationships(importSummaries, relationships) + } + + private fun processIgnoredRelationships( + importSummaries: List?, + relationships: List + ) { + val processedRelationships = getReferences(importSummaries) + + relationships.filterNot { processedRelationships.contains(it.uid()) }.forEach { relationship -> + relationshipStore.setSyncStateOrDelete(relationship.uid()!!, State.TO_UPDATE) + dataStatePropagator.propagateRelationshipUpdate(relationship.from()) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStore.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStore.kt similarity index 72% rename from core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStore.java rename to core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStore.kt index e273e97063..cd0619247a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStore.kt @@ -25,23 +25,22 @@ * (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.relationship.internal -package org.hisp.dhis.android.core.relationship.internal; +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.relationship.RelationshipConstraintType +import org.hisp.dhis.android.core.relationship.RelationshipItem -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore; -import org.hisp.dhis.android.core.relationship.RelationshipConstraintType; -import org.hisp.dhis.android.core.relationship.RelationshipItem; +internal interface RelationshipItemStore : ObjectWithoutUidStore { -import java.util.List; + fun getRelationshipUidsForItems(from: RelationshipItem, to: RelationshipItem): List -import androidx.annotation.NonNull; + fun getForRelationshipUidAndConstraintType( + uid: String, + constraintType: RelationshipConstraintType + ): RelationshipItem? -public interface RelationshipItemStore extends ObjectWithoutUidStore { - List getRelationshipUidsForItems(@NonNull RelationshipItem from, @NonNull RelationshipItem to); + fun getRelatedTeiUids(trackedEntityInstanceUids: List): List - RelationshipItem getForRelationshipUidAndConstraintType( - @NonNull String uid, - @NonNull RelationshipConstraintType constraintType); - - List getRelatedTeiUids(List trackedEntityInstanceUids); -} \ No newline at end of file + fun getByItem(item: RelationshipItem): List +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStoreImpl.java deleted file mode 100644 index 876b3b09b2..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStoreImpl.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.relationship.internal; - -import android.database.Cursor; - -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinder; -import org.hisp.dhis.android.core.arch.db.stores.binders.internal.WhereStatementBinder; -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStoreImpl; -import org.hisp.dhis.android.core.arch.helpers.UidsHelper; -import org.hisp.dhis.android.core.relationship.RelationshipConstraintType; -import org.hisp.dhis.android.core.relationship.RelationshipItem; -import org.hisp.dhis.android.core.relationship.RelationshipItemTableInfo; -import org.hisp.dhis.android.core.relationship.RelationshipItemTableInfo.Columns; - -import java.util.ArrayList; -import java.util.List; - -public final class RelationshipItemStoreImpl extends ObjectWithoutUidStoreImpl - implements RelationshipItemStore { - - private static final StatementBinder BINDER = (o, w) -> { - String trackedEntityInstance = o.trackedEntityInstance() == null ? null : - o.trackedEntityInstance().trackedEntityInstance(); - String enrollment = o.enrollment() == null ? null : o.enrollment().enrollment(); - String event = o.event() == null ? null : o.event().event(); - - w.bind(1, UidsHelper.getUidOrNull(o.relationship())); - w.bind(2, o.relationshipItemType()); - w.bind(3, trackedEntityInstance); - w.bind(4, enrollment); - w.bind(5, event); - }; - - private static final WhereStatementBinder WHERE_UPDATE_BINDER = (o, w) -> { - w.bind(6, UidsHelper.getUidOrNull(o.relationship())); - w.bind(7, o.relationshipItemType()); - }; - - private static final WhereStatementBinder WHERE_DELETE_BINDER = (o, w) -> { - w.bind(1, UidsHelper.getUidOrNull(o.relationship())); - w.bind(2, o.relationshipItemType()); - }; - - private RelationshipItemStoreImpl(DatabaseAdapter databaseAdapter, - SQLStatementBuilderImpl builder) { - super(databaseAdapter, builder, BINDER, WHERE_UPDATE_BINDER, WHERE_DELETE_BINDER, RelationshipItem::create); - } - - @Override - public List getRelationshipUidsForItems(@NonNull RelationshipItem from, @NonNull RelationshipItem to) { - List relationships = new ArrayList<>(); - try (Cursor cursor = this.getAllItemsOfSameType(from, to)) { - if (cursor.getCount() > 0) { - cursor.moveToFirst(); - do { - String relationshipInDb = cursor.getString(0); - String fromElementUidInDb = cursor.getString(1); - String toElementUidInDb = cursor.getString(2); - - if (from.elementUid().equals(fromElementUidInDb) && to.elementUid().equals(toElementUidInDb)) { - relationships.add(relationshipInDb); - } - } - while (cursor.moveToNext()); - } - } - - return relationships; - } - - @Override - public RelationshipItem getForRelationshipUidAndConstraintType( - @NonNull String uid, - @NonNull RelationshipConstraintType constraintType) { - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(RelationshipItemTableInfo.Columns.RELATIONSHIP, uid) - .appendKeyStringValue(RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE, constraintType) - .build(); - return selectOneWhere(whereClause); - } - - @Override - public List getRelatedTeiUids(List trackedEntityInstanceUids) { - String whereFromClause = new WhereClauseBuilder() - .appendInKeyStringValues(Columns.TRACKED_ENTITY_INSTANCE, trackedEntityInstanceUids) - .appendKeyStringValue(Columns.RELATIONSHIP_ITEM_TYPE, - RelationshipConstraintType.FROM) - .build(); - List relationshipItems = selectWhere(whereFromClause); - List relationshipUids = new ArrayList<>(); - for (RelationshipItem relationshipItem : relationshipItems) { - relationshipUids.add(relationshipItem.relationship().uid()); - } - - String whereToClause = new WhereClauseBuilder() - .appendInKeyStringValues(Columns.RELATIONSHIP, relationshipUids) - .appendKeyStringValue(Columns.RELATIONSHIP_ITEM_TYPE, RelationshipConstraintType.TO) - .appendIsNotNullValue(Columns.TRACKED_ENTITY_INSTANCE) - .build(); - List relatedRelationshipItems = selectWhere(whereToClause); - List relatedTEiUids = new ArrayList<>(); - for (RelationshipItem relationshipItem : relatedRelationshipItems) { - relatedTEiUids.add(relationshipItem.trackedEntityInstance().trackedEntityInstance()); - } - return relatedTEiUids; - } - - private Cursor getAllItemsOfSameType(@NonNull RelationshipItem from, @NonNull RelationshipItem to) { - String query = "SELECT " + RelationshipItemTableInfo.Columns.RELATIONSHIP + ", " + - "MAX(CASE WHEN " + RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE + " = 'FROM' " + - "THEN " + getItemElementColumn(from) + " END) AS fromElementUid, " + - "MAX(CASE WHEN " + RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE + " = 'TO' " + - "THEN " + getItemElementColumn(to) + " END) AS toElementUid " + - "FROM " + RelationshipItemTableInfo.TABLE_INFO.name() + - " GROUP BY " + RelationshipItemTableInfo.Columns.RELATIONSHIP; - - return this.getDatabaseAdapter().rawQuery(query); - } - - private String getItemElementColumn(RelationshipItem item) { - if (item.hasTrackedEntityInstance()) { - return Columns.TRACKED_ENTITY_INSTANCE; - } else if (item.hasEnrollment()) { - return Columns.ENROLLMENT; - } else { - return Columns.EVENT; - } - } - - public static RelationshipItemStore create(DatabaseAdapter databaseAdapter) { - SQLStatementBuilderImpl statementBuilder = new SQLStatementBuilderImpl( - RelationshipItemTableInfo.TABLE_INFO.name(), new RelationshipItemTableInfo.Columns()); - - return new RelationshipItemStoreImpl( - databaseAdapter, - statementBuilder - ); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStoreImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStoreImpl.kt new file mode 100644 index 0000000000..2a7ebeac46 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipItemStoreImpl.kt @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import android.database.Cursor +import java.util.* +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +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.binders.internal.WhereStatementBinder +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStoreImpl +import org.hisp.dhis.android.core.arch.helpers.UidsHelper.getUidOrNull +import org.hisp.dhis.android.core.relationship.RelationshipConstraintType +import org.hisp.dhis.android.core.relationship.RelationshipItem +import org.hisp.dhis.android.core.relationship.RelationshipItemTableInfo + +internal class RelationshipItemStoreImpl private constructor( + databaseAdapter: DatabaseAdapter, + builder: SQLStatementBuilderImpl +) : ObjectWithoutUidStoreImpl( + databaseAdapter, + builder, + BINDER, + WHERE_UPDATE_BINDER, + WHERE_DELETE_BINDER, + { cursor: Cursor -> RelationshipItem.create(cursor) } +), + RelationshipItemStore { + + @Suppress("NestedBlockDepth") + override fun getRelationshipUidsForItems(from: RelationshipItem, to: RelationshipItem): List { + val relationships: MutableList = ArrayList() + + getAllItemsOfSameType(from, to).use { cursor -> + if (cursor.count > 0) { + cursor.moveToFirst() + do { + val relationshipInDb = cursor.getString(0) + val fromElementUidInDb = cursor.getString(1) + val toElementUidInDb = cursor.getString(2) + if (from.elementUid() == fromElementUidInDb && to.elementUid() == toElementUidInDb) { + relationships.add(relationshipInDb) + } + } while (cursor.moveToNext()) + } + } + return relationships + } + + override fun getForRelationshipUidAndConstraintType( + uid: String, + constraintType: RelationshipConstraintType + ): RelationshipItem? { + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(RelationshipItemTableInfo.Columns.RELATIONSHIP, uid) + .appendKeyStringValue(RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE, constraintType) + .build() + return selectOneWhere(whereClause) + } + + override fun getRelatedTeiUids(trackedEntityInstanceUids: List): List { + val whereFromClause = WhereClauseBuilder() + .appendInKeyStringValues( + RelationshipItemTableInfo.Columns.TRACKED_ENTITY_INSTANCE, + trackedEntityInstanceUids + ) + .appendKeyStringValue( + RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE, + RelationshipConstraintType.FROM + ) + .build() + + val relationshipItems = selectWhere(whereFromClause) + val relationshipUids = relationshipItems.map { it.relationship()!!.uid() } + + val whereToClause = WhereClauseBuilder() + .appendInKeyStringValues(RelationshipItemTableInfo.Columns.RELATIONSHIP, relationshipUids) + .appendKeyStringValue( + RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE, + RelationshipConstraintType.TO + ) + .appendIsNotNullValue(RelationshipItemTableInfo.Columns.TRACKED_ENTITY_INSTANCE) + .build() + + val relatedRelationshipItems = selectWhere(whereToClause) + + return relatedRelationshipItems.map { it.trackedEntityInstance()!!.trackedEntityInstance() } + } + + override fun getByItem(item: RelationshipItem): List { + val clauseBuilder = WhereClauseBuilder().apply { + appendKeyStringValue(item.elementType(), item.elementUid()) + + item.relationshipItemType()?.let { + appendKeyStringValue(RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE, it.name) + } + + item.relationship()?.let { + appendKeyStringValue(RelationshipItemTableInfo.Columns.RELATIONSHIP, it.uid()) + } + } + + return selectWhere(clauseBuilder.build()) + } + + private fun getAllItemsOfSameType(from: RelationshipItem, to: RelationshipItem): Cursor { + val query = "SELECT " + RelationshipItemTableInfo.Columns.RELATIONSHIP + ", " + + "MAX(CASE WHEN " + RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE + " = 'FROM' " + + "THEN " + from.elementType() + " END) AS fromElementUid, " + + "MAX(CASE WHEN " + RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE + " = 'TO' " + + "THEN " + to.elementType() + " END) AS toElementUid " + + "FROM " + RelationshipItemTableInfo.TABLE_INFO.name() + + " GROUP BY " + RelationshipItemTableInfo.Columns.RELATIONSHIP + return databaseAdapter.rawQuery(query) + } + + companion object { + private val BINDER = StatementBinder { o: RelationshipItem, w: StatementWrapper -> + val trackedEntityInstance = if (o.trackedEntityInstance() == null) null else o.trackedEntityInstance()!! + .trackedEntityInstance() + val enrollment = if (o.enrollment() == null) null else o.enrollment()!!.enrollment() + val event = if (o.event() == null) null else o.event()!!.event() + w.bind(1, getUidOrNull(o.relationship())) + w.bind(2, o.relationshipItemType()) + w.bind(3, trackedEntityInstance) + w.bind(4, enrollment) + w.bind(5, event) + } + private val WHERE_UPDATE_BINDER = WhereStatementBinder { o: RelationshipItem, w: StatementWrapper -> + w.bind(6, getUidOrNull(o.relationship())) + w.bind(7, o.relationshipItemType()) + } + private val WHERE_DELETE_BINDER = WhereStatementBinder { o: RelationshipItem, w: StatementWrapper -> + w.bind(1, getUidOrNull(o.relationship())) + w.bind(2, o.relationshipItemType()) + } + + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): RelationshipItemStore { + val statementBuilder = SQLStatementBuilderImpl( + RelationshipItemTableInfo.TABLE_INFO.name(), RelationshipItemTableInfo.Columns() + ) + return RelationshipItemStoreImpl( + databaseAdapter, + statementBuilder + ) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java index 532e1b0abc..664d1a8a9c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipOrphanCleanerImpl.java @@ -67,7 +67,7 @@ public boolean deleteOrphan(O instance, Collection relationships) { int count = 0; for (Relationship existingRelationship : existingRelationships) { - if (isSynced(existingRelationship.state()) && + if (isSynced(existingRelationship.syncState()) && !isInRelationshipList(existingRelationship, relationships(relationships))) { relationshipStore.delete(existingRelationship.uid()); count++; diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipPayload.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipPayload.java new file mode 100644 index 0000000000..deb8889780 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipPayload.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.auto.value.AutoValue; + +import org.hisp.dhis.android.core.relationship.Relationship; + +import java.util.List; + +@AutoValue +public abstract class RelationshipPayload { + + @JsonProperty() + public abstract List relationships(); + + public static Builder builder() { + return new AutoValue_RelationshipPayload.Builder(); + } + + public static RelationshipPayload create(List relationships) { + return builder().relationships(relationships).build(); + } + + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder relationships(List relationships); + + public abstract RelationshipPayload build(); + } + +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipPostCall.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipPostCall.kt new file mode 100644 index 0000000000..185e38b559 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipPostCall.kt @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import dagger.Reusable +import io.reactivex.Completable +import io.reactivex.Observable +import io.reactivex.ObservableEmitter +import java.net.HttpURLConnection.* +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor +import org.hisp.dhis.android.core.arch.call.D2Progress +import org.hisp.dhis.android.core.arch.call.internal.D2ProgressManager +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper +import org.hisp.dhis.android.core.common.CoreColumns +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.internal.DataStatePropagator +import org.hisp.dhis.android.core.imports.ImportStatus +import org.hisp.dhis.android.core.imports.internal.RelationshipDeleteWebResponse +import org.hisp.dhis.android.core.imports.internal.RelationshipWebResponse +import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor +import org.hisp.dhis.android.core.trackedentity.internal.TrackerPostStateManager + +@Reusable +internal class RelationshipPostCall @Inject internal constructor( + private val relationshipService: RelationshipService, + private val relationshipStore: RelationshipStore, + private val relationshipImportHandler: RelationshipImportHandler, + private val dataStatePropagator: DataStatePropagator, + private val trackerStateManager: TrackerPostStateManager, + private val apiCallExecutor: APICallExecutor +) { + + fun deleteRelationships(relationships: List): Observable { + return Observable.create { emitter: ObservableEmitter -> + for (relationship in relationships) { + val httpResponse = apiCallExecutor.executeObjectCallWithAcceptedErrorCodes( + relationshipService.deleteRelationship(relationship.uid()!!), + listOf(HTTP_NOT_FOUND), + RelationshipDeleteWebResponse::class.java + ) + val status = httpResponse.response()?.status() + + if ((httpResponse.httpStatusCode() == HTTP_OK && ImportStatus.SUCCESS == status) || + httpResponse.httpStatusCode() == HTTP_NOT_FOUND + ) { + relationshipStore.delete(relationship.uid()!!) + } else { + // TODO Implement better handling + // The relationship is marked as error, but there is no handling in the TEI. The TEI is being posted + relationshipStore.setSyncState(relationship.uid()!!, State.ERROR) + } + dataStatePropagator.propagateRelationshipUpdate(relationship.from()) + } + emitter.onComplete() + } + } + + @Suppress("TooGenericExceptionCaught") + fun postRelationships(relationships: List): Observable { + val progressManager = D2ProgressManager(null) + + return if (relationships.isEmpty()) { + Observable.just(progressManager.increaseProgress(Relationship::class.java, false)) + } else { + Observable.defer { + try { + val payload = RelationshipPayload.builder().relationships(relationships).build() + trackerStateManager.setPayloadStates( + relationships = relationships, + forcedState = State.UPLOADING + ) + val httpResponse = apiCallExecutor.executeObjectCallWithAcceptedErrorCodes( + relationshipService.postRelationship(payload), + listOf(HTTP_CONFLICT), + RelationshipWebResponse::class.java + ) + + relationshipImportHandler.handleRelationshipImportSummaries( + importSummaries = httpResponse.response()?.importSummaries(), + relationships = relationships + ) + Observable.just(progressManager.increaseProgress(Relationship::class.java, false)) + } catch (e: Exception) { + trackerStateManager.restorePayloadStates(relationships = relationships) + relationships.forEach { dataStatePropagator.propagateRelationshipUpdate(it.from()) } + Observable.error(e) + } + } + } + } + + fun postDeletedRelationships29(trackedEntityInstances: List): List { + return trackedEntityInstances.map { instance -> + val relationships = TrackedEntityInstanceInternalAccessor.accessRelationships(instance) + + if (relationships == null || relationships.isEmpty()) { + instance + } else { + val partitionedRelationships = relationships.partition { CollectionsHelper.isDeleted(it) } + + partitionedRelationships.first.forEach { + deleteRelationship29(it).blockingAwait() + } + + TrackedEntityInstanceInternalAccessor + .insertRelationships(instance.toBuilder(), partitionedRelationships.second) + .build() + } + } + } + + private fun deleteRelationship29(relationship: Relationship229Compatible): Completable { + return Completable.fromCallable { + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(CoreColumns.ID, relationship.id()).build() + relationshipStore.deleteWhereIfExists(whereClause) + return@fromCallable RelationshipDeleteWebResponse.empty() + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipService.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipService.kt new file mode 100644 index 0000000000..adcc2ec12b --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipService.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import org.hisp.dhis.android.core.imports.internal.RelationshipDeleteWebResponse +import org.hisp.dhis.android.core.imports.internal.RelationshipWebResponse +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.POST +import retrofit2.http.Path + +internal interface RelationshipService { + + @DELETE("$RELATIONSHIPS/{uid}") + fun deleteRelationship(@Path("uid") relationship: String): Call + + @POST(RELATIONSHIPS) + fun postRelationship(@Body payload: RelationshipPayload): Call + + companion object { + const val RELATIONSHIPS = "relationships" + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStore.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStore.kt new file mode 100644 index 0000000000..174349b413 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStore.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStore +import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.relationship.RelationshipConstraintType +import org.hisp.dhis.android.core.relationship.RelationshipItem + +internal interface RelationshipStore : IdentifiableDeletableDataObjectStore { + + fun getRelationshipsByItem(relationshipItem: RelationshipItem): List + + fun getRelationshipsByItem( + relationshipItem: RelationshipItem, + type: RelationshipConstraintType? + ): List +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStoreImpl.java deleted file mode 100644 index 8ae929243d..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStoreImpl.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.relationship.internal; - -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl; -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinder; -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStoreImpl; -import org.hisp.dhis.android.core.relationship.Relationship; -import org.hisp.dhis.android.core.relationship.RelationshipItem; -import org.hisp.dhis.android.core.relationship.RelationshipTableInfo; - -import java.util.ArrayList; -import java.util.List; - -public final class RelationshipStoreImpl extends IdentifiableDeletableDataObjectStoreImpl - implements RelationshipStore { - - private static StatementBinder BINDER = (o, w) -> { - w.bind(1, o.uid()); - w.bind(2, o.name()); - w.bind(3, o.created()); - w.bind(4, o.lastUpdated()); - w.bind(5, o.relationshipType()); - w.bind(6, o.state()); - w.bind(7, o.deleted()); - }; - - private RelationshipStoreImpl(DatabaseAdapter databaseAdapter, - SQLStatementBuilderImpl statementBuilder) { - super(databaseAdapter, statementBuilder, BINDER, Relationship::create); - } - - @Override - public List getRelationshipsByItem(RelationshipItem relationshipItem) { - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue("RelationshipItem." + relationshipItem.elementType(), - relationshipItem.elementUid()) - .build(); - - String queryStatement = - "SELECT DISTINCT Relationship.* " + - "FROM (Relationship INNER JOIN RelationshipItem " + - "ON Relationship.uid = RelationshipItem.relationship) " + - "WHERE " + whereClause + ";"; - - List relationships = new ArrayList<>(); - addObjectsToCollection(getDatabaseAdapter().rawQuery(queryStatement), relationships); - - return relationships; - } - - public static RelationshipStore create(DatabaseAdapter databaseAdapter) { - SQLStatementBuilderImpl statementBuilder = new SQLStatementBuilderImpl(RelationshipTableInfo.TABLE_INFO); - return new RelationshipStoreImpl(databaseAdapter, statementBuilder); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStoreImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStoreImpl.kt new file mode 100644 index 0000000000..411c22dad7 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipStoreImpl.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import android.database.Cursor +import java.util.* +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +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.IdentifiableDeletableDataObjectStoreImpl +import org.hisp.dhis.android.core.relationship.* + +internal class RelationshipStoreImpl private constructor( + databaseAdapter: DatabaseAdapter, + statementBuilder: SQLStatementBuilderImpl +) : IdentifiableDeletableDataObjectStoreImpl( + databaseAdapter, + statementBuilder, + BINDER, + { cursor: Cursor -> Relationship.create(cursor) } +), + RelationshipStore { + + override fun getRelationshipsByItem(relationshipItem: RelationshipItem): List { + val whereClause = WhereClauseBuilder() + .appendKeyStringValue( + "RelationshipItem." + relationshipItem.elementType(), + relationshipItem.elementUid() + ) + .build() + + val queryStatement = "SELECT DISTINCT Relationship.* " + + "FROM (Relationship INNER JOIN RelationshipItem " + + "ON Relationship.uid = RelationshipItem.relationship) " + + "WHERE " + whereClause + ";" + + val relationships: MutableList = ArrayList() + addObjectsToCollection(databaseAdapter.rawQuery(queryStatement), relationships) + return relationships + } + + override fun getRelationshipsByItem( + relationshipItem: RelationshipItem, + type: RelationshipConstraintType? + ): List { + val relationshipTable = RelationshipTableInfo.TABLE_INFO.name() + val itemTable = RelationshipItemTableInfo.TABLE_INFO.name() + + val builder = WhereClauseBuilder() + .appendKeyStringValue( + "$itemTable.${relationshipItem.elementType()}", + relationshipItem.elementUid() + ) + + val whereClause = + if (type == null) { + builder.build() + } else { + builder.appendKeyStringValue( + "$itemTable.${RelationshipItemTableInfo.Columns.RELATIONSHIP_ITEM_TYPE}", + type.name + ).build() + } + + val queryStatement = "SELECT DISTINCT $relationshipTable.* " + + "FROM ($relationshipTable INNER JOIN $itemTable " + + "ON $relationshipTable.${RelationshipTableInfo.Columns.UID} = " + + "$itemTable.${RelationshipItemTableInfo.Columns.RELATIONSHIP}) " + + "WHERE " + whereClause + ";" + + val relationships: MutableList = ArrayList() + addObjectsToCollection(databaseAdapter.rawQuery(queryStatement), relationships) + return relationships + } + + companion object { + private val BINDER = StatementBinder { o: Relationship, w: StatementWrapper -> + w.bind(1, o.uid()) + w.bind(2, o.name()) + w.bind(3, o.created()) + w.bind(4, o.lastUpdated()) + w.bind(5, o.relationshipType()) + w.bind(6, o.syncState()) + w.bind(7, o.deleted()) + } + + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): RelationshipStore { + val statementBuilder = SQLStatementBuilderImpl(RelationshipTableInfo.TABLE_INFO) + return RelationshipStoreImpl(databaseAdapter, statementBuilder) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeCollectionRepositoryHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeCollectionRepositoryHelper.kt new file mode 100644 index 0000000000..8922d7488c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeCollectionRepositoryHelper.kt @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.relationship.RelationshipConstraintTableInfo +import org.hisp.dhis.android.core.relationship.RelationshipConstraintTableInfo.Columns +import org.hisp.dhis.android.core.relationship.RelationshipConstraintType +import org.hisp.dhis.android.core.relationship.RelationshipConstraintType.FROM +import org.hisp.dhis.android.core.relationship.RelationshipEntityType +import org.hisp.dhis.android.core.relationship.RelationshipTypeTableInfo +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance + +object RelationshipTypeCollectionRepositoryHelper { + + @JvmStatic + fun availableForTrackedEntityInstanceRawQuery(tei: TrackedEntityInstance?): String { + return availableForItemRawQuery(tei, availabilityTeiQuery) + } + + private val availabilityTeiQuery = { tei: TrackedEntityInstance?, type: RelationshipConstraintType? -> + val whereClause = WhereClauseBuilder().apply { + tei?.let { + if (type != null) { + appendKeyStringValue(Columns.CONSTRAINT_TYPE, type.name) + } + appendKeyStringValue(Columns.RELATIONSHIP_ENTITY, RelationshipEntityType.TRACKED_ENTITY_INSTANCE) + appendKeyStringValue(Columns.TRACKED_ENTITY_TYPE, it.trackedEntityType()) + appendComplexQuery(appendOptionalEnrollmentInProgram(tei)) + } + } + + relationshipTypeInConstraint(whereClause.build()) + } + + private fun appendOptionalEnrollmentInProgram(tei: TrackedEntityInstance): String { + return WhereClauseBuilder() + .appendIsNullValue(Columns.PROGRAM) + .appendOrInSubQuery( + Columns.PROGRAM, + "SELECT ${EnrollmentTableInfo.Columns.PROGRAM} FROM ${EnrollmentTableInfo.TABLE_INFO.name()}" + + " WHERE ${EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE} == '${tei.uid()}'" + ) + .build() + } + + @JvmStatic + fun availableForEnrollmentRawQuery(enrollment: Enrollment?): String { + return availableForItemRawQuery(enrollment, availableForEnrollment) + } + + private val availableForEnrollment = { enrollment: Enrollment?, type: RelationshipConstraintType? -> + val whereClause = WhereClauseBuilder().apply { + enrollment?.let { + if (type != null) { + appendKeyStringValue(Columns.CONSTRAINT_TYPE, type.name) + } + appendKeyStringValue(Columns.RELATIONSHIP_ENTITY, RelationshipEntityType.PROGRAM_INSTANCE) + appendKeyStringValue(Columns.PROGRAM, enrollment.program()) + } + } + + relationshipTypeInConstraint(whereClause.build()) + } + + @JvmStatic + fun availableForEventRawQuery(event: Event?): String { + return availableForItemRawQuery(event, availableForEvent) + } + + private val availableForEvent = { event: Event?, type: RelationshipConstraintType? -> + val whereClause = WhereClauseBuilder().apply { + event?.let { + if (type != null) { + appendKeyStringValue(Columns.CONSTRAINT_TYPE, type.name) + } + appendKeyStringValue(Columns.RELATIONSHIP_ENTITY, RelationshipEntityType.PROGRAM_STAGE_INSTANCE) + appendComplexQuery( + WhereClauseBuilder() + .appendOrKeyStringValue(Columns.PROGRAM, it.program()) + .appendOrKeyStringValue(Columns.PROGRAM_STAGE, it.programStage()) + .build() + ) + } + } + + relationshipTypeInConstraint(whereClause.build()) + } + + private fun availableForItemRawQuery( + t: T?, + availableForItem: (T, RelationshipConstraintType?) -> String + ): String { + return t?.let { + "SELECT DISTINCT ${RelationshipTypeTableInfo.Columns.UID} " + + "FROM ${RelationshipTypeTableInfo.TABLE_INFO.name()} " + + "WHERE ${RelationshipTypeTableInfo.Columns.UID} IN (${availableForItem(it, FROM)}) " + + "OR (${RelationshipTypeTableInfo.Columns.BIDIRECTIONAL} = 1 " + + "AND ${RelationshipTypeTableInfo.Columns.UID} IN (${availableForItem(it, null)}))" + } ?: "" + } + + private fun relationshipTypeInConstraint(whereClause: String): String { + return "SELECT ${Columns.RELATIONSHIP_TYPE} FROM ${RelationshipConstraintTableInfo.TABLE_INFO.name()} " + + "WHERE $whereClause" + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeEntityDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeEntityDIModule.kt similarity index 68% rename from core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeEntityDIModule.java rename to core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeEntityDIModule.kt index 509fe69e9b..4ce4b6d178 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeEntityDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeEntityDIModule.kt @@ -25,43 +25,39 @@ * (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.relationship.internal; - -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.relationship.RelationshipType; - -import java.util.Collections; -import java.util.Map; - -import dagger.Module; -import dagger.Provides; -import dagger.Reusable; +package org.hisp.dhis.android.core.relationship.internal + +import dagger.Module +import dagger.Provides +import dagger.Reusable +import java.util.* +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.relationship.RelationshipType @Module -public final class RelationshipTypeEntityDIModule { +internal class RelationshipTypeEntityDIModule { @Provides @Reusable - IdentifiableObjectStore store(DatabaseAdapter databaseAdapter) { - return RelationshipTypeStore.create(databaseAdapter); + fun store(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + return RelationshipTypeStore.create(databaseAdapter) } @Provides @Reusable - Handler handler(RelationshipTypeHandler impl) { - return impl; + fun handler(impl: RelationshipTypeHandler): Handler { + return impl } @Provides @Reusable - Map> childrenAppenders(DatabaseAdapter databaseAdapter) { - ChildrenAppender childrenAppender = new RelationshipConstraintChildrenAppender( - RelationshipConstraintStore.create(databaseAdapter) - ); - return Collections.singletonMap(RelationshipTypeFields.CONSTRAINTS, childrenAppender); + fun childrenAppenders(databaseAdapter: DatabaseAdapter): MutableMap> { + val childrenAppender: ChildrenAppender = RelationshipConstraintChildrenAppender( + RelationshipConstraintStore.create(databaseAdapter) + ) + return Collections.singletonMap(RelationshipTypeFields.CONSTRAINTS, childrenAppender) } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualization.java b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualization.java new file mode 100644 index 0000000000..9eb8236451 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualization.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2004-2021, 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.settings; + +import android.database.Cursor; + +import androidx.annotation.NonNull; +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.AnalyticsDhisVisualizationScopeColumnAdapter; +import org.hisp.dhis.android.core.common.CoreObject; +import org.hisp.dhis.android.core.common.ObjectWithUidInterface; + +import static org.hisp.dhis.android.core.common.BaseIdentifiableObject.UID; + +@AutoValue +@JsonDeserialize(builder = AutoValue_AnalyticsDhisVisualization.Builder.class) +public abstract class AnalyticsDhisVisualization implements CoreObject, ObjectWithUidInterface { + + @Nullable + public abstract String scopeUid(); + + @Nullable + public abstract String groupUid(); + + @Nullable + public abstract String groupName(); + + @Nullable + @ColumnAdapter(AnalyticsDhisVisualizationScopeColumnAdapter.class) + public abstract AnalyticsDhisVisualizationScope scope(); + + @NonNull + @JsonProperty(UID) + public abstract String uid(); + + @Nullable + public abstract String timestamp(); + + public static AnalyticsDhisVisualization create(Cursor cursor) { + return AutoValue_AnalyticsDhisVisualization.createFromCursor(cursor); + } + + public abstract Builder toBuilder(); + + public static Builder builder() { + return new AutoValue_AnalyticsDhisVisualization.Builder(); + } + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public abstract static class Builder { + + public abstract Builder id(Long id); + + public abstract Builder scopeUid(String scopeUid); + + public abstract Builder groupUid(String groupUid); + + public abstract Builder groupName(String groupName); + + public abstract Builder scope(AnalyticsDhisVisualizationScope scope); + + @JsonProperty(UID) + public abstract Builder uid(String uid); + + @JsonProperty("timestamp") + public abstract Builder timestamp(String timestamp); + + public abstract AnalyticsDhisVisualization build(); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationScope.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationScope.kt new file mode 100644 index 0000000000..3cadd747ff --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationScope.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.settings + +enum class AnalyticsDhisVisualizationScope { + HOME, + PROGRAM, + DATA_SET +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationTableInfo.kt new file mode 100644 index 0000000000..0b61f3d3c9 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationTableInfo.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2004-2021, 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.settings + +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 AnalyticsDhisVisualizationTableInfo { + + @JvmField + val TABLE_INFO: TableInfo = object : TableInfo() { + override fun name(): String { + return "AnalyticsDhisVisualization" + } + + override fun columns(): CoreColumns { + return Columns() + } + } + + class Columns : CoreColumns() { + override fun all(): Array { + return CollectionsHelper.appendInNewArray( + super.all(), + UID, + SCOPE_UID, + SCOPE, + GROUP_UID, + GROUP_NAME, + TIME_STAMP + ) + } + + companion object { + const val UID = IdentifiableColumns.UID + const val SCOPE_UID = "scopeUid" + const val SCOPE = "scope" + const val GROUP_UID = "groupUid" + const val GROUP_NAME = "groupName" + const val TIME_STAMP = "timestamp" + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsGroup.java b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsGroup.java new file mode 100644 index 0000000000..2bf1bd7328 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsGroup.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004-2021, 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.settings; + +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_AnalyticsDhisVisualizationsGroup.Builder.class) +public abstract class AnalyticsDhisVisualizationsGroup { + + public abstract String name(); + + public abstract String id(); + + public abstract List visualizations(); + + public abstract Builder toBuilder(); + + public static Builder builder() { + return new AutoValue_AnalyticsDhisVisualizationsGroup.Builder(); + } + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public abstract static class Builder { + + public abstract Builder name(String name); + + public abstract Builder id(String id); + + public abstract Builder visualizations(List visualizations); + + public abstract AnalyticsDhisVisualizationsGroup build(); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsSetting.java b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsSetting.java new file mode 100644 index 0000000000..c9fd41071e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsSetting.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2004-2021, 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.settings; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.google.auto.value.AutoValue; + +import java.util.List; +import java.util.Map; + +@AutoValue +@JsonDeserialize(builder = AutoValue_AnalyticsDhisVisualizationsSetting.Builder.class) +public abstract class AnalyticsDhisVisualizationsSetting { + + public abstract List home(); + + public abstract Map> program(); + + public abstract Map> dataSet(); + + public abstract Builder toBuilder(); + + public static Builder builder() { + return new AutoValue_AnalyticsDhisVisualizationsSetting.Builder(); + } + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public abstract static class Builder { + + public abstract Builder home(List home); + + public abstract Builder program(Map> program); + + public abstract Builder dataSet(Map> dataSet); + + public abstract AnalyticsDhisVisualizationsSetting build(); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsSettingObjectRepository.java b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsSettingObjectRepository.java new file mode 100644 index 0000000000..7e429868d1 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsDhisVisualizationsSettingObjectRepository.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2004-2021, 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.settings; + +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore; +import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository; +import org.hisp.dhis.android.core.arch.repositories.object.internal.ReadOnlyAnyObjectWithDownloadRepositoryImpl; +import org.hisp.dhis.android.core.settings.internal.AnalyticsSettingCall; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import javax.inject.Inject; + +import dagger.Reusable; + +@Reusable +public class AnalyticsDhisVisualizationsSettingObjectRepository + extends ReadOnlyAnyObjectWithDownloadRepositoryImpl + implements ReadOnlyWithDownloadObjectRepository { + + private final ObjectWithoutUidStore analyticsDhisVisualizationStore; + + @Inject + public AnalyticsDhisVisualizationsSettingObjectRepository( + ObjectWithoutUidStore analyticsDhisVisualizationStore, + AnalyticsSettingCall analyticsSettingCall) { + super(analyticsSettingCall); + this.analyticsDhisVisualizationStore = analyticsDhisVisualizationStore; + } + + @Override + public AnalyticsDhisVisualizationsSetting blockingGet() { + List analyticsDhisVisualizations = analyticsDhisVisualizationStore.selectAll(); + + if (analyticsDhisVisualizations.isEmpty()) { + return null; + } + + AnalyticsDhisVisualizationsSetting.Builder analyticsDhisVisualizationsSettingBuilder = + AnalyticsDhisVisualizationsSetting.builder(); + List home = new ArrayList<>(); + Map> program = new HashMap<>(); + Map> dataSet = new HashMap<>(); + + for (AnalyticsDhisVisualization analyticsDhisVisualization : analyticsDhisVisualizations) { + AnalyticsDhisVisualizationsGroup group; + switch (Objects.requireNonNull(analyticsDhisVisualization.scope())) { + case HOME: + group = getGroup(analyticsDhisVisualization.groupUid(), home); + + if (group == null) { + group = createGroup(analyticsDhisVisualization); + home.add(group); + } else { + AnalyticsDhisVisualizationsGroup updatedGroup = updateGroup(group, analyticsDhisVisualization); + home.remove(group); + home.add(updatedGroup); + } + break; + case PROGRAM: + group = getGroup( + analyticsDhisVisualization.groupUid(), + program.get(analyticsDhisVisualization.scopeUid()) + ); + + if (group == null) { + group = createGroup(analyticsDhisVisualization); + program.put( + analyticsDhisVisualization.scopeUid(), + createGroupList(group) + ); + } else { + AnalyticsDhisVisualizationsGroup updatedGroup = updateGroup(group, analyticsDhisVisualization); + Objects.requireNonNull(program.get(analyticsDhisVisualization.scopeUid())).remove(group); + Objects.requireNonNull(program.get(analyticsDhisVisualization.scopeUid())).add(updatedGroup); + } + + break; + case DATA_SET: + group = getGroup( + analyticsDhisVisualization.groupUid(), + dataSet.get(analyticsDhisVisualization.scopeUid()) + ); + + if (group == null) { + group = createGroup(analyticsDhisVisualization); + dataSet.put( + analyticsDhisVisualization.scopeUid(), + createGroupList(group) + ); + } else { + AnalyticsDhisVisualizationsGroup updatedGroup = updateGroup(group, analyticsDhisVisualization); + Objects.requireNonNull(dataSet.get(analyticsDhisVisualization.scopeUid())).remove(group); + Objects.requireNonNull(dataSet.get(analyticsDhisVisualization.scopeUid())).add(updatedGroup); + } + break; + default: + break; + } + } + + return analyticsDhisVisualizationsSettingBuilder + .home(home) + .program(program) + .dataSet(dataSet) + .build(); + } + + private AnalyticsDhisVisualizationsGroup getGroup( + String groupUid, + List groupList + ) { + if (groupList != null) { + for (AnalyticsDhisVisualizationsGroup group : groupList) { + if (group.id().equals(groupUid)) { + return group; + } + } + } + + return null; + } + + private AnalyticsDhisVisualizationsGroup createGroup(AnalyticsDhisVisualization analyticsDhisVisualization) { + return AnalyticsDhisVisualizationsGroup + .builder() + .id(analyticsDhisVisualization.groupUid()) + .name(analyticsDhisVisualization.groupName()) + .visualizations(new ArrayList<>(Arrays.asList(analyticsDhisVisualization))) + .build(); + } + + private AnalyticsDhisVisualizationsGroup updateGroup( + AnalyticsDhisVisualizationsGroup oldGroup, + AnalyticsDhisVisualization analyticsDhisVisualization + ) { + List updatedVisualizations = + new ArrayList<>(oldGroup.visualizations()); + updatedVisualizations.add(analyticsDhisVisualization); + return oldGroup + .toBuilder() + .visualizations(updatedVisualizations) + .build(); + } + + private List createGroupList(AnalyticsDhisVisualizationsGroup group) { + return new ArrayList<>(Arrays.asList(group)); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsSettingObjectRepository.java b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsSettingObjectRepository.java index 45d4878319..a8eace8eb8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsSettingObjectRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsSettingObjectRepository.java @@ -45,22 +45,31 @@ public class AnalyticsSettingObjectRepository private final AnalyticsTeiSettingCollectionRepository analyticsTeiSettingRepository; + private final AnalyticsDhisVisualizationsSettingObjectRepository analyticsDhisVisualizationsSettingObjectRepository; + @Inject - AnalyticsSettingObjectRepository(AnalyticsTeiSettingCollectionRepository analyticsTeiSettingRepository, - AnalyticsSettingCall analyticsSettingCall) { + AnalyticsSettingObjectRepository( + AnalyticsTeiSettingCollectionRepository analyticsTeiSettingRepository, + AnalyticsSettingCall analyticsSettingCall, + AnalyticsDhisVisualizationsSettingObjectRepository analyticsDhisVisualizationsSettingObjectRepository + ) { super(analyticsSettingCall); this.analyticsTeiSettingRepository = analyticsTeiSettingRepository; + this.analyticsDhisVisualizationsSettingObjectRepository = analyticsDhisVisualizationsSettingObjectRepository; } @Override public AnalyticsSettings blockingGet() { List analyticsTeiSettings = analyticsTeiSettingRepository.blockingGet(); + AnalyticsDhisVisualizationsSetting analyticsDhisVisualizationsSetting = + analyticsDhisVisualizationsSettingObjectRepository.blockingGet(); if (analyticsTeiSettings.isEmpty()) { return null; } else { return AnalyticsSettings.builder() .tei(analyticsTeiSettings) + .dhisVisualizations(analyticsDhisVisualizationsSetting) .build(); } } @@ -68,4 +77,8 @@ public AnalyticsSettings blockingGet() { public AnalyticsTeiSettingCollectionRepository teis() { return analyticsTeiSettingRepository; } + + public AnalyticsDhisVisualizationsSettingObjectRepository visualizationsSettings() { + return analyticsDhisVisualizationsSettingObjectRepository; + } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsSettings.java b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsSettings.java index 34ffae3019..fae283baba 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsSettings.java +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/AnalyticsSettings.java @@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.google.auto.value.AutoValue; +import java.util.Collections; import java.util.List; @AutoValue @@ -40,6 +41,8 @@ public abstract class AnalyticsSettings { public abstract List tei(); + public abstract AnalyticsDhisVisualizationsSetting dhisVisualizations(); + public static Builder builder() { return new AutoValue_AnalyticsSettings.Builder(); } @@ -47,8 +50,28 @@ public static Builder builder() { @AutoValue.Builder @JsonPOJOBuilder(withPrefix = "") public abstract static class Builder { + public abstract Builder tei(List tei); - public abstract AnalyticsSettings build(); + public abstract Builder dhisVisualizations(AnalyticsDhisVisualizationsSetting dhisVisualizations); + + public abstract AnalyticsSettings autoBuild(); + + //Auxiliary fields + abstract AnalyticsDhisVisualizationsSetting dhisVisualizations(); + + public AnalyticsSettings build() { + try { + dhisVisualizations(); + } catch (IllegalStateException e) { + dhisVisualizations(AnalyticsDhisVisualizationsSetting.builder() + .home(Collections.emptyList()) + .program(Collections.emptyMap()) + .dataSet(Collections.emptyMap()) + .build()); + } + + return autoBuild(); + } } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/ProgramFilter.java b/core/src/main/java/org/hisp/dhis/android/core/settings/ProgramFilter.java index 802233371d..ef2c51e545 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/ProgramFilter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/ProgramFilter.java @@ -53,5 +53,8 @@ public enum ProgramFilter { ORG_UNIT, @JsonProperty("categoryCombo") - CAT_COMBO -} + CAT_COMBO, + + @JsonProperty("followUp") + FOLLOW_UP +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationSettingHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationSettingHandler.kt new file mode 100644 index 0000000000..3b1d5772ef --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationSettingHandler.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004-2021, 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.settings.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.arch.handlers.internal.ObjectWithoutUidHandlerImpl +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization + +@Reusable +internal class AnalyticsDhisVisualizationSettingHandler @Inject constructor( + store: ObjectWithoutUidStore +) : ObjectWithoutUidHandlerImpl(store) { + + override fun beforeCollectionHandled( + oCollection: Collection + ): Collection { + store.delete() + return oCollection + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationStore.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationStore.kt new file mode 100644 index 0000000000..a0a85e96a8 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsDhisVisualizationStore.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2021, 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.settings.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.StatementBinder +import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapper +import org.hisp.dhis.android.core.arch.db.stores.binders.internal.WhereStatementBinder +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory.objectWithoutUidStore +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationTableInfo + +@Suppress("MagicNumber") +internal object AnalyticsDhisVisualizationStore { + + private val BINDER = StatementBinder { o: AnalyticsDhisVisualization, w: StatementWrapper -> + w.bind(1, o.uid()) + w.bind(2, o.scopeUid()) + w.bind(3, o.scope()) + w.bind(4, o.groupUid()) + w.bind(5, o.groupName()) + w.bind(6, o.timestamp()) + } + + private val WHERE_UPDATE_BINDER = WhereStatementBinder { _: AnalyticsDhisVisualization, _: StatementWrapper -> + } + + private val WHERE_DELETE_BINDER = WhereStatementBinder { _: AnalyticsDhisVisualization, _: StatementWrapper -> } + + fun create(databaseAdapter: DatabaseAdapter): ObjectWithoutUidStore { + return objectWithoutUidStore( + databaseAdapter, AnalyticsDhisVisualizationTableInfo.TABLE_INFO, BINDER, + WHERE_UPDATE_BINDER, WHERE_DELETE_BINDER + ) { cursor: Cursor -> AnalyticsDhisVisualization.create(cursor) } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingCall.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingCall.kt index f550727852..b32c577b97 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingCall.kt @@ -35,12 +35,14 @@ import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor import org.hisp.dhis.android.core.arch.handlers.internal.Handler import org.hisp.dhis.android.core.maintenance.D2Error import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization import org.hisp.dhis.android.core.settings.AnalyticsSettings import org.hisp.dhis.android.core.settings.AnalyticsTeiSetting @Reusable internal class AnalyticsSettingCall @Inject constructor( private val analyticsTeiSettingHandler: Handler, + private val analyticsDhisVisualizationsSettingHandler: Handler, private val settingAppService: SettingAppService, private val apiCallExecutor: RxAPICallExecutor, private val appVersionManager: SettingsAppInfoManager @@ -68,5 +70,11 @@ internal class AnalyticsSettingCall @Inject constructor( override fun process(item: AnalyticsSettings?) { val analyticsTeiSettingList = item?.tei() ?: emptyList() analyticsTeiSettingHandler.handleMany(analyticsTeiSettingList) + + val analyticsDhisVisualizations: List = item?. let { + SettingsAppHelper.getAnalyticsDhisVisualizations(it) + } ?: emptyList() + + analyticsDhisVisualizationsSettingHandler.handleMany(analyticsDhisVisualizations) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingEntityDIModule.kt index 64470910a1..c2ea345bad 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingEntityDIModule.kt @@ -37,7 +37,12 @@ import org.hisp.dhis.android.core.arch.handlers.internal.Handler 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.arch.repositories.children.internal.ChildrenAppender -import org.hisp.dhis.android.core.settings.* +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization +import org.hisp.dhis.android.core.settings.AnalyticsTeiAttribute +import org.hisp.dhis.android.core.settings.AnalyticsTeiDataElement +import org.hisp.dhis.android.core.settings.AnalyticsTeiIndicator +import org.hisp.dhis.android.core.settings.AnalyticsTeiSetting +import org.hisp.dhis.android.core.settings.AnalyticsTeiWHONutritionData @Module @Suppress("TooManyFunctions") @@ -49,6 +54,14 @@ internal class AnalyticsSettingEntityDIModule { return AnalyticsTeiSettingStore.create(databaseAdapter) } + @Provides + @Reusable + fun analyticsDhisVisualizationStore( + databaseAdapter: DatabaseAdapter + ): ObjectWithoutUidStore { + return AnalyticsDhisVisualizationStore.create(databaseAdapter) + } + @Provides @Reusable fun analyticsTeiDataElementStore(databaseAdapter: DatabaseAdapter): LinkStore { @@ -79,6 +92,14 @@ internal class AnalyticsSettingEntityDIModule { return impl } + @Provides + @Reusable + fun analyticsDhisVisualizationsHandler( + impl: AnalyticsDhisVisualizationSettingHandler + ): Handler { + return impl + } + @Provides @Reusable fun analyticsTeiDataElementHandler( diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingModuleWiper.java b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingModuleWiper.java index 93cbc784f5..e1c490436b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingModuleWiper.java +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingModuleWiper.java @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.settings.internal; +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationTableInfo; import org.hisp.dhis.android.core.settings.AnalyticsTeiAttributeTableInfo; import org.hisp.dhis.android.core.settings.AnalyticsTeiDataElementTableInfo; import org.hisp.dhis.android.core.settings.AnalyticsTeiIndicatorTableInfo; @@ -70,6 +71,7 @@ public void wipeMetadata() { tableWiper.wipeTable(AnalyticsTeiIndicatorTableInfo.TABLE_INFO); tableWiper.wipeTable(AnalyticsTeiAttributeTableInfo.TABLE_INFO); tableWiper.wipeTable(UserSettingsTableInfo.TABLE_INFO); + tableWiper.wipeTable(AnalyticsDhisVisualizationTableInfo.TABLE_INFO); } @Override diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingsAppHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingsAppHelper.kt index a254a5dcdd..5dddd3c79f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingsAppHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SettingsAppHelper.kt @@ -27,8 +27,33 @@ */ package org.hisp.dhis.android.core.settings.internal -import org.hisp.dhis.android.core.settings.* +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationScope +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationsGroup +import org.hisp.dhis.android.core.settings.AnalyticsSettings +import org.hisp.dhis.android.core.settings.AnalyticsTeiAttribute +import org.hisp.dhis.android.core.settings.AnalyticsTeiData +import org.hisp.dhis.android.core.settings.AnalyticsTeiDataElement +import org.hisp.dhis.android.core.settings.AnalyticsTeiIndicator +import org.hisp.dhis.android.core.settings.AnalyticsTeiSetting +import org.hisp.dhis.android.core.settings.AnalyticsTeiWHONutritionData +import org.hisp.dhis.android.core.settings.AnalyticsTeiWHONutritionItem +import org.hisp.dhis.android.core.settings.AppearanceSettings +import org.hisp.dhis.android.core.settings.ChartType +import org.hisp.dhis.android.core.settings.CompletionSpinner +import org.hisp.dhis.android.core.settings.DataSetFilter +import org.hisp.dhis.android.core.settings.DataSetFilters +import org.hisp.dhis.android.core.settings.DataSetSetting +import org.hisp.dhis.android.core.settings.DataSetSettings +import org.hisp.dhis.android.core.settings.FilterSetting +import org.hisp.dhis.android.core.settings.HomeFilter +import org.hisp.dhis.android.core.settings.ProgramFilter +import org.hisp.dhis.android.core.settings.ProgramFilters +import org.hisp.dhis.android.core.settings.ProgramSetting +import org.hisp.dhis.android.core.settings.ProgramSettings +import org.hisp.dhis.android.core.settings.WHONutritionComponent +@Suppress("TooManyFunctions") internal object SettingsAppHelper { fun getDataSetSettingList(dataSetSettings: DataSetSettings): List { @@ -119,6 +144,60 @@ internal object SettingsAppHelper { return list } + fun getAnalyticsDhisVisualizations(analyticsSettings: AnalyticsSettings): List { + val result = mutableListOf() + + analyticsSettings.dhisVisualizations()?.let { visualizationsSetting -> + + getHomeVisualizations(visualizationsSetting.home())?.let { result.addAll(it) } + + getProgramVisualizations(visualizationsSetting.program())?.let { result.addAll(it) } + + getDataSetVisualizations(visualizationsSetting.dataSet())?.let { result.addAll(it) } + } + + return result + } + + private fun getHomeVisualizations(analyticsDhisVisualizationsGroups: List?) = + analyticsDhisVisualizationsGroups?.flatMap { group -> + group.visualizations().map { visualization -> + visualization.toBuilder() + .groupUid(group.id()) + .groupName(group.name()) + .scope(AnalyticsDhisVisualizationScope.HOME) + .build() + } + } + + private fun getProgramVisualizations(programVisualizations: Map>?) = + programVisualizations?.flatMap { entry -> + entry.value.flatMap { group -> + group.visualizations().map { visualization -> + visualization.toBuilder() + .groupUid(group.id()) + .groupName(group.name()) + .scope(AnalyticsDhisVisualizationScope.PROGRAM) + .scopeUid(entry.key) + .build() + } + } + } + + private fun getDataSetVisualizations(dataSetVisualizations: Map>?) = + dataSetVisualizations?.flatMap { entry -> + entry.value.flatMap { group -> + group.visualizations().map { visualization -> + visualization.toBuilder() + .groupUid(group.id()) + .groupName(group.name()) + .scope(AnalyticsDhisVisualizationScope.DATA_SET) + .scopeUid(entry.key) + .build() + } + } + } + @JvmStatic fun buildAnalyticsTeiSettings( teiSettings: List, diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/DataSetsStore.java b/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/DataSetsStore.java index 164da654c6..c04753dcd2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/DataSetsStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/DataSetsStore.java @@ -72,7 +72,7 @@ Single> getDataValues(String dataSetUid, String orgUnit, .byAttributeOptionComboUid().eq(attributeOptionComboUid); List dataValues = baseDataValuesRepo - .byState().in(Arrays.asList(State.uploadableStates())) + .bySyncState().in(Arrays.asList(State.uploadableStates())) .blockingGet(); // TODO Workaround to prevent empty lists. Not supported in compression library diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/FileResourceCleaner.java b/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/FileResourceCleaner.java index 5a34a37891..c6d9854690 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/FileResourceCleaner.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/FileResourceCleaner.java @@ -126,7 +126,7 @@ Single removeFileAttributeValues(TrackedEntityInstance in private boolean isExistingAndNotSyncedFileResource(String resourceUid) { return fileResourceModule.fileResources() - .byState().notIn(State.SYNCED) + .bySyncState().notIn(State.SYNCED) .uid(resourceUid) .blockingExists(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/LocalDbRepositoryImpl.java b/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/LocalDbRepositoryImpl.java index a65f61701a..67a3ea51e0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/LocalDbRepositoryImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/data/localdbrepository/internal/LocalDbRepositoryImpl.java @@ -32,9 +32,9 @@ import android.content.SharedPreferences; import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.arch.helpers.UidsHelper; import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory; import org.hisp.dhis.android.core.common.State; +import org.hisp.dhis.android.core.common.internal.DataStatePropagator; import org.hisp.dhis.android.core.dataset.DataSetCompleteRegistrationTableInfo; import org.hisp.dhis.android.core.dataset.internal.DataSetCompleteRegistrationStore; import org.hisp.dhis.android.core.enrollment.Enrollment; @@ -45,6 +45,9 @@ import org.hisp.dhis.android.core.event.EventModule; import org.hisp.dhis.android.core.event.internal.EventStore; import org.hisp.dhis.android.core.relationship.Relationship; +import org.hisp.dhis.android.core.relationship.RelationshipConstraintType; +import org.hisp.dhis.android.core.relationship.RelationshipItem; +import org.hisp.dhis.android.core.relationship.internal.RelationshipItemStore; import org.hisp.dhis.android.core.relationship.internal.RelationshipStore; import org.hisp.dhis.android.core.sms.domain.model.internal.SMSDataValueSet; import org.hisp.dhis.android.core.sms.domain.repository.WebApiRepository; @@ -68,7 +71,7 @@ import io.reactivex.Observable; import io.reactivex.Single; -@SuppressWarnings("PMD.ExcessiveImports") +@SuppressWarnings({"PMD.ExcessiveImports", "PMD.TooManyFields"}) public class LocalDbRepositoryImpl implements LocalDbRepository { private final Context context; private final AuthenticatedUserObjectRepository userRepository; @@ -89,9 +92,11 @@ public class LocalDbRepositoryImpl implements LocalDbRepository { private final MetadataIdsStore metadataIdsStore; private final OngoingSubmissionsStore ongoingSubmissionsStore; private final RelationshipStore relationshipStore; + private final RelationshipItemStore relationshipItemStore; private final DataSetsStore dataSetsStore; private final TrackedEntityInstanceStore trackedEntityInstanceStore; private final DataSetCompleteRegistrationStore dataSetCompleteRegistrationStore; + private final DataStatePropagator dataStatePropagator; @Inject LocalDbRepositoryImpl(Context ctx, @@ -103,9 +108,11 @@ public class LocalDbRepositoryImpl implements LocalDbRepository { EventStore eventStore, EnrollmentStore enrollmentStore, RelationshipStore relationshipStore, + RelationshipItemStore relationshipItemStore, DataSetsStore dataSetsStore, TrackedEntityInstanceStore trackedEntityInstanceStore, - DataSetCompleteRegistrationStore dataSetCompleteRegistrationStore) { + DataSetCompleteRegistrationStore dataSetCompleteRegistrationStore, + DataStatePropagator dataStatePropagator) { this.context = ctx; this.userRepository = userRepository; this.trackedEntityModule = trackedEntityModule; @@ -115,9 +122,11 @@ public class LocalDbRepositoryImpl implements LocalDbRepository { this.eventStore = eventStore; this.enrollmentStore = enrollmentStore; this.relationshipStore = relationshipStore; + this.relationshipItemStore = relationshipItemStore; this.dataSetsStore = dataSetsStore; this.trackedEntityInstanceStore = trackedEntityInstanceStore; this.dataSetCompleteRegistrationStore = dataSetCompleteRegistrationStore; + this.dataStatePropagator = dataStatePropagator; metadataIdsStore = new MetadataIdsStore(context); ongoingSubmissionsStore = new OngoingSubmissionsStore(context); } @@ -257,7 +266,7 @@ private Single getTrackedEntityInstance(String instanceUi private Single> getEventsForEnrollment(String enrollmentUid) { return eventModule.events() .byEnrollmentUid().eq(enrollmentUid) - .byState().in(State.uploadableStates()) + .bySyncState().in(State.uploadableStates()) .withTrackedEntityDataValues() .get() .flatMapObservable(Observable::fromIterable) @@ -267,21 +276,41 @@ private Single> getEventsForEnrollment(String enrollmentUid) { @Override public Completable updateEventSubmissionState(String eventUid, State state) { - return Completable.fromAction(() -> eventStore.setState(eventUid, state)); + return Completable.fromAction(() -> { + eventStore.setSyncState(eventUid, state); + Event event = eventStore.selectByUid(eventUid); + dataStatePropagator.propagateEventUpdate(event); + }); } @Override public Completable updateEnrollmentSubmissionState(TrackedEntityInstance tei, State state) { return Completable.fromAction(() -> { Enrollment enrollment = TrackedEntityInstanceInternalAccessor.accessEnrollments(tei).get(0); - enrollmentStore.setState(enrollment.uid(), state); - trackedEntityInstanceStore.setState(enrollment.trackedEntityInstance(), state); - List events = EnrollmentInternalAccessor.accessEvents(enrollment); + if (events != null && !events.isEmpty()) { - List eventUids = UidsHelper.getUidsList(events); - eventStore.setState(eventUids, state); + for (Event event : events) { + eventStore.setSyncState(event.uid(), state); + dataStatePropagator.propagateEventUpdate(event); + } } + + enrollmentStore.setSyncState(enrollment.uid(), state); + dataStatePropagator.propagateEnrollmentUpdate(enrollment); + + trackedEntityInstanceStore.setSyncState(enrollment.trackedEntityInstance(), state); + dataStatePropagator.propagateTrackedEntityInstanceUpdate(tei); + }); + } + + @Override + public Completable updateRelationshipSubmissionState(String relationshipUid, State state) { + return Completable.fromAction(() -> { + relationshipStore.setSyncState(relationshipUid, state); + RelationshipItem fromItem = relationshipItemStore + .getForRelationshipUidAndConstraintType(relationshipUid, RelationshipConstraintType.FROM); + dataStatePropagator.propagateRelationshipUpdate(fromItem); }); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/DeletionConverter.java b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/DeletionConverter.java index 5dbfd1ce2c..089ee41b6f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/DeletionConverter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/DeletionConverter.java @@ -40,13 +40,13 @@ import io.reactivex.Single; public class DeletionConverter extends Converter { - private final String uid; + private final String eventUid; public DeletionConverter(LocalDbRepository localDbRepository, DHISVersionManager dhisVersionManager, - String uid) { + String eventUid) { super(localDbRepository, dhisVersionManager); - this.uid = uid; + this.eventUid = eventUid; } @Override @@ -62,12 +62,11 @@ Single convert(@NonNull String uid, String user, int su @Override public Completable updateSubmissionState(State state) { - // there is no submission state update for deletion - return Completable.complete(); + return getLocalDbRepository().updateEventSubmissionState(eventUid, state).onErrorComplete(); } @Override Single readItemFromDb() { - return Single.just(uid); + return Single.just(eventUid); } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/RelationshipConverter.java b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/RelationshipConverter.java index 239b797e61..421f9bfbec 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/RelationshipConverter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/RelationshipConverter.java @@ -66,8 +66,7 @@ Single convert(@NonNull Relationship relationship, Stri @Override public Completable updateSubmissionState(State state) { - // there is no submission state update for RelationShip - return Completable.complete(); + return getLocalDbRepository().updateRelationshipSubmissionState(relationshipUid, state); } @Override diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/repository/internal/LocalDbRepository.java b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/repository/internal/LocalDbRepository.java index 5fd584cebd..9652656713 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/repository/internal/LocalDbRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/repository/internal/LocalDbRepository.java @@ -77,6 +77,8 @@ public interface LocalDbRepository { Completable updateEnrollmentSubmissionState(TrackedEntityInstance tei, State state); + Completable updateRelationshipSubmissionState(String relationshipUid, State state); + Completable setMetadataDownloadConfig(WebApiRepository.GetMetadataIdsConfig metadataIdsConfig); Single getMetadataDownloadConfig(); diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersion.java b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersion.java index bbf13f961f..e2729010b5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersion.java +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersion.java @@ -74,6 +74,7 @@ public static boolean isAllowedVersion(String versionStr) { } public static String[] allowedVersionsAsStr() { - return new String[]{V2_29_STR, V2_30_STR, V2_31_STR, V2_32_STR, V2_33_STR, V2_34_STR, V2_35_STR, V2_36_STR}; + return new String[]{V2_29_STR, V2_30_STR, V2_31_STR, V2_32_STR, V2_33_STR, V2_34_STR, V2_35_STR, + V2_36_STR}; } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.java b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.java index 7f1712a350..cceda5171f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.java +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.java @@ -48,6 +48,8 @@ public interface DHISVersionManager { boolean is2_35(); + boolean is2_36(); + /** * Check if the current version is strictly greater than the parameter. * diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.java b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.java index 6bd0473bc2..9d9fa25943 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.java @@ -121,6 +121,11 @@ public boolean is2_35() { return getVersion() == DHISVersion.V2_35; } + @Override + public boolean is2_36() { + return getVersion() == DHISVersion.V2_36; + } + @Override public boolean isGreaterThan(DHISVersion version) { return version.compareTo(getVersion()) < 0; diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/NewTrackerImporterTrackedEntity.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/NewTrackerImporterTrackedEntity.java index 8a474f870c..0bb119a4a7 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/NewTrackerImporterTrackedEntity.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/NewTrackerImporterTrackedEntity.java @@ -32,20 +32,23 @@ 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.gabrielittner.auto.value.cursor.ColumnName; import com.google.auto.value.AutoValue; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbDateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbGeometryColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.StateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNewTrackerImporterEnrollmentListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreNewTrackerImporterTrackedEntityAttributeValueListColumnAdapter; import org.hisp.dhis.android.core.common.BaseDeletableDataObject; +import org.hisp.dhis.android.core.common.DataColumns; import org.hisp.dhis.android.core.common.Geometry; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.enrollment.NewTrackerImporterEnrollment; import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceFields; @@ -58,7 +61,7 @@ public abstract class NewTrackerImporterTrackedEntity extends BaseDeletableDataO implements ObjectWithUidInterface { @Override - @JsonProperty() + @JsonProperty("trackedEntity") public abstract String uid(); @Nullable @@ -72,12 +75,12 @@ public abstract class NewTrackerImporterTrackedEntity extends BaseDeletableDataO public abstract Date updatedAt(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date createdAtClient(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date updatedAtClient(); @@ -94,6 +97,11 @@ public abstract class NewTrackerImporterTrackedEntity extends BaseDeletableDataO @ColumnAdapter(DbGeometryColumnAdapter.class) public abstract Geometry geometry(); + @Nullable + @ColumnName(DataColumns.AGGREGATED_SYNC_STATE) + @ColumnAdapter(StateColumnAdapter.class) + public abstract State aggregatedSyncState(); + @Nullable @JsonProperty(TrackedEntityInstanceFields.TRACKED_ENTITY_ATTRIBUTE_VALUES) @ColumnAdapter(IgnoreNewTrackerImporterTrackedEntityAttributeValueListColumnAdapter.class) @@ -119,7 +127,7 @@ public static NewTrackerImporterTrackedEntity create(Cursor cursor) { public abstract static class Builder extends BaseDeletableDataObject.Builder { public abstract Builder id(Long id); - @JsonProperty() + @JsonProperty("trackedEntity") public abstract Builder uid(String uid); public abstract Builder createdAt(Date created); @@ -137,6 +145,8 @@ public abstract static class Builder extends BaseDeletableDataObject.Builder trackedEntityAttributeValues); diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/NewTrackerImporterTranckedEntityTransformer.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/NewTrackerImporterTranckedEntityTransformer.kt index 03ef092a7e..8d6ae66296 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/NewTrackerImporterTranckedEntityTransformer.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/NewTrackerImporterTranckedEntityTransformer.kt @@ -43,7 +43,8 @@ internal class NewTrackerImporterTranckedEntityTransformer : .organisationUnit(o.organisationUnit()) .trackedEntityType(o.trackedEntityType()) .geometry(o.geometry()) - .state(o.state()) + .syncState(o.syncState()) + .aggregatedSyncState(o.aggregatedSyncState()) .build() } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstance.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstance.java index 532776b495..a521a7ff31 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstance.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstance.java @@ -32,23 +32,27 @@ 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.gabrielittner.auto.value.cursor.ColumnName; import com.google.auto.value.AutoValue; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbDateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbGeometryColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.StateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreEnrollmentListColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreRelationship229CompatibleListColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreStateColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreStringColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreTrackedEntityAttributeValueListColumnAdapter; import org.hisp.dhis.android.core.common.BaseDeletableDataObject; +import org.hisp.dhis.android.core.common.DataColumns; import org.hisp.dhis.android.core.common.FeatureType; import org.hisp.dhis.android.core.common.Geometry; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.enrollment.Enrollment; import org.hisp.dhis.android.core.relationship.internal.Relationship229Compatible; import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceFields; @@ -75,12 +79,12 @@ public abstract class TrackedEntityInstance extends BaseDeletableDataObject impl public abstract Date lastUpdated(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date createdAtClient(); @Nullable - @JsonIgnore() + @JsonProperty() @ColumnAdapter(DbDateColumnAdapter.class) public abstract Date lastUpdatedAtClient(); @@ -121,6 +125,21 @@ public abstract class TrackedEntityInstance extends BaseDeletableDataObject impl @ColumnAdapter(IgnoreEnrollmentListColumnAdapter.class) abstract List enrollments(); + @Nullable + @ColumnName(DataColumns.AGGREGATED_SYNC_STATE) + @ColumnAdapter(StateColumnAdapter.class) + public abstract State aggregatedSyncState(); + + /** + * @deprecated Use {@link #aggregatedSyncState()} instead. + */ + @Deprecated + @Nullable + @ColumnAdapter(IgnoreStateColumnAdapter.class) + public State state() { + return aggregatedSyncState(); + } + public static Builder builder() { return new $$AutoValue_TrackedEntityInstance.Builder(); } @@ -164,6 +183,16 @@ public abstract static class Builder extends BaseDeletableDataObject.Builder trackedEntityAttributeValues); + public abstract Builder aggregatedSyncState(State state); + + /** + * @deprecated Use {@link #aggregatedSyncState(State)} and {@link #syncState(State)} instead. + */ + @Deprecated + public Builder state(State state) { + return aggregatedSyncState(state).syncState(state); + } + abstract Builder relationships(List relationships); abstract Builder enrollments(List enrollments); diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceCollectionRepository.java index afbc1bf078..b10246f37a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceCollectionRepository.java @@ -42,11 +42,13 @@ import org.hisp.dhis.android.core.arch.repositories.scope.internal.RepositoryScopeHelper; import org.hisp.dhis.android.core.common.FeatureType; import org.hisp.dhis.android.core.common.State; +import org.hisp.dhis.android.core.common.internal.DataStatePropagator; import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo; import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceTableInfo.Columns; import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceFields; -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceParentPostCall; +import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstancePostParentCall; import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStore; +import org.hisp.dhis.android.core.tracker.importer.internal.JobQueryCall; import java.util.List; import java.util.Map; @@ -63,8 +65,10 @@ public final class TrackedEntityInstanceCollectionRepository implements ReadWriteWithUploadWithUidCollectionRepository { - private final TrackedEntityInstanceParentPostCall postCall; + private final TrackedEntityInstancePostParentCall postCall; private final TrackedEntityInstanceStore store; + private final DataStatePropagator dataStatePropagator; + private final JobQueryCall jobQueryCall; @Inject TrackedEntityInstanceCollectionRepository( @@ -72,18 +76,31 @@ public final class TrackedEntityInstanceCollectionRepository final Map> childrenAppenders, final RepositoryScope scope, final Transformer transformer, - final TrackedEntityInstanceParentPostCall postCall) { + final DataStatePropagator dataStatePropagator, + final TrackedEntityInstancePostParentCall postCall, + final JobQueryCall jobQueryCall) { super(store, childrenAppenders, scope, transformer, new FilterConnectorFactory<>(scope, s -> - new TrackedEntityInstanceCollectionRepository(store, childrenAppenders, s, transformer, postCall))); + new TrackedEntityInstanceCollectionRepository(store, childrenAppenders, s, transformer, + dataStatePropagator, postCall, jobQueryCall))); this.postCall = postCall; this.store = store; + this.dataStatePropagator = dataStatePropagator; + this.jobQueryCall = jobQueryCall; + } + + @Override + protected void propagateState(TrackedEntityInstance trackedEntityInstance) { + dataStatePropagator.propagateTrackedEntityInstanceUpdate(trackedEntityInstance); } @Override public Observable upload() { - return Observable.fromCallable(() -> - byState().in(State.uploadableStates()).blockingGetWithoutChildren() - ).flatMap(postCall::uploadTrackedEntityInstances); + return Observable.concat( + jobQueryCall.queryPendingJobs(), + Observable.fromCallable(() -> + byAggregatedSyncState().in(State.uploadableStates()).blockingGetWithoutChildren() + ).flatMap(postCall::uploadTrackedEntityInstances) + ); } @Override @@ -94,7 +111,8 @@ public void blockingUpload() { @Override public TrackedEntityInstanceObjectRepository uid(String uid) { RepositoryScope updatedScope = RepositoryScopeHelper.withUidFilterItem(scope, uid); - return new TrackedEntityInstanceObjectRepository(store, uid, childrenAppenders, updatedScope); + return new TrackedEntityInstanceObjectRepository(store, uid, childrenAppenders, updatedScope, + dataStatePropagator); } public StringFilterConnector byUid() { @@ -133,8 +151,20 @@ public StringFilterConnector byGeomet return cf.string(Columns.GEOMETRY_COORDINATES); } + /** + * @deprecated Use {@link #byAggregatedSyncState()} instead. + */ + @Deprecated public EnumFilterConnector byState() { - return cf.enumC(Columns.STATE); + return byAggregatedSyncState(); + } + + public EnumFilterConnector bySyncState() { + return cf.enumC(Columns.SYNC_STATE); + } + + public EnumFilterConnector byAggregatedSyncState() { + return cf.enumC(Columns.AGGREGATED_SYNC_STATE); } public BooleanFilterConnector byDeleted() { diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceObjectRepository.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceObjectRepository.java index 9fd6ef70f0..e8365cfbcd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceObjectRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceObjectRepository.java @@ -35,6 +35,7 @@ import org.hisp.dhis.android.core.common.Geometry; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.common.Unit; +import org.hisp.dhis.android.core.common.internal.DataStatePropagator; import org.hisp.dhis.android.core.maintenance.D2Error; import org.hisp.dhis.android.core.maintenance.D2ErrorCode; import org.hisp.dhis.android.core.maintenance.D2ErrorComponent; @@ -46,12 +47,16 @@ public final class TrackedEntityInstanceObjectRepository extends ReadWriteWithUidDataObjectRepositoryImpl { + private final DataStatePropagator dataStatePropagator; + TrackedEntityInstanceObjectRepository(final TrackedEntityInstanceStore store, final String uid, final Map> childrenAppenders, - final RepositoryScope scope) { + final RepositoryScope scope, + final DataStatePropagator dataStatePropagator) { super(store, childrenAppenders, scope, - s -> new TrackedEntityInstanceObjectRepository(store, uid, childrenAppenders, s)); + s -> new TrackedEntityInstanceObjectRepository(store, uid, childrenAppenders, s, dataStatePropagator)); + this.dataStatePropagator = dataStatePropagator; } public Unit setOrganisationUnitUid(String organisationUnitUid) throws D2Error { @@ -65,7 +70,7 @@ public Unit setGeometry(Geometry geometry) throws D2Error { private TrackedEntityInstance.Builder updateBuilder() throws D2Error { TrackedEntityInstance trackedEntityInstance = blockingGetWithoutChildren(); - State state = trackedEntityInstance.state(); + State state = trackedEntityInstance.aggregatedSyncState(); if (state == State.RELATIONSHIP) { throw D2Error .builder() @@ -78,8 +83,14 @@ private TrackedEntityInstance.Builder updateBuilder() throws D2Error { state = state == State.TO_POST ? state : State.TO_UPDATE; return trackedEntityInstance.toBuilder() - .state(state) + .syncState(state) + .aggregatedSyncState(state) .lastUpdated(updateDate) .lastUpdatedAtClient(updateDate); } + + @Override + protected void propagateState(TrackedEntityInstance trackedEntityInstance) { + dataStatePropagator.propagateTrackedEntityInstanceUpdate(trackedEntityInstance); + } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceTableInfo.java index 4c145370e4..b5ecd4d2a0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceTableInfo.java @@ -73,7 +73,8 @@ public String[] all() { TRACKED_ENTITY_TYPE, GEOMETRY_TYPE, GEOMETRY_COORDINATES, - STATE, + SYNC_STATE, + AGGREGATED_SYNC_STATE, DELETED ); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterPayload.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterPayload.kt new file mode 100644 index 0000000000..6c84b61668 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterPayload.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import org.hisp.dhis.android.core.enrollment.NewTrackerImporterEnrollment +import org.hisp.dhis.android.core.event.NewTrackerImporterEvent +import org.hisp.dhis.android.core.relationship.NewTrackerImporterRelationship +import org.hisp.dhis.android.core.trackedentity.NewTrackerImporterTrackedEntity + +internal data class NewTrackerImporterPayload( + val trackedEntities: MutableList = mutableListOf(), + val enrollments: MutableList = mutableListOf(), + val events: MutableList = mutableListOf(), + val relationships: MutableList = mutableListOf() +) { + fun isEmpty(): Boolean { + return trackedEntities.isEmpty() && enrollments.isEmpty() && events.isEmpty() && relationships.isEmpty() + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPayload.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterPayloadWrapper.kt similarity index 88% rename from core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPayload.kt rename to core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterPayloadWrapper.kt index 31b8aab13d..e069acdddb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPayload.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterPayloadWrapper.kt @@ -27,8 +27,7 @@ */ package org.hisp.dhis.android.core.trackedentity.internal -import org.hisp.dhis.android.core.trackedentity.NewTrackerImporterTrackedEntity - -internal data class NewTrackerImporterTrackedEntityPayload( - val trackedEntities: List +internal data class NewTrackerImporterPayloadWrapper( + val deleted: NewTrackerImporterPayload = NewTrackerImporterPayload(), + val updated: NewTrackerImporterPayload = NewTrackerImporterPayload() ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGenerator.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGenerator.kt index 5d4f84bf8a..747bfe0df1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGenerator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGenerator.kt @@ -34,16 +34,19 @@ import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStor import org.hisp.dhis.android.core.arch.handlers.internal.Transformer import org.hisp.dhis.android.core.common.DataColumns import org.hisp.dhis.android.core.common.State -import org.hisp.dhis.android.core.enrollment.NewTrackerImporterEnrollment import org.hisp.dhis.android.core.enrollment.NewTrackerImporterEnrollmentTransformer import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore -import org.hisp.dhis.android.core.event.NewTrackerImporterEvent import org.hisp.dhis.android.core.event.NewTrackerImporterEventTransformer import org.hisp.dhis.android.core.event.internal.EventStore import org.hisp.dhis.android.core.note.NewTrackerImporterNote import org.hisp.dhis.android.core.note.NewTrackerImporterNoteTransformer import org.hisp.dhis.android.core.note.Note -import org.hisp.dhis.android.core.trackedentity.* +import org.hisp.dhis.android.core.relationship.NewTrackerImporterRelationship +import org.hisp.dhis.android.core.relationship.NewTrackerImporterRelationshipTransformer +import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository +import org.hisp.dhis.android.core.trackedentity.NewTrackerImporterTrackedEntityAttributeValueTransformer +import org.hisp.dhis.android.core.trackedentity.NewTrackerImporterTrackedEntityDataValueTransformer +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance @Reusable internal class NewTrackerImporterTrackedEntityPostPayloadGenerator @Inject internal constructor( @@ -52,12 +55,12 @@ internal class NewTrackerImporterTrackedEntityPostPayloadGenerator @Inject inter private val trackedEntityDataValueStore: TrackedEntityDataValueStore, private val trackedEntityAttributeValueStore: TrackedEntityAttributeValueStore, private val noteStore: IdentifiableObjectStore, - private val stateManager: NewTrackerImporterTrackedEntityPostStateManager + private val relationshipRepository: RelationshipCollectionRepository ) { fun getTrackedEntities( filteredTrackedEntityInstances: List - ): List { + ): NewTrackerImporterPayloadWrapper { val dataValueMap = transformMap( trackedEntityDataValueStore.queryTrackerTrackedEntityDataValues(), NewTrackerImporterTrackedEntityDataValueTransformer() @@ -75,77 +78,47 @@ internal class NewTrackerImporterTrackedEntityPostPayloadGenerator @Inject inter NewTrackerImporterTrackedEntityAttributeValueTransformer() ) val notes = getNotes() + val relationships = getRelationships() - val trackedEntityTransformer = NewTrackerImporterTranckedEntityTransformer() - - val trackedEntitiesToSync = filteredTrackedEntityInstances.map { - addTrackedEntityChildren( - trackedEntityTransformer.transform(it), - dataValueMap, - eventMap, - enrollmentMap, - attributeValueMap, - notes - ) - } - - stateManager.setStates(trackedEntitiesToSync, State.UPLOADING) - return trackedEntitiesToSync + return NewTrackerImporterTrackedEntityPostPayloadGeneratorTask( + filteredTrackedEntityInstances, + dataValueMap, + eventMap, + enrollmentMap, + attributeValueMap, + relationships, + notes + ).generate() } private fun getNotes(): List { val whereNotesClause = WhereClauseBuilder() .appendInKeyStringValues( - DataColumns.STATE, State.uploadableStatesIncludingError().map { it.name } + DataColumns.SYNC_STATE, State.uploadableStatesIncludingError().map { it.name } ) .build() val notesTransformer = NewTrackerImporterNoteTransformer() return noteStore.selectWhere(whereNotesClause).map { notesTransformer.transform(it) } } + private fun getRelationships(): Map> { + val relationships = relationshipRepository.bySyncState() + .`in`(State.uploadableStatesIncludingError().toList()) + .withItems() + .blockingGet() + + val relationshipsTransformer = NewTrackerImporterRelationshipTransformer() + return relationships + .filter { it.from()?.elementUid() != null } + .groupBy({ it.from()?.elementUid()!! }, { relationshipsTransformer.transform(it) }) + } + private fun transformMap( - map: MutableMap>, + map: Map>, transformer: Transformer ): Map> { - return map.mapValues { - v -> + return map.mapValues { v -> v.value.map { transformer.transform(it) } } } - - @Suppress("LongParameterList") - private fun addTrackedEntityChildren( - trackedEntity: NewTrackerImporterTrackedEntity, - dataValueMap: Map>, - eventMap: Map>, - enrollmentMap: Map>, - attributeValueMap: Map>, - notes: List - ): NewTrackerImporterTrackedEntity { - return trackedEntity.toBuilder() - .enrollments(getEnrollments(dataValueMap, eventMap, enrollmentMap, notes, trackedEntity.uid())) - .trackedEntityAttributeValues(attributeValueMap[trackedEntity.uid()] ?: emptyList()) - .build() - } - - private fun getEnrollments( - dataValueMap: Map>, - eventMap: Map>, - enrollmentMap: Map>, - notes: List, - trackedEntityInstanceUid: String - ): List { - return enrollmentMap[trackedEntityInstanceUid]?.map { enrollment -> - val events = eventMap[enrollment.uid()]?.map { event -> - val eventBuilder = event.toBuilder() - .trackedEntityDataValues(dataValueMap[event.uid()]) - .notes(notes.filter { it.event() == event.uid() }) - eventBuilder.build() - } ?: emptyList() - NewTrackerImporterEnrollment.builder() - .events(events) - .notes(notes.filter { it.enrollment() == enrollment.uid() }) - .build() - } ?: emptyList() - } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGeneratorTask.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGeneratorTask.kt new file mode 100644 index 0000000000..04340b5522 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGeneratorTask.kt @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.NewTrackerImporterEnrollment +import org.hisp.dhis.android.core.event.NewTrackerImporterEvent +import org.hisp.dhis.android.core.note.NewTrackerImporterNote +import org.hisp.dhis.android.core.relationship.NewTrackerImporterRelationship +import org.hisp.dhis.android.core.trackedentity.* + +@Reusable +internal class NewTrackerImporterTrackedEntityPostPayloadGeneratorTask @Inject internal constructor( + private val trackedEntities: List, + private val dataValueMap: Map>, + private val eventMap: Map>, + private val enrollmentMap: Map>, + private val attributeValueMap: Map>, + private val relationshipMap: Map>, + private val notes: List +) { + + fun generate(): NewTrackerImporterPayloadWrapper { + val trackedEntityTransformer = NewTrackerImporterTranckedEntityTransformer() + val wrapper = NewTrackerImporterPayloadWrapper() + + val partitioned = trackedEntities + .map { trackedEntityTransformer.transform(it) } + .map { getTrackedEntity(it) } + .partition { it.deleted()!! } + + partitioned.first.forEach { + wrapper.deleted.trackedEntities.add(getTrackedEntityWithEnrollments(it)) + } + + partitioned.second.forEach { entity -> + if (entity.syncState() != State.SYNCED) { + wrapper.updated.trackedEntities.add(entity) + } + + addRelationshipsToWrapper(wrapper, relationshipMap[entity.uid()]) + + val partitionedEnrollments = getEnrollments(entity.uid()) + .partition { it.deleted()!! } + + partitionedEnrollments.first.forEach { + wrapper.deleted.enrollments.add(getEnrollmentWithEvents(it)) + } + + partitionedEnrollments.second.forEach { enrollment -> + if (enrollment.syncState() != State.SYNCED) { + wrapper.updated.enrollments.add(enrollment) + } + + addRelationshipsToWrapper(wrapper, relationshipMap[enrollment.uid()]) + + val partitionedEvents = getEvents(enrollment.uid()) + .filter { it.syncState() != State.SYNCED } + .partition { it.deleted()!! } + + partitionedEvents.first.forEach { + wrapper.deleted.events.add(it) + } + + partitionedEvents.second.forEach { event -> + wrapper.updated.events.add(event) + + addRelationshipsToWrapper(wrapper, relationshipMap[event.uid()]) + } + } + } + + return wrapper + } + + private fun getTrackedEntityWithEnrollments( + trackedEntity: NewTrackerImporterTrackedEntity + ): NewTrackerImporterTrackedEntity { + return getTrackedEntity(trackedEntity).toBuilder() + .enrollments(getEnrollmentsWithEvents(trackedEntity.uid())) + .build() + } + + private fun getTrackedEntity( + trackedEntity: NewTrackerImporterTrackedEntity + ): NewTrackerImporterTrackedEntity { + return trackedEntity.toBuilder() + .trackedEntityAttributeValues(attributeValueMap[trackedEntity.uid()] ?: emptyList()) + .build() + } + + private fun getEnrollmentsWithEvents( + trackedEntityInstanceUid: String + ): List { + return getEnrollments(trackedEntityInstanceUid).map { getEnrollmentWithEvents(it) } + } + + private fun getEnrollmentWithEvents( + enrollment: NewTrackerImporterEnrollment + ): NewTrackerImporterEnrollment { + return enrollment.toBuilder() + .events(getEvents(enrollment.uid())) + .build() + } + + private fun getEnrollments( + trackedEntityInstanceUid: String + ): List { + return enrollmentMap[trackedEntityInstanceUid]?.map { enrollment -> + enrollment.toBuilder() + .notes(notes.filter { it.enrollment() == enrollment.uid() }) + .build() + } ?: emptyList() + } + + private fun getEvents( + enrollmentUid: String + ): List { + return eventMap[enrollmentUid]?.map { event -> + val eventBuilder = event.toBuilder() + .trackedEntityDataValues(dataValueMap[event.uid()]) + .notes(notes.filter { it.event() == event.uid() }) + eventBuilder.build() + } ?: emptyList() + } + + private fun addRelationshipsToWrapper( + wrapper: NewTrackerImporterPayloadWrapper, + relationships: List? + ) { + val partitionedRelationships = relationships?.partition { it.deleted()!! } + + partitionedRelationships?.first?.let { deleted -> + wrapper.deleted.relationships.addAll(deleted) + } + + partitionedRelationships?.second?.let { updated -> + wrapper.updated.relationships.addAll(updated) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostStateManager.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostStateManager.kt index 3cfeef339b..86c3160813 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostStateManager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostStateManager.kt @@ -32,38 +32,40 @@ import javax.inject.Inject import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore import org.hisp.dhis.android.core.event.internal.EventStore -import org.hisp.dhis.android.core.trackedentity.NewTrackerImporterTrackedEntity +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore @Reusable internal class NewTrackerImporterTrackedEntityPostStateManager @Inject internal constructor( private val trackedEntityInstanceStore: TrackedEntityInstanceStore, private val enrollmentStore: EnrollmentStore, private val eventStore: EventStore, + private val relationshipStore: RelationshipStore, private val h: StatePersistorHelper ) { - fun restoreStates(trackedEntities: List) { - setStates(trackedEntities, null) + fun restoreStates(payload: NewTrackerImporterPayload) { + setStates(payload, null) } - @Suppress("NestedBlockDepth") - fun setStates(trackedEntities: List, forcedState: State?) { + fun setStates(payload: NewTrackerImporterPayload, forcedState: State?) { val teiMap = mutableMapOf>() val enrollmentMap = mutableMapOf>() val eventMap = mutableMapOf>() + val relationshipMap = mutableMapOf>() - for (trackedEntity in trackedEntities) { - h.addState(teiMap, trackedEntity, forcedState) - for (enrollment in trackedEntity.enrollments()!!) { - h.addState(enrollmentMap, enrollment, forcedState) - for (event in enrollment.events()!!) { - h.addState(eventMap, event, forcedState) - } - } - } + val trackedEntities = payload.trackedEntities + val enrollments = trackedEntities.flatMap { it.enrollments() ?: emptyList() } + payload.enrollments + val events = enrollments.flatMap { it.events() ?: emptyList() } + payload.events + val relationships = payload.relationships + + trackedEntities.forEach { h.addState(teiMap, it, forcedState) } + enrollments.forEach { h.addState(enrollmentMap, it, forcedState) } + events.forEach { h.addState(eventMap, it, forcedState) } + relationships.forEach { h.addState(relationshipMap, it, forcedState) } h.persistStates(teiMap, trackedEntityInstanceStore) h.persistStates(enrollmentMap, enrollmentStore) h.persistStates(eventMap, eventStore) + h.persistStates(relationshipMap, relationshipStore) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackedEntityInstancePostCall.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackedEntityInstancePostCall.kt index 9ef38a1252..98d71f17e2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackedEntityInstancePostCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackedEntityInstancePostCall.kt @@ -34,39 +34,54 @@ import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor import org.hisp.dhis.android.core.arch.call.D2Progress import org.hisp.dhis.android.core.arch.call.internal.D2ProgressManager +import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.imports.internal.TEIWebResponse import org.hisp.dhis.android.core.imports.internal.TEIWebResponseHandler import org.hisp.dhis.android.core.maintenance.D2Error -import org.hisp.dhis.android.core.relationship.internal.RelationshipDeleteCall +import org.hisp.dhis.android.core.relationship.internal.RelationshipPostCall import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance @Reusable internal class OldTrackedEntityInstancePostCall @Inject internal constructor( - private val payloadGenerator: TrackedEntityInstancePostPayloadGenerator, - private val stateManager: TrackedEntityInstancePostStateManager, + private val payloadGenerator29: TrackedEntityInstancePostPayloadGenerator29, + private val stateManager: TrackerPostStateManager, private val versionManager: DHISVersionManager, private val trackedEntityInstanceService: TrackedEntityInstanceService, private val teiWebResponseHandler: TEIWebResponseHandler, private val apiCallExecutor: APICallExecutor, - private val relationshipDeleteCall: RelationshipDeleteCall + private val relationshipPostCall: RelationshipPostCall, + private val oldTrackerImporterPostCall: OldTrackerImporterPostCall ) { - @Suppress("TooGenericExceptionCaught") fun uploadTrackedEntityInstances( + trackedEntityInstances: List + ): Observable { + return if (versionManager.is2_29) { + uploadTrackedEntityInstances29(trackedEntityInstances) + } else { + oldTrackerImporterPostCall.uploadTrackedEntityInstances(trackedEntityInstances) + } + } + + @Suppress("TooGenericExceptionCaught") + private fun uploadTrackedEntityInstances29( filteredTrackedEntityInstances: List ): Observable { return Observable.create { emitter: ObservableEmitter -> - val strategy = if (versionManager.is2_29) "CREATE_AND_UPDATE" else "SYNC" - val teiPartitions = payloadGenerator.getTrackedEntityInstancesPartitions(filteredTrackedEntityInstances) + val teiPartitions = payloadGenerator29.getTrackedEntityInstancesPartitions29(filteredTrackedEntityInstances) val progressManager = D2ProgressManager(teiPartitions.size) for (partition in teiPartitions) { - val thisPartition = relationshipDeleteCall.postDeletedRelationships(partition) - val trackedEntityInstancePayload = TrackedEntityInstancePayload.create(thisPartition) + stateManager.setPayloadStates( + trackedEntityInstances = partition, + forcedState = State.UPLOADING + ) + val thisPartition = relationshipPostCall.postDeletedRelationships29(partition) try { + val trackedEntityInstancePayload = TrackedEntityInstancePayload.create(thisPartition) val webResponse = apiCallExecutor.executeObjectCallWithAcceptedErrorCodes( trackedEntityInstanceService.postTrackedEntityInstances( - trackedEntityInstancePayload, strategy + trackedEntityInstancePayload, "CREATE_AND_UPDATE" ), @Suppress("MagicNumber") listOf(409), @@ -75,8 +90,7 @@ internal class OldTrackedEntityInstancePostCall @Inject internal constructor( teiWebResponseHandler.handleWebResponse(webResponse, thisPartition) emitter.onNext(progressManager.increaseProgress(TrackedEntityInstance::class.java, false)) } catch (e: Exception) { - stateManager.restorePartitionStates(thisPartition) - + stateManager.restorePayloadStates(trackedEntityInstances = thisPartition) if (e is D2Error && e.isOffline) { emitter.onError(e) break diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayload.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayload.kt new file mode 100644 index 0000000000..69d5ee8b38 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayload.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance + +internal data class OldTrackerImporterPayload( + val trackedEntityInstances: List = emptyList(), + val events: List = emptyList(), + val relationships: List = emptyList() +) { + fun isEmpty(): Boolean = trackedEntityInstances.isEmpty() && events.isEmpty() && relationships.isEmpty() + + fun isNotEmpty(): Boolean = trackedEntityInstances.isNotEmpty() || events.isNotEmpty() || relationships.isNotEmpty() + + fun concat(other: OldTrackerImporterPayload): OldTrackerImporterPayload { + return OldTrackerImporterPayload( + trackedEntityInstances = trackedEntityInstances + other.trackedEntityInstances, + events = events + other.events, + relationships = relationships + other.relationships + ) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt new file mode 100644 index 0000000000..b1db923bff --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPayloadGenerator.kt @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.common.DataColumns +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.EnrollmentInternalAccessor +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.internal.EventStore +import org.hisp.dhis.android.core.note.Note +import org.hisp.dhis.android.core.note.internal.NoteToPostTransformer +import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager +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.TrackedEntityInstanceInternalAccessor + +@Reusable +@Suppress("TooManyFunctions") +internal class OldTrackerImporterPayloadGenerator @Inject internal constructor( + private val versionManager: DHISVersionManager, + private val relationshipRepository: RelationshipCollectionRepository, + private val trackedEntityInstanceStore: TrackedEntityInstanceStore, + private val enrollmentStore: EnrollmentStore, + private val eventStore: EventStore, + private val trackedEntityDataValueStore: TrackedEntityDataValueStore, + private val trackedEntityAttributeValueStore: TrackedEntityAttributeValueStore, + private val noteStore: IdentifiableObjectStore +) { + + private val noteTransformer = NoteToPostTransformer(versionManager) + + private data class ExtraData( + val dataValueMap: Map>, + val eventMap: Map>, + val enrollmentMap: Map>, + val attributeValueMap: Map>, + val notes: List, + val relationships: List + ) + + fun getTrackedEntityInstancePayload( + trackedEntityInstances: List + ): OldTrackerImporterPayload { + val extraData = getExtraData() + + val recreatedTeis = trackedEntityInstances.map { + getTrackedEntityInstance(it, extraData) + } + + val payload = OldTrackerImporterPayload(trackedEntityInstances = recreatedTeis) + + return addRelationships(payload, extraData) + } + + fun getEventPayload( + events: List + ): OldTrackerImporterPayload { + val extraData = getExtraData() + + val recreatedEvents = events.map { + getEvent(it, extraData) + } + + val payload = OldTrackerImporterPayload(events = recreatedEvents) + + return addRelationships(payload, extraData) + } + + private fun addRelationships( + payload: OldTrackerImporterPayload, + extraData: ExtraData + ): OldTrackerImporterPayload { + val payloadWithRelationships = payload.copy( + relationships = extractRelationships(payload, extraData) + ) + + return addRelatedItems(payloadWithRelationships, extraData) + } + + private fun addRelatedItems( + payload: OldTrackerImporterPayload, + extraData: ExtraData + ): OldTrackerImporterPayload { + var accPayload = payload + var relatedItems = getMissingItems(payload, extraData) + + while (relatedItems.isNotEmpty()) { + accPayload = accPayload.concat(relatedItems) + relatedItems = getMissingItems(accPayload, extraData) + } + + return accPayload + } + + @Suppress("NestedBlockDepth") + private fun getMissingItems( + payload: OldTrackerImporterPayload, + extraData: ExtraData + ): OldTrackerImporterPayload { + val relatedItems = payload.relationships.map { it.to() } + + val missingItems = relatedItems.filterNotNull().filter { item -> + when { + item.hasTrackedEntityInstance() -> isMissingTei(item.elementUid(), payload) + item.hasEnrollment() -> isMissingEnrollment(item.elementUid(), payload) + item.hasEvent() -> isMissingEvent(item.elementUid(), payload) + else -> false + } + } + + val missingTeis = mutableSetOf() + val missingEvents = mutableSetOf() + + missingItems.forEach { item -> + when { + item.hasTrackedEntityInstance() -> missingTeis.add(item.elementUid()) + item.hasEnrollment() -> { + enrollmentStore.selectByUid(item.elementUid())?.let { + missingTeis.add(it.trackedEntityInstance()!!) + } + } + item.hasEvent() -> { + eventStore.selectByUid(item.elementUid())?.let { event -> + if (event.enrollment() == null) { + missingEvents.add(event.uid()) + } else { + enrollmentStore.selectByUid(event.enrollment()!!)?.let { + missingTeis.add(it.trackedEntityInstance()!!) + } + } + } + } + } + } + + val trackedEntityInstances = trackedEntityInstanceStore.selectByUids(missingTeis.toList()).map { + getTrackedEntityInstance(it, extraData) + } + val events = eventStore.selectByUids(missingEvents.toList()) + + return OldTrackerImporterPayload( + trackedEntityInstances = trackedEntityInstances, + events = events + ).run { + copy(relationships = extractRelationships(this, extraData)) + } + } + + private fun isMissingTei(uid: String, payload: OldTrackerImporterPayload): Boolean { + val isIncludeInPayload = payload.trackedEntityInstances.map { it.uid() }.contains(uid) + val isPendingToSync: Boolean by lazy { + val dbTei = trackedEntityInstanceStore.selectByUid(uid) + State.uploadableStates().contains(dbTei?.aggregatedSyncState()) + } + + return !isIncludeInPayload && isPendingToSync + } + + private fun isMissingEnrollment(uid: String, payload: OldTrackerImporterPayload): Boolean { + val enrollments = payload.trackedEntityInstances.flatMap { + TrackedEntityInstanceInternalAccessor.accessEnrollments(it) ?: emptyList() + } + val isIncludeInPayload = enrollments.map { it.uid() }.contains(uid) + val isPendingToSync: Boolean by lazy { + val enrollment = enrollmentStore.selectByUid(uid) + State.uploadableStates().contains(enrollment?.aggregatedSyncState()) + } + + return !isIncludeInPayload && isPendingToSync + } + + private fun isMissingEvent(uid: String, payload: OldTrackerImporterPayload): Boolean { + val events = payload.trackedEntityInstances.flatMap { + TrackedEntityInstanceInternalAccessor.accessEnrollments(it) ?: emptyList() + }.flatMap { + EnrollmentInternalAccessor.accessEvents(it) ?: emptyList() + } + payload.events + + val isIncludedInPayload = events.map { it.uid() }.contains(uid) + val isPendingToSync: Boolean by lazy { + val event = eventStore.selectByUid(uid) + State.uploadableStates().contains(event?.aggregatedSyncState()) + } + + return !isIncludedInPayload && isPendingToSync + } + + private fun getExtraData(): ExtraData { + return ExtraData( + dataValueMap = trackedEntityDataValueStore.queryByUploadableEvents(), + eventMap = eventStore.queryEventsAttachedToEnrollmentToPost(), + enrollmentMap = enrollmentStore.queryEnrollmentsToPost(), + attributeValueMap = trackedEntityAttributeValueStore.queryTrackedEntityAttributeValueToPost(), + notes = noteStore.selectWhere( + WhereClauseBuilder() + .appendInKeyStringValues( + DataColumns.SYNC_STATE, State.uploadableStatesIncludingError().map { it.name } + ) + .build() + ), + relationships = relationshipRepository.bySyncState() + .`in`(State.uploadableStatesIncludingError().toList()) + .withItems() + .blockingGet() + ) + } + + private fun extractRelationships( + payload: OldTrackerImporterPayload, + extraData: ExtraData + ): List { + val teiItems = payload.trackedEntityInstances.map { + RelationshipHelper.teiItem(it.uid()) + } + + val enrollments = payload.trackedEntityInstances.flatMap { + TrackedEntityInstanceInternalAccessor.accessEnrollments(it) ?: emptyList() + } + + val enrollmentItems = enrollments.map { + RelationshipHelper.enrollmentItem(it.uid()) + } + + val events = enrollments.flatMap { + EnrollmentInternalAccessor.accessEvents(it) ?: emptyList() + } + payload.events + + val eventItems = events.map { + RelationshipHelper.eventItem(it.uid()) + } + + val items = teiItems + enrollmentItems + eventItems + + return extraData.relationships.filter { + items.any { item -> RelationshipHelper.areItemsEqual(it.from(), item) } + } + } + + private fun getTrackedEntityInstance( + trackedEntityInstance: TrackedEntityInstance, + extraData: ExtraData + ): TrackedEntityInstance { + val enrollmentsRecreated = getEnrollments(extraData, trackedEntityInstance.uid()) + val attributeValues = extraData.attributeValueMap[trackedEntityInstance.uid()] + return TrackedEntityInstanceInternalAccessor + .insertEnrollments(trackedEntityInstance.toBuilder(), enrollmentsRecreated) + .trackedEntityAttributeValues(attributeValues ?: emptyList()) + .build() + } + + private fun getEnrollments( + extraData: ExtraData, + trackedEntityInstanceUid: String + ): List { + return extraData.enrollmentMap[trackedEntityInstanceUid]?.map { enrollment -> + val events = extraData.eventMap[enrollment.uid()]?.map { event -> + getEvent(event, extraData) + } ?: emptyList() + + EnrollmentInternalAccessor.insertEvents(enrollment.toBuilder(), events) + .notes(getEnrollmentNotes(extraData.notes, enrollment)) + .build() + } ?: emptyList() + } + + private fun getEvent(event: Event, extraData: ExtraData): Event { + val eventBuilder = event.toBuilder() + .trackedEntityDataValues(extraData.dataValueMap[event.uid()]) + .notes(getEventNotes(extraData.notes, event)) + + if (versionManager.is2_30) { + eventBuilder.geometry(null) + } + + return eventBuilder.build() + } + + private fun getEventNotes(notes: List, event: Event): List { + return notes + .filter { it.event() == event.uid() } + .map { noteTransformer.transform(it) } + } + + private fun getEnrollmentNotes(notes: List, enrollment: Enrollment): List { + return notes + .filter { it.enrollment() == enrollment.uid() } + .map { noteTransformer.transform(it) } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPostCall.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPostCall.kt new file mode 100644 index 0000000000..b36439b303 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/OldTrackerImporterPostCall.kt @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import dagger.Reusable +import io.reactivex.Observable +import io.reactivex.ObservableEmitter +import java.net.HttpURLConnection.HTTP_CONFLICT +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor +import org.hisp.dhis.android.core.arch.call.D2Progress +import org.hisp.dhis.android.core.arch.call.internal.D2ProgressManager +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.internal.EventImportHandler +import org.hisp.dhis.android.core.event.internal.EventPayload +import org.hisp.dhis.android.core.event.internal.EventService +import org.hisp.dhis.android.core.imports.internal.EventWebResponse +import org.hisp.dhis.android.core.imports.internal.TEIWebResponse +import org.hisp.dhis.android.core.imports.internal.TEIWebResponseHandler +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.relationship.internal.RelationshipPostCall +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance + +@Reusable +internal class OldTrackerImporterPostCall @Inject internal constructor( + private val trackerImporterPayloadGenerator: OldTrackerImporterPayloadGenerator, + private val trackerStateManager: TrackerPostStateManager, + private val trackedEntityInstanceService: TrackedEntityInstanceService, + private val eventService: EventService, + private val teiWebResponseHandler: TEIWebResponseHandler, + private val eventImportHandler: EventImportHandler, + private val apiCallExecutor: APICallExecutor, + private val relationshipPostCall: RelationshipPostCall +) { + + fun uploadTrackedEntityInstances( + trackedEntityInstances: List + ): Observable { + val payload = trackerImporterPayloadGenerator.getTrackedEntityInstancePayload(trackedEntityInstances) + return uploadPayload(payload) + } + + fun uploadEvents( + events: List + ): Observable { + val payload = trackerImporterPayloadGenerator.getEventPayload(events) + return uploadPayload(payload) + } + + private fun uploadPayload( + payload: OldTrackerImporterPayload + ): Observable { + return Observable.defer { + val partitionedRelationships = payload.relationships.partition { it.deleted()!! } + + Observable.concat( + relationshipPostCall.deleteRelationships(partitionedRelationships.first), + postTrackedEntityInstances(payload.trackedEntityInstances), + postEvents(payload.events), + relationshipPostCall.postRelationships(partitionedRelationships.second) + ) + } + } + + @Suppress("TooGenericExceptionCaught") + private fun postTrackedEntityInstances( + trackedEntityInstances: List + ): Observable { + return Observable.create { emitter: ObservableEmitter -> + val progressManager = D2ProgressManager(null) + val teiPartitions = trackedEntityInstances.chunked(TrackedEntityInstanceService.DEFAULT_PAGE_SIZE) + + for (partition in teiPartitions) { + try { + trackerStateManager.setPayloadStates( + trackedEntityInstances = partition, + forcedState = State.UPLOADING + ) + val trackedEntityInstancePayload = TrackedEntityInstancePayload.create(partition) + val webResponse = apiCallExecutor.executeObjectCallWithAcceptedErrorCodes( + trackedEntityInstanceService.postTrackedEntityInstances( + trackedEntityInstancePayload, "SYNC" + ), + @Suppress("MagicNumber") + listOf(HTTP_CONFLICT), + TEIWebResponse::class.java + ) + teiWebResponseHandler.handleWebResponse(webResponse, partition) + emitter.onNext(progressManager.increaseProgress(TrackedEntityInstance::class.java, false)) + } catch (e: Exception) { + trackerStateManager.restorePayloadStates(partition) + if (e is D2Error && e.isOffline) { + emitter.onError(e) + break + } else { + emitter.onNext( + progressManager.increaseProgress( + TrackedEntityInstance::class.java, + false + ) + ) + } + } + } + emitter.onComplete() + } + } + + @Suppress("TooGenericExceptionCaught") + private fun postEvents( + events: List + ): Observable { + val progressManager = D2ProgressManager(null) + + return if (events.isEmpty()) { + Observable.just(progressManager.increaseProgress(Event::class.java, true)) + } else { + Observable.defer { + val eventPayload = EventPayload() + trackerStateManager.setPayloadStates( + events = events, + forcedState = State.UPLOADING + ) + + eventPayload.events = events + val strategy = "SYNC" + try { + val webResponse = apiCallExecutor.executeObjectCallWithAcceptedErrorCodes( + eventService.postEvents(eventPayload, strategy), + @Suppress("MagicNumber") + listOf(HTTP_CONFLICT), + EventWebResponse::class.java + ) + eventImportHandler.handleEventImportSummaries( + eventImportSummaries = webResponse?.response()?.importSummaries(), + events = events, + // TODO Manage tracker events + enrollmentUid = null, + teiUid = null + ) + Observable.just(progressManager.increaseProgress(Event::class.java, true)) + } catch (e: Exception) { + trackerStateManager.restorePayloadStates(events = events) + Observable.error(e) + } + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/StatePersistorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/StatePersistorHelper.kt index 0e546fc2eb..ae2bd0e326 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/StatePersistorHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/StatePersistorHelper.kt @@ -52,12 +52,12 @@ internal class StatePersistorHelper @Inject internal constructor() { private fun getStateToSet(o: O, forcedState: State?): State where O : DataObject, O : ObjectWithUidInterface { return forcedState - ?: if (o.state() == State.UPLOADING) State.TO_UPDATE else o.state() + ?: if (o.syncState() == State.UPLOADING) State.TO_UPDATE else o.syncState() } fun persistStates(map: Map>, store: IdentifiableDeletableDataObjectStore<*>) { for ((key, value) in map) { - store.setState(value, key) + store.setSyncState(value, key) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeValueStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeValueStoreImpl.java index b878e91eb3..389d43842a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeValueStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeValueStoreImpl.java @@ -39,6 +39,7 @@ import org.hisp.dhis.android.core.arch.db.stores.projections.internal.SingleParentChildProjection; import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper; import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper; +import org.hisp.dhis.android.core.common.DataColumns; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue; import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueTableInfo; @@ -103,7 +104,10 @@ public Map> queryTrackedEntityAttribut private String teiInUploadableState() { String states = CollectionsHelper.commaAndSpaceSeparatedArrayValues( CollectionsHelper.withSingleQuotationMarksArray(EnumHelper.asStringList(State.uploadableStates()))); - return "(TrackedEntityInstance.state IN (" + states + "))"; + return "(" + TrackedEntityAttributeValueTableInfo.Columns.TRACKED_ENTITY_INSTANCE + + "." + + DataColumns.AGGREGATED_SYNC_STATE + + " IN (" + states + "))"; } @Override diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java index d83d15e593..de78803be0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStore.java @@ -47,4 +47,6 @@ public interface TrackedEntityDataValueStore extends ObjectWithoutUidStore> querySingleEventsTrackedEntityDataValues(); Map> queryTrackerTrackedEntityDataValues(); + + Map> queryByUploadableEvents(); } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java index 15941fcd67..57d6b7f40b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityDataValueStoreImpl.java @@ -42,6 +42,7 @@ import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper; import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper; import org.hisp.dhis.android.core.common.State; +import org.hisp.dhis.android.core.event.EventTableInfo; import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue; import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueTableInfo; @@ -129,7 +130,7 @@ public Map> querySingleEventsTrackedEntityD private String eventInUploadableState() { String states = CollectionsHelper.commaAndSpaceSeparatedArrayValues( CollectionsHelper.withSingleQuotationMarksArray(EnumHelper.asStringList(State.uploadableStates()))); - return "(Event.state IN (" + states + "))"; + return "(Event." + EventTableInfo.Columns.SYNC_STATE + " IN (" + states + "))"; } @Override @@ -143,6 +144,16 @@ public Map> queryTrackerTrackedEntityDataVa return queryTrackedEntityDataValues(queryStatement); } + @Override + public Map> queryByUploadableEvents() { + + String queryStatement = "SELECT TrackedEntityDataValue.* " + + " FROM (TrackedEntityDataValue INNER JOIN Event ON TrackedEntityDataValue.event = Event.uid) " + + " WHERE " + eventInUploadableState() + ";"; + + return queryTrackedEntityDataValues(queryStatement); + } + private Map> queryTrackedEntityDataValues(String queryStatement) { List dataValueList = new ArrayList<>(); diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceEntityDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceEntityDIModule.java index dd24838f17..af3343b038 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceEntityDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceEntityDIModule.java @@ -80,7 +80,7 @@ TrackedEntityInstanceUidHelper uidHelper(TrackedEntityInstanceUidHelperImpl impl @Reusable OrphanCleaner enrollmentOrphanCleaner(DatabaseAdapter databaseAdapter) { return new DataOrphanCleanerImpl<>(EnrollmentTableInfo.TABLE_INFO.name(), - EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE, DataColumns.STATE, databaseAdapter); + EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE, DataColumns.SYNC_STATE, databaseAdapter); } @Provides diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceHandler.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceHandler.java index 6467dc6c16..c1ea119e60 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceHandler.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceHandler.java @@ -105,7 +105,8 @@ protected void afterObjectHandled(final TrackedEntityInstance trackedEntityInsta TrackedEntityInstanceInternalAccessor.accessEnrollments(trackedEntityInstance); if (enrollments != null) { enrollmentHandler.handleMany(enrollments, enrollment -> enrollment.toBuilder() - .state(State.SYNCED) + .syncState(State.SYNCED) + .aggregatedSyncState(State.SYNCED) .build(), overwrite); } @@ -122,12 +123,12 @@ protected void afterObjectHandled(final TrackedEntityInstance trackedEntityInsta @Override protected TrackedEntityInstance addRelationshipState(TrackedEntityInstance o) { - return o.toBuilder().state(State.RELATIONSHIP).build(); + return o.toBuilder().aggregatedSyncState(State.RELATIONSHIP).syncState(State.RELATIONSHIP).build(); } @Override protected TrackedEntityInstance addSyncedState(TrackedEntityInstance o) { - return o.toBuilder().state(State.SYNCED).build(); + return o.toBuilder().aggregatedSyncState(State.SYNCED).syncState(State.SYNCED).build(); } @Override diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandler.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandler.java deleted file mode 100644 index f4edfe16ef..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandler.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.trackedentity.internal; - -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; -import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.common.internal.DataStatePropagator; -import org.hisp.dhis.android.core.enrollment.Enrollment; -import org.hisp.dhis.android.core.enrollment.internal.EnrollmentImportHandler; -import org.hisp.dhis.android.core.imports.TrackerImportConflict; -import org.hisp.dhis.android.core.imports.TrackerImportConflictTableInfo; -import org.hisp.dhis.android.core.imports.internal.BaseImportSummaryHelper; -import org.hisp.dhis.android.core.imports.internal.EnrollmentImportSummaries; -import org.hisp.dhis.android.core.imports.internal.ImportConflict; -import org.hisp.dhis.android.core.imports.internal.TEIImportSummary; -import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser; -import org.hisp.dhis.android.core.relationship.Relationship; -import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository; -import org.hisp.dhis.android.core.relationship.RelationshipHelper; -import org.hisp.dhis.android.core.relationship.internal.RelationshipDHISVersionManager; -import org.hisp.dhis.android.core.relationship.internal.RelationshipStore; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceTableInfo; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import javax.inject.Inject; - -import dagger.Reusable; - -import static org.hisp.dhis.android.core.arch.db.stores.internal.StoreUtils.getState; - -@Reusable -@SuppressWarnings("PMD.ExcessiveImports") -public final class TrackedEntityInstanceImportHandler { - private final TrackedEntityInstanceStore trackedEntityInstanceStore; - private final EnrollmentImportHandler enrollmentImportHandler; - private final ObjectStore trackerImportConflictStore; - private final TrackerImportConflictParser trackerImportConflictParser; - private final RelationshipStore relationshipStore; - private final DataStatePropagator dataStatePropagator; - private final RelationshipDHISVersionManager relationshipDHISVersionManager; - private final RelationshipCollectionRepository relationshipRepository; - - @Inject - TrackedEntityInstanceImportHandler(@NonNull TrackedEntityInstanceStore trackedEntityInstanceStore, - @NonNull EnrollmentImportHandler enrollmentImportHandler, - @NonNull ObjectStore trackerImportConflictStore, - @NonNull TrackerImportConflictParser trackerImportConflictParser, - @NonNull RelationshipStore relationshipStore, - @NonNull DataStatePropagator dataStatePropagator, - @NonNull RelationshipDHISVersionManager relationshipDHISVersionManager, - @NonNull RelationshipCollectionRepository relationshipRepository) { - this.trackedEntityInstanceStore = trackedEntityInstanceStore; - this.enrollmentImportHandler = enrollmentImportHandler; - this.trackerImportConflictStore = trackerImportConflictStore; - this.trackerImportConflictParser = trackerImportConflictParser; - this.relationshipStore = relationshipStore; - this.dataStatePropagator = dataStatePropagator; - this.relationshipDHISVersionManager = relationshipDHISVersionManager; - this.relationshipRepository = relationshipRepository; - } - - public void handleTrackedEntityInstanceImportSummaries(List teiImportSummaries, - List instances) { - if (teiImportSummaries != null) { - for (TEIImportSummary teiImportSummary : teiImportSummaries) { - String teiUid = teiImportSummary == null ? null : teiImportSummary.reference(); - - if (teiUid == null) { - continue; - } - - State state = getState(teiImportSummary.status()); - deleteTEIConflicts(teiUid); - - HandleAction handleAction = trackedEntityInstanceStore.setStateOrDelete(teiUid, state); - - if (state.equals(State.ERROR) || state.equals(State.WARNING)) { - dataStatePropagator.resetUploadingEnrollmentAndEventStates(teiUid); - setRelationshipsState(teiUid, State.TO_UPDATE); - } else { - setRelationshipsState(teiUid, State.SYNCED); - } - - if (handleAction != HandleAction.Delete) { - storeTEIImportConflicts(teiImportSummary); - if (teiImportSummary.enrollments() != null) { - EnrollmentImportSummaries importEnrollment = teiImportSummary.enrollments(); - - enrollmentImportHandler.handleEnrollmentImportSummary( - importEnrollment.importSummaries(), - getEnrollments(teiUid, instances), - teiUid); - } - } - } - } - - processIgnoredTEIs(teiImportSummaries, instances); - } - - private void storeTEIImportConflicts(TEIImportSummary teiImportSummary) { - List trackerImportConflicts = new ArrayList<>(); - if (teiImportSummary.description() != null) { - trackerImportConflicts.add(getConflictBuilder(teiImportSummary) - .conflict(teiImportSummary.description()) - .displayDescription(teiImportSummary.description()) - .value(teiImportSummary.reference()) - .build()); - } - - if (teiImportSummary.conflicts() != null) { - for (ImportConflict importConflict : teiImportSummary.conflicts()) { - trackerImportConflicts.add(trackerImportConflictParser - .getTrackedEntityInstanceConflict(importConflict, getConflictBuilder(teiImportSummary))); - } - } - - for (TrackerImportConflict trackerImportConflict : trackerImportConflicts) { - trackerImportConflictStore.insert(trackerImportConflict); - } - } - - private void deleteTEIConflicts(String teiUid) { - String whereClause = new WhereClauseBuilder() - .appendKeyStringValue(TrackerImportConflictTableInfo.Columns.TRACKED_ENTITY_INSTANCE, teiUid) - .appendKeyStringValue( - TrackerImportConflictTableInfo.Columns.TABLE_REFERENCE, - TrackedEntityInstanceTableInfo.TABLE_INFO.name()) - .build(); - trackerImportConflictStore.deleteWhereIfExists(whereClause); - } - - private void setRelationshipsState(String trackedEntityInstanceUid, State state) { - List dbRelationships = - relationshipRepository.getByItem(RelationshipHelper.teiItem(trackedEntityInstanceUid), true); - - List ownedRelationships = relationshipDHISVersionManager - .getOwnedRelationships(dbRelationships, trackedEntityInstanceUid); - - for (Relationship relationship : ownedRelationships) { - relationshipStore.setStateOrDelete(relationship.uid(), state); - } - } - - private void processIgnoredTEIs(List teiImportSummaries, - List instances) { - - List processedTEIs = BaseImportSummaryHelper.getReferences(teiImportSummaries); - for (TrackedEntityInstance instance : instances) { - if (!processedTEIs.contains(instance.uid())) { - trackedEntityInstanceStore.setStateOrDelete(instance.uid(), State.TO_UPDATE); - dataStatePropagator.resetUploadingEnrollmentAndEventStates(instance.uid()); - setRelationshipsState(instance.uid(), State.TO_UPDATE); - } - } - } - - private List getEnrollments(String trackedEntityInstanceUid, - List instances) { - for (TrackedEntityInstance instance : instances) { - if (trackedEntityInstanceUid.equals(instance.uid())) { - return TrackedEntityInstanceInternalAccessor.accessEnrollments(instance); - } - } - return Collections.emptyList(); - } - - private TrackerImportConflict.Builder getConflictBuilder(TEIImportSummary teiImportSummary) { - return TrackerImportConflict.builder() - .trackedEntityInstance(teiImportSummary.reference()) - .tableReference(TrackedEntityInstanceTableInfo.TABLE_INFO.name()) - .status(teiImportSummary.status()) - .created(new Date()); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandler.kt new file mode 100644 index 0000000000..3339f21d2e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandler.kt @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import dagger.Reusable +import java.util.* +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreUtils.getSyncState +import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.internal.DataStatePropagator +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentImportHandler +import org.hisp.dhis.android.core.imports.TrackerImportConflict +import org.hisp.dhis.android.core.imports.internal.BaseImportSummaryHelper.getReferences +import org.hisp.dhis.android.core.imports.internal.TEIImportSummary +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore +import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.relationship.internal.RelationshipDHISVersionManager +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceTableInfo + +@Reusable +internal class TrackedEntityInstanceImportHandler @Inject internal constructor( + private val trackedEntityInstanceStore: TrackedEntityInstanceStore, + private val enrollmentImportHandler: EnrollmentImportHandler, + private val trackerImportConflictStore: TrackerImportConflictStore, + private val trackerImportConflictParser: TrackerImportConflictParser, + private val relationshipStore: RelationshipStore, + private val dataStatePropagator: DataStatePropagator, + private val relationshipDHISVersionManager: RelationshipDHISVersionManager, + private val relationshipRepository: RelationshipCollectionRepository +) { + + fun handleTrackedEntityInstanceImportSummaries( + teiImportSummaries: List?, + instances: List + ) { + teiImportSummaries?.filterNotNull()?.forEach { teiImportSummary -> + teiImportSummary.reference()?.let { teiUid -> + + val state = getSyncState(teiImportSummary.status()) + trackerImportConflictStore.deleteTrackedEntityConflicts(teiUid) + + val handleAction = trackedEntityInstanceStore.setSyncStateOrDelete(teiUid, state) + + if (state == State.ERROR || state == State.WARNING) { + dataStatePropagator.resetUploadingEnrollmentAndEventStates(teiUid) + setRelationshipsState(teiUid, State.TO_UPDATE) + } else { + setRelationshipsState(teiUid, State.SYNCED) + } + + if (handleAction !== HandleAction.Delete) { + storeTEIImportConflicts(teiImportSummary) + + handleEnrollmentImportSummaries(teiImportSummary, instances) + } + } + } + + processIgnoredTEIs(teiImportSummaries, instances) + } + + private fun handleEnrollmentImportSummaries( + teiImportSummary: TEIImportSummary, + instances: List + ) { + teiImportSummary.enrollments()?.importSummaries().let { importSummaries -> + val teiUid = teiImportSummary.reference()!! + enrollmentImportHandler.handleEnrollmentImportSummary( + importSummaries, + getEnrollments(teiUid, instances), + teiUid + ) + } + } + + private fun storeTEIImportConflicts(teiImportSummary: TEIImportSummary) { + val trackerImportConflicts: MutableList = ArrayList() + if (teiImportSummary.description() != null) { + trackerImportConflicts.add( + getConflictBuilder(teiImportSummary) + .conflict(teiImportSummary.description()) + .displayDescription(teiImportSummary.description()) + .value(teiImportSummary.reference()) + .build() + ) + } + teiImportSummary.conflicts()?.forEach { importConflict -> + trackerImportConflicts.add( + trackerImportConflictParser + .getTrackedEntityInstanceConflict(importConflict, getConflictBuilder(teiImportSummary)) + ) + } + + trackerImportConflicts.forEach { trackerImportConflictStore.insert(it) } + } + + private fun setRelationshipsState(trackedEntityInstanceUid: String?, state: State) { + val dbRelationships = + relationshipRepository.getByItem(RelationshipHelper.teiItem(trackedEntityInstanceUid), true) + val ownedRelationships = relationshipDHISVersionManager + .getOwnedRelationships(dbRelationships, trackedEntityInstanceUid) + for (relationship in ownedRelationships) { + relationshipStore.setSyncStateOrDelete(relationship.uid()!!, state) + } + } + + private fun processIgnoredTEIs( + teiImportSummaries: List?, + instances: List + ) { + val processedTEIs = getReferences(teiImportSummaries) + + instances.filterNot { processedTEIs.contains(it.uid()) }.forEach { instance -> + trackedEntityInstanceStore.setSyncStateOrDelete(instance.uid(), State.TO_UPDATE) + dataStatePropagator.resetUploadingEnrollmentAndEventStates(instance.uid()) + setRelationshipsState(instance.uid(), State.TO_UPDATE) + } + } + + private fun getEnrollments( + trackedEntityInstanceUid: String, + instances: List + ): List { + return instances.find { it.uid() == trackedEntityInstanceUid }?.let { + TrackedEntityInstanceInternalAccessor.accessEnrollments(it) + } ?: listOf() + } + + private fun getConflictBuilder(teiImportSummary: TEIImportSummary): TrackerImportConflict.Builder { + return TrackerImportConflict.builder() + .trackedEntityInstance(teiImportSummary.reference()) + .tableReference(TrackedEntityInstanceTableInfo.TABLE_INFO.name()) + .status(teiImportSummary.status()) + .created(Date()) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceParentPostCall.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostParentCall.kt similarity index 97% rename from core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceParentPostCall.kt rename to core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostParentCall.kt index a7a2e543fe..ef5e820c02 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceParentPostCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostParentCall.kt @@ -35,7 +35,7 @@ import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance import org.hisp.dhis.android.core.tracker.importer.internal.TrackedEntityInstanceTrackerImporterPostCall @Reusable -internal class TrackedEntityInstanceParentPostCall @Inject internal constructor( +internal class TrackedEntityInstancePostParentCall @Inject internal constructor( private val oldCall: OldTrackedEntityInstancePostCall, private val trackerImporterCall: TrackedEntityInstanceTrackerImporterPostCall ) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator29.kt similarity index 71% rename from core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator.kt rename to core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator29.kt index 3c038854ec..94f8e578eb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostPayloadGenerator29.kt @@ -28,11 +28,9 @@ package org.hisp.dhis.android.core.trackedentity.internal import dagger.Reusable -import java.util.ArrayList import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore -import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper import org.hisp.dhis.android.core.arch.helpers.UidsHelper.getUidsList import org.hisp.dhis.android.core.common.DataColumns import org.hisp.dhis.android.core.common.State @@ -53,9 +51,9 @@ import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValue import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor -@Suppress("LongParameterList") @Reusable -internal class TrackedEntityInstancePostPayloadGenerator @Inject internal constructor( +@Suppress("LongParameterList") +internal class TrackedEntityInstancePostPayloadGenerator29 @Inject internal constructor( private val versionManager: DHISVersionManager, private val relationshipDHISVersionManager: RelationshipDHISVersionManager, private val relationshipRepository: RelationshipCollectionRepository, @@ -65,41 +63,51 @@ internal class TrackedEntityInstancePostPayloadGenerator @Inject internal constr private val trackedEntityDataValueStore: TrackedEntityDataValueStore, private val trackedEntityAttributeValueStore: TrackedEntityAttributeValueStore, private val relationshipItemStore: RelationshipItemStore, - private val noteStore: IdentifiableObjectStore, - private val stateManager: TrackedEntityInstancePostStateManager + private val noteStore: IdentifiableObjectStore ) { - fun getTrackedEntityInstancesPartitions( + private data class ExtraData( + val dataValueMap: Map>, + val eventMap: Map>, + val enrollmentMap: Map>, + val attributeValueMap: Map>, + val notes: List + ) + + fun getTrackedEntityInstancesPartitions29( filteredTrackedEntityInstances: List ): List> { - val dataValueMap = trackedEntityDataValueStore.queryTrackerTrackedEntityDataValues() - val eventMap = eventStore.queryEventsAttachedToEnrollmentToPost() - val enrollmentMap = enrollmentStore.queryEnrollmentsToPost() - val attributeValueMap = trackedEntityAttributeValueStore.queryTrackedEntityAttributeValueToPost() - val whereNotesClause = WhereClauseBuilder() - .appendInKeyStringValues( - DataColumns.STATE, State.uploadableStatesIncludingError().map { it.name } - ) - .build() - val notes = noteStore.selectWhere(whereNotesClause) + val extraData = getExtraData() val trackedEntityInstancesToSync = getPagedTrackedEntityInstances(filteredTrackedEntityInstances) return trackedEntityInstancesToSync.map { partition -> - val partitionRecreated = partition.map { trackedEntityInstance -> - getTrackedEntityInstance( - trackedEntityInstance, dataValueMap, eventMap, enrollmentMap, attributeValueMap, notes - ) + partition.map { trackedEntityInstance -> + getTrackedEntityInstance(trackedEntityInstance, extraData) } - - stateManager.setPartitionStates(partitionRecreated, State.UPLOADING) - partitionRecreated } } + private fun getExtraData(): ExtraData { + return ExtraData( + dataValueMap = trackedEntityDataValueStore.queryByUploadableEvents(), + eventMap = eventStore.queryEventsAttachedToEnrollmentToPost(), + enrollmentMap = enrollmentStore.queryEnrollmentsToPost(), + attributeValueMap = trackedEntityAttributeValueStore.queryTrackedEntityAttributeValueToPost(), + notes = noteStore.selectWhere( + WhereClauseBuilder() + .appendInKeyStringValues( + DataColumns.SYNC_STATE, State.uploadableStatesIncludingError().map { it.name } + ) + .build() + ) + ) + } + private fun getPagedTrackedEntityInstances( filteredTrackedEntityInstances: List ): List> { + val partitions = filteredTrackedEntityInstances.chunked(TrackedEntityInstanceService.DEFAULT_PAGE_SIZE) + val includedUids: MutableSet = mutableSetOf() - val partitions = CollectionsHelper.setPartition(filteredTrackedEntityInstances, DEFAULT_PAGE_SIZE) val partitionsWithRelationships: MutableList> = ArrayList() for (partition in partitions) { val partitionWithoutDuplicates = partition.filterNot { o -> includedUids.contains(o.uid()) } @@ -119,16 +127,19 @@ internal class TrackedEntityInstancePostPayloadGenerator @Inject internal constr val filteredUids: List = filteredTrackedEntityInstances.map { it.uid() } val teiUidsToPost = trackedEntityInstanceStore.queryTrackedEntityInstancesToPost().map { it.uid() } val relatedTeisToPost: MutableList = ArrayList() + var internalRelatedTeis = filteredUids do { val relatedTeiUids = relationshipItemStore.getRelatedTeiUids(internalRelatedTeis) - relatedTeiUids.retainAll(teiUidsToPost) - relatedTeiUids.removeAll(filteredUids) - relatedTeiUids.removeAll(relatedTeisToPost) - relatedTeiUids.removeAll(excludedUids) - relatedTeisToPost.addAll(relatedTeiUids) - internalRelatedTeis = relatedTeiUids + val filteredToPost = relatedTeiUids + .filter { teiUidsToPost.contains(it) } + .filter { !filteredUids.contains(it) } + .filter { !relatedTeisToPost.contains(it) } + .filter { !excludedUids.contains(it) } + relatedTeisToPost.addAll(filteredToPost) + internalRelatedTeis = filteredToPost } while (internalRelatedTeis.isNotEmpty()) + for (trackedEntityInstanceInDB in trackedEntityInstancesInDBToSync) { if (relatedTeisToPost.contains(trackedEntityInstanceInDB.uid())) { filteredTrackedEntityInstances.add(trackedEntityInstanceInDB) @@ -137,18 +148,12 @@ internal class TrackedEntityInstancePostPayloadGenerator @Inject internal constr return filteredTrackedEntityInstances } - @Suppress("LongParameterList") private fun getTrackedEntityInstance( trackedEntityInstance: TrackedEntityInstance, - dataValueMap: Map>, - eventMap: Map>, - enrollmentMap: Map>, - attributeValueMap: Map>, - notes: List + extraData: ExtraData ): TrackedEntityInstance { - val enrollmentsRecreated = - getEnrollments(dataValueMap, eventMap, enrollmentMap, notes, trackedEntityInstance.uid()) - val attributeValues = attributeValueMap[trackedEntityInstance.uid()] + val enrollmentsRecreated = getEnrollments(extraData, trackedEntityInstance.uid()) + val attributeValues = extraData.attributeValueMap[trackedEntityInstance.uid()] val dbRelationships = relationshipRepository.getByItem(RelationshipHelper.teiItem(trackedEntityInstance.uid()), true) val ownedRelationships = @@ -166,30 +171,29 @@ internal class TrackedEntityInstancePostPayloadGenerator @Inject internal constr } private fun getEnrollments( - dataValueMap: Map>, - eventMap: Map>, - enrollmentMap: Map>, - notes: List, + extraData: ExtraData, trackedEntityInstanceUid: String ): List { - return enrollmentMap[trackedEntityInstanceUid]?.map { enrollment -> + return extraData.enrollmentMap[trackedEntityInstanceUid]?.map { enrollment -> val transformer = NoteToPostTransformer(versionManager) - val events = eventMap[enrollment.uid()]?.map { event -> - val eventBuilder = event.toBuilder() - .trackedEntityDataValues(dataValueMap[event.uid()]) - .notes(getEventNotes(notes, event, transformer)) - if (versionManager.is2_30) { - eventBuilder.geometry(null).build() - } else { - eventBuilder.build() - } + val events = extraData.eventMap[enrollment.uid()]?.map { event -> + getEvent(event, extraData, transformer) } ?: emptyList() + EnrollmentInternalAccessor.insertEvents(enrollment.toBuilder(), events) - .notes(getEnrollmentNotes(notes, enrollment, transformer)) + .notes(getEnrollmentNotes(extraData.notes, enrollment, transformer)) .build() } ?: emptyList() } + private fun getEvent(event: Event, extraData: ExtraData, transformer: NoteToPostTransformer): Event { + val eventBuilder = event.toBuilder() + .trackedEntityDataValues(extraData.dataValueMap[event.uid()]) + .notes(getEventNotes(extraData.notes, event, transformer)) + + return eventBuilder.build() + } + private fun getEventNotes(notes: List, event: Event, t: NoteToPostTransformer): List { return notes .filter { it.event() == event.uid() } @@ -201,8 +205,4 @@ internal class TrackedEntityInstancePostPayloadGenerator @Inject internal constr .filter { it.enrollment() == enrollment.uid() } .map { t.transform(it) } } - - companion object { - private const val DEFAULT_PAGE_SIZE = 20 - } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceProjectionTransformer.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceProjectionTransformer.java index 9fe8d772ac..4ca05b7d1e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceProjectionTransformer.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceProjectionTransformer.java @@ -46,7 +46,8 @@ public TrackedEntityInstance transform(TrackedEntityInstanceCreateProjection pro return TrackedEntityInstance.builder() .uid(generatedUid) - .state(State.TO_POST) + .aggregatedSyncState(State.TO_POST) + .syncState(State.TO_POST) .created(creationDate) .lastUpdated(creationDate) .createdAtClient(creationDate) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceService.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceService.java index 696afe0e59..31a131895f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceService.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceService.java @@ -76,6 +76,8 @@ public interface TrackedEntityInstanceService { String ASSIGNED_USER_MODE = "assignedUserMode"; String ORDER = "order"; + Integer DEFAULT_PAGE_SIZE = 20; + @POST(TRACKED_ENTITY_INSTANCES) Call postTrackedEntityInstances( @Body TrackedEntityInstancePayload trackedEntityInstances, diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStore.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStore.java index e800667f2f..41698677f3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStore.java @@ -29,6 +29,7 @@ package org.hisp.dhis.android.core.trackedentity.internal; import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStore; +import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; import java.util.List; @@ -42,4 +43,6 @@ public interface TrackedEntityInstanceStore extends IdentifiableDeletableDataObj List querySyncedTrackedEntityInstanceUids(); List queryMissingRelationshipsUids(); + + int setAggregatedSyncState(String uid, State state); } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStoreImpl.java index 31b1fc1edd..e5ac7c2955 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceStoreImpl.java @@ -28,6 +28,8 @@ package org.hisp.dhis.android.core.trackedentity.internal; +import android.content.ContentValues; + import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; import org.hisp.dhis.android.core.arch.db.querybuilders.internal.SQLStatementBuilderImpl; import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; @@ -35,6 +37,7 @@ import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStoreImpl; import org.hisp.dhis.android.core.arch.helpers.internal.EnumHelper; import org.hisp.dhis.android.core.common.DataColumns; +import org.hisp.dhis.android.core.common.IdentifiableColumns; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceTableInfo; @@ -55,8 +58,9 @@ public final class TrackedEntityInstanceStoreImpl w.bind(7, o.trackedEntityType()); w.bind(8, o.geometry() == null ? null : o.geometry().type()); w.bind(9, o.geometry() == null ? null : o.geometry().coordinates()); - w.bind(10, o.state()); - w.bind(11, o.deleted()); + w.bind(10, o.syncState()); + w.bind(11, o.aggregatedSyncState()); + w.bind(12, o.deleted()); }; public TrackedEntityInstanceStoreImpl(DatabaseAdapter databaseAdapter, @@ -66,8 +70,9 @@ public TrackedEntityInstanceStoreImpl(DatabaseAdapter databaseAdapter, @Override public List queryTrackedEntityInstancesToSync() { + List uploadableStatesString = EnumHelper.asStringList(State.uploadableStates()); String whereToSyncClause = new WhereClauseBuilder() - .appendInKeyStringValues(DataColumns.STATE, EnumHelper.asStringList(State.uploadableStates())) + .appendInKeyStringValues(DataColumns.AGGREGATED_SYNC_STATE, uploadableStatesString) .build(); return selectWhere(whereToSyncClause); @@ -76,7 +81,7 @@ public List queryTrackedEntityInstancesToSync() { @Override public List queryTrackedEntityInstancesToPost() { String whereToPostClause = new WhereClauseBuilder() - .appendKeyStringValue(DataColumns.STATE, State.TO_POST.name()) + .appendKeyStringValue(DataColumns.AGGREGATED_SYNC_STATE, State.TO_POST.name()) .build(); return selectWhere(whereToPostClause); @@ -85,7 +90,7 @@ public List queryTrackedEntityInstancesToPost() { @Override public List querySyncedTrackedEntityInstanceUids() { String whereSyncedClause = new WhereClauseBuilder() - .appendKeyStringValue(DataColumns.STATE, State.SYNCED) + .appendKeyStringValue(DataColumns.AGGREGATED_SYNC_STATE, State.SYNCED) .build(); return selectUidsWhere(whereSyncedClause); @@ -94,13 +99,24 @@ public List querySyncedTrackedEntityInstanceUids() { @Override public List queryMissingRelationshipsUids() { String whereRelationshipsClause = new WhereClauseBuilder() - .appendKeyStringValue(DataColumns.STATE, State.RELATIONSHIP) + .appendKeyStringValue(DataColumns.AGGREGATED_SYNC_STATE, State.RELATIONSHIP) .appendIsNullValue(TrackedEntityInstanceTableInfo.Columns.ORGANISATION_UNIT) .build(); return selectUidsWhere(whereRelationshipsClause); } + @Override + public int setAggregatedSyncState(String uid, State state) { + ContentValues updates = new ContentValues(); + updates.put(DataColumns.AGGREGATED_SYNC_STATE, state.toString()); + String whereClause = new WhereClauseBuilder() + .appendKeyStringValue(IdentifiableColumns.UID, uid) + .build(); + + return updateWhere(updates, whereClause); + } + public static TrackedEntityInstanceStore create(DatabaseAdapter databaseAdapter) { SQLStatementBuilderImpl statementBuilder = new SQLStatementBuilderImpl( TrackedEntityInstanceTableInfo.TABLE_INFO.name(), diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostStateManager.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerPostStateManager.kt similarity index 78% rename from core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostStateManager.kt rename to core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerPostStateManager.kt index 7ba78c7841..d652f1cceb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstancePostStateManager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerPostStateManager.kt @@ -34,14 +34,16 @@ import org.hisp.dhis.android.core.common.CoreColumns import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.enrollment.EnrollmentInternalAccessor import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore +import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.event.internal.EventStore +import org.hisp.dhis.android.core.relationship.Relationship import org.hisp.dhis.android.core.relationship.internal.RelationshipStore import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceInternalAccessor @Reusable -internal class TrackedEntityInstancePostStateManager @Inject internal constructor( +internal class TrackerPostStateManager @Inject internal constructor( private val versionManager: DHISVersionManager, private val trackedEntityInstanceStore: TrackedEntityInstanceStore, private val enrollmentStore: EnrollmentStore, @@ -50,26 +52,35 @@ internal class TrackedEntityInstancePostStateManager @Inject internal constructo private val h: StatePersistorHelper ) { - fun restorePartitionStates(partition: List) { - setPartitionStates(partition, null) + fun restorePayloadStates( + trackedEntityInstances: List = emptyList(), + events: List = emptyList(), + relationships: List = emptyList() + ) { + setPayloadStates(trackedEntityInstances, events, relationships, null) } @Suppress("NestedBlockDepth") - fun setPartitionStates(partition: List, forcedState: State?) { + fun setPayloadStates( + trackedEntityInstances: List = emptyList(), + events: List = emptyList(), + relationships: List = emptyList(), + forcedState: State? + ) { val teiMap: MutableMap> = mutableMapOf() val enrollmentMap: MutableMap> = mutableMapOf() val eventMap: MutableMap> = mutableMapOf() val relationshipMap: MutableMap> = mutableMapOf() - for (instance in partition) { + trackedEntityInstances.forEach { instance -> h.addState(teiMap, instance, forcedState) - for (enrollment in TrackedEntityInstanceInternalAccessor.accessEnrollments(instance)) { + TrackedEntityInstanceInternalAccessor.accessEnrollments(instance)?.forEach { enrollment -> h.addState(enrollmentMap, enrollment, forcedState) for (event in EnrollmentInternalAccessor.accessEvents(enrollment)) { h.addState(eventMap, event, forcedState) } } - for (r in TrackedEntityInstanceInternalAccessor.accessRelationships(instance)) { + TrackedEntityInstanceInternalAccessor.accessRelationships(instance)?.forEach { r -> if (versionManager.is2_29) { val whereClause = WhereClauseBuilder().appendKeyStringValue(CoreColumns.ID, r.id()).build() val dbRelationship = relationshipStore.selectOneWhere(whereClause) @@ -80,6 +91,10 @@ internal class TrackedEntityInstancePostStateManager @Inject internal constructo } } + events.forEach { h.addState(eventMap, it, forcedState) } + + relationships.forEach { h.addState(relationshipMap, it, forcedState) } + h.persistStates(teiMap, trackedEntityInstanceStore) h.persistStates(enrollmentMap, enrollmentStore) h.persistStates(eventMap, eventStore) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelper.kt index fd8e4773e7..00d3f2b02e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelper.kt @@ -126,9 +126,9 @@ internal class TrackedEntityInstanceLocalQueryHelper @Inject constructor( } if (scope.states() == null) { - where.appendNotKeyStringValue(dot(teiAlias, DataColumns.STATE), State.RELATIONSHIP.name) + where.appendNotKeyStringValue(dot(teiAlias, DataColumns.AGGREGATED_SYNC_STATE), State.RELATIONSHIP.name) } else { - where.appendInKeyEnumValues(dot(teiAlias, DataColumns.STATE), scope.states()) + where.appendInKeyEnumValues(dot(teiAlias, DataColumns.AGGREGATED_SYNC_STATE), scope.states()) } if (!scope.includeDeleted()) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobQueryCall.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobQueryCall.kt index 469b277e72..20eef19d96 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobQueryCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobQueryCall.kt @@ -34,64 +34,76 @@ import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor import org.hisp.dhis.android.core.arch.call.D2Progress import org.hisp.dhis.android.core.arch.call.internal.D2ProgressManager -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore -import org.hisp.dhis.android.core.common.StorableObjectWithUid +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +internal const val ATTEMPTS_AFTER_UPLOAD = 3 +internal const val ATTEMPTS_WHEN_QUERYING = 1 @Reusable internal class JobQueryCall @Inject internal constructor( private val service: TrackerImporterService, private val apiCallExecutor: APICallExecutor, - private val trackerJobStore: IdentifiableObjectStore + private val trackerJobObjectStore: ObjectWithoutUidStore, + private val handler: JobReportHandler ) { - fun storeJob(jobId: String) { - trackerJobStore.insert(StorableObjectWithUid.create(jobId)) - } - fun queryPendingJobs(): Observable { return Observable.just(true) .flatMapIterable { - val pendingJobs = trackerJobStore.selectAll() - pendingJobs.withIndex().map { ij -> Pair(ij.value, ij.index == pendingJobs.size - 1) } + val pendingJobs = trackerJobObjectStore.selectAll() + .sortedBy { it.lastUpdated() } + .groupBy { it.jobUid() } + .toList() + + pendingJobs.withIndex().map { + Triple(it.value.first, it.value.second, it.index == pendingJobs.size - 1) + } } - .flatMap { jobWithIsLast -> queryJob(jobWithIsLast.first.uid(), jobWithIsLast.second) } + .flatMap { queryJobInternal(it.first, it.second, it.third, ATTEMPTS_WHEN_QUERYING) } } fun queryJob(jobId: String): Observable { - return queryJob(jobId, true) + val jobObjects = trackerJobObjectStore.selectWhere(byJobIdClause(jobId)) + return queryJobInternal(jobId, jobObjects, true, ATTEMPTS_AFTER_UPLOAD) } - private fun queryJob(jobId: String, isLastJob: Boolean): Observable { + private fun queryJobInternal( + jobId: String, + jobObjects: List, + isLastJob: Boolean, + attempts: Int + ): Observable { val progressManager = D2ProgressManager(null) @Suppress("MagicNumber") return Observable.interval(0, 5, TimeUnit.SECONDS) .map { - apiCallExecutor.executeObjectCall(service.getJob(jobId)) - } - .map { it.any { ji -> ji.completed } } - .takeUntil { it } - .doOnNext { - if (it) { - val jobReport = apiCallExecutor.executeObjectCall(service.getJobReport(jobId)) - trackerJobStore.delete(jobId) - println(jobReport) - // TODO manage status + try { + downloadAndHandle(jobId, jobObjects) + true + } catch (_: Throwable) { + false } } - .take(3) + .takeUntil { it } + .take(attempts.toLong()) .map { progressManager.increaseProgress( JobReport::class.java, it && isLastJob ) } - .onErrorResumeNext { _: Throwable -> - return@onErrorResumeNext Observable.just( - progressManager.increaseProgress( - JobReport::class.java, - false - ) - ) - } } + + private fun downloadAndHandle(jobId: String, jobObjects: List) { + val jobReport = apiCallExecutor.executeObjectCallWithErrorCatcher( + service.getJobReport(jobId), + JobQueryErrorCatcher() + ) + trackerJobObjectStore.deleteWhere(byJobIdClause(jobId)) + handler.handle(jobReport, jobObjects) + } + + private fun byJobIdClause(jobId: String) = WhereClauseBuilder() + .appendKeyStringValue(TrackerJobObjectTableInfo.Columns.JOB_UID, jobId) + .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobQueryErrorCatcher.kt similarity index 75% rename from core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobInfo.kt rename to core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobQueryErrorCatcher.kt index 642696b5c4..22cc4ce936 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobQueryErrorCatcher.kt @@ -25,18 +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.tracker.importer.internal -import com.gabrielittner.auto.value.cursor.ColumnAdapter -import java.util.Date -import org.hisp.dhis.android.core.arch.db.adapters.custom.internal.DbDateColumnAdapter +import java.net.HttpURLConnection +import org.hisp.dhis.android.core.arch.api.executors.internal.APICallErrorCatcher +import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import retrofit2.Response + +class JobQueryErrorCatcher : APICallErrorCatcher { + + override fun mustBeStored(): Boolean = false -internal data class JobInfo( - val id: String, - val uid: String, - val level: String, - val category: String, - @ColumnAdapter(DbDateColumnAdapter::class) val time: Date, - val message: String, - val completed: Boolean -) + override fun catchError(response: Response<*>): D2ErrorCode? { + return if (response.code() == HttpURLConnection.HTTP_NOT_FOUND) { + D2ErrorCode.JOB_REPORT_NOT_AVAILABLE + } else { + null + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReport.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReport.kt index 373b20a61b..0a3406395f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReport.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReport.kt @@ -39,7 +39,7 @@ internal data class JobImportCount( internal data class JobValidationError( val uid: String, - val trackerType: String, + val trackerType: TrackerImporterObjectType, val errorCode: String, val message: String ) @@ -51,7 +51,7 @@ internal data class JobValidationReport( internal data class JobObjectReport( val errorReports: List, val index: Int, - val trackerType: String, + val trackerType: TrackerImporterObjectType, val uid: String ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEnrollmentHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEnrollmentHandler.kt new file mode 100644 index 0000000000..6ec77f754f --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEnrollmentHandler.kt @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +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.common.DataColumns +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore +import org.hisp.dhis.android.core.note.Note +import org.hisp.dhis.android.core.note.NoteTableInfo +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore + +@Reusable +internal class JobReportEnrollmentHandler @Inject internal constructor( + private val noteStore: IdentifiableObjectStore, + private val enrollmentStore: EnrollmentStore, + private val conflictStore: TrackerImportConflictStore, + private val conflictHelper: TrackerConflictHelper, + relationshipStore: RelationshipStore +) : JobReportTypeHandler(relationshipStore) { + + fun handleEnrollmentNotes(enrollmentUid: String, state: State) { + val newNoteState = if (state == State.SYNCED) State.SYNCED else State.TO_POST + val whereClause = WhereClauseBuilder() + .appendInKeyStringValues( + DataColumns.SYNC_STATE, State.uploadableStatesIncludingError().map { it.name } + ) + .appendKeyStringValue(NoteTableInfo.Columns.ENROLLMENT, enrollmentUid).build() + for (note in noteStore.selectWhere(whereClause)) { + noteStore.update(note.toBuilder().syncState(newNoteState).build()) + } + } + + override fun handleObject(uid: String, state: State): HandleAction { + conflictStore.deleteEnrollmentConflicts(uid) + val handleAction = enrollmentStore.setSyncStateOrDelete(uid, state) + + if (handleAction !== HandleAction.Delete) { + handleEnrollmentNotes(uid, state) + } + + return handleAction + } + + override fun storeConflict(errorReport: JobValidationError) { + val enrollment = enrollmentStore.selectByUid(errorReport.uid) + conflictStore.insert( + conflictHelper.getConflictBuilder(errorReport) + .tableReference(EnrollmentTableInfo.TABLE_INFO.name()) + .enrollment(errorReport.uid) + .trackedEntityInstance(enrollment!!.trackedEntityInstance()) + .build() + ) + } + + override fun getRelatedRelationships(uid: String): List { + return relationshipStore.getRelationshipsByItem(RelationshipHelper.enrollmentItem(uid)).mapNotNull { it.uid() } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEventHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEventHandler.kt new file mode 100644 index 0000000000..60fcbaa82c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportEventHandler.kt @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +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.common.DataColumns +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore +import org.hisp.dhis.android.core.event.EventTableInfo +import org.hisp.dhis.android.core.event.internal.EventStore +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore +import org.hisp.dhis.android.core.note.Note +import org.hisp.dhis.android.core.note.NoteTableInfo +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore + +@Reusable +internal class JobReportEventHandler @Inject internal constructor( + private val noteStore: IdentifiableObjectStore, + private val conflictStore: TrackerImportConflictStore, + private val eventStore: EventStore, + private val enrollmentStore: EnrollmentStore, + private val conflictHelper: TrackerConflictHelper, + relationshipStore: RelationshipStore +) : JobReportTypeHandler(relationshipStore) { + + fun handleEventNotes(eventUid: String, state: State) { + val newNoteState = if (state == State.SYNCED) State.SYNCED else State.TO_POST + val whereClause = WhereClauseBuilder() + .appendInKeyStringValues( + DataColumns.SYNC_STATE, State.uploadableStatesIncludingError().map { it.name } + ) + .appendKeyStringValue(NoteTableInfo.Columns.EVENT, eventUid).build() + for (note in noteStore.selectWhere(whereClause)) { + noteStore.update(note.toBuilder().syncState(newNoteState).build()) + } + } + + override fun handleObject(uid: String, state: State): HandleAction { + conflictStore.deleteEventConflicts(uid) + val handleAction = eventStore.setSyncStateOrDelete(uid, state) + + if (handleAction !== HandleAction.Delete) { + handleEventNotes(uid, state) + } + + return handleAction + } + + override fun storeConflict(errorReport: JobValidationError) { + val event = eventStore.selectByUid(errorReport.uid) + val trackedEntityInstanceUid = event?.enrollment()?.let { + enrollmentStore.selectByUid(it)?.trackedEntityInstance() + } + conflictStore.insert( + conflictHelper.getConflictBuilder(errorReport) + .tableReference(EventTableInfo.TABLE_INFO.name()) + .trackedEntityInstance(trackedEntityInstanceUid) + .enrollment(event?.enrollment()) + .event(errorReport.uid) + .build() + ) + } + + override fun getRelatedRelationships(uid: String): List { + return relationshipStore.getRelationshipsByItem(RelationshipHelper.eventItem(uid)).mapNotNull { it.uid() } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportHandler.kt new file mode 100644 index 0000000000..870fbea159 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportHandler.kt @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.internal.DataStatePropagator +import org.hisp.dhis.android.core.common.internal.DataStateUidHolder +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType.ENROLLMENT +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType.EVENT +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType.RELATIONSHIP +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType.TRACKED_ENTITY + +@Reusable +internal class JobReportHandler @Inject internal constructor( + private val eventHandler: JobReportEventHandler, + private val enrollmentHandler: JobReportEnrollmentHandler, + private val trackedEntityHandler: JobReportTrackedEntityHandler, + private val relationshipHandler: JobReportRelationshipHandler, + private val dataStatePropagator: DataStatePropagator +) { + + fun handle(o: JobReport, jobObjects: List) { + val jobObjectsMap = jobObjects.groupBy { jo -> Pair(jo.trackerType(), jo.objectUid()) } + val relatedUids = getRelatedUids(jobObjects) + + handleErrors(o, jobObjectsMap) + handleSuccesses(o, jobObjectsMap) + handleNotPresentObjects(o, jobObjects) + + dataStatePropagator.refreshAggregatedSyncStates(relatedUids) + } + + private fun handleErrors( + o: JobReport, + jobObjectsMap: Map, List> + ) { + o.validationReport.errorReports.forEach { errorReport -> + if (jobObjectsMap.containsKey(Pair(errorReport.trackerType, errorReport.uid))) { + getHandler(errorReport.trackerType).handleError(errorReport) + } + } + } + + private fun handleSuccesses( + o: JobReport, + jobObjectsMap: Map, List> + ) { + if (o.bundleReport != null) { + val typeMap = o.bundleReport.typeReportMap + applySuccess(typeMap.event, jobObjectsMap, eventHandler) + applySuccess(typeMap.enrollment, jobObjectsMap, enrollmentHandler) + applySuccess(typeMap.trackedEntity, jobObjectsMap, trackedEntityHandler) + applySuccess(typeMap.relationship, jobObjectsMap, relationshipHandler) + } + } + + private fun handleNotPresentObjects( + o: JobReport, + jobObjectsMap: List + ) { + val presentSuccesses = if (o.bundleReport == null) emptySet>() else { + val tm = o.bundleReport.typeReportMap + setOf(tm.event, tm.trackedEntity, tm.enrollment, tm.relationship).flatMap { + it.objectReports + }.map { Pair(it.trackerType, it.uid) } + } + + val presentErrors = o.validationReport.errorReports.map { + Pair(it.trackerType, it.uid) + }.toSet() + + val expectedObjects = jobObjectsMap.map { Pair(it.trackerType(), it.objectUid()) } + + val notPresentObjects = expectedObjects - presentSuccesses - presentErrors + + for (p in notPresentObjects) { + getHandler(p.first).handleObject(p.second, State.TO_UPDATE) + } + } + + private fun applySuccess( + typeReport: JobTypeReport, + jobObjects: Map, List>, + typeHandler: JobReportTypeHandler + ) { + typeReport.objectReports + .filter { jobObjects.containsKey(Pair(it.trackerType, it.uid)) } + .forEach { typeHandler.handleSuccess(it.uid) } + } + + private fun getRelatedUids(jobObjects: List): DataStateUidHolder { + return dataStatePropagator.getRelatedUids( + jobObjects.filter { it.trackerType() == TRACKED_ENTITY }.map { it.objectUid() }, + jobObjects.filter { it.trackerType() == ENROLLMENT }.map { it.objectUid() }, + jobObjects.filter { it.trackerType() == EVENT }.map { it.objectUid() }, + jobObjects.filter { it.trackerType() == RELATIONSHIP }.map { it.objectUid() } + ) + } + + private fun getHandler(type: TrackerImporterObjectType): JobReportTypeHandler { + return when (type) { + EVENT -> eventHandler + ENROLLMENT -> enrollmentHandler + TRACKED_ENTITY -> trackedEntityHandler + RELATIONSHIP -> relationshipHandler + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportRelationshipHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportRelationshipHandler.kt new file mode 100644 index 0000000000..b0af70a89a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportRelationshipHandler.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore + +@Reusable +internal class JobReportRelationshipHandler @Inject internal constructor( + relationshipStore: RelationshipStore +) : JobReportTypeHandler(relationshipStore) { + + override fun handleObject(uid: String, state: State): HandleAction { + return relationshipStore.setSyncStateOrDelete(uid, state) + } + + @Suppress("EmptyFunctionBlock") + override fun storeConflict(errorReport: JobValidationError) { + } + + override fun getRelatedRelationships(uid: String): List { + return emptyList() + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportTrackedEntityHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportTrackedEntityHandler.kt new file mode 100644 index 0000000000..307f0e5e1b --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportTrackedEntityHandler.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal + +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore +import org.hisp.dhis.android.core.relationship.RelationshipHelper +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceTableInfo +import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStore + +@Reusable +internal class JobReportTrackedEntityHandler @Inject internal constructor( + private val conflictStore: TrackerImportConflictStore, + private val trackedEntityStore: TrackedEntityInstanceStore, + private val conflictHelper: TrackerConflictHelper, + relationshipStore: RelationshipStore +) : JobReportTypeHandler(relationshipStore) { + + override fun handleObject(uid: String, state: State): HandleAction { + conflictStore.deleteTrackedEntityConflicts(uid) + return trackedEntityStore.setSyncStateOrDelete(uid, state) + } + + override fun storeConflict(errorReport: JobValidationError) { + conflictStore.insert( + conflictHelper.getConflictBuilder(errorReport) + .tableReference(TrackedEntityInstanceTableInfo.TABLE_INFO.name()) + .trackedEntityInstance(errorReport.uid).build() + ) + } + + override fun getRelatedRelationships(uid: String): List { + return relationshipStore.getRelationshipsByItem(RelationshipHelper.teiItem(uid)).mapNotNull { it.uid() } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportTypeHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportTypeHandler.kt new file mode 100644 index 0000000000..1b6c18aea0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/JobReportTypeHandler.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal + +import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore + +internal abstract class JobReportTypeHandler constructor( + protected val relationshipStore: RelationshipStore +) { + + fun handleSuccess(uid: String) { + val handleAction = handleObject(uid, State.SYNCED) + + if (handleAction === HandleAction.Delete) { + getRelatedRelationships(uid).forEach { relationshipStore.delete(it) } + } + } + + fun handleError(errorReport: JobValidationError) { + handleObject(errorReport.uid, State.ERROR) + storeConflict(errorReport) + } + + abstract fun handleObject(uid: String, state: State): HandleAction + abstract fun storeConflict(errorReport: JobValidationError) + abstract fun getRelatedRelationships(uid: String): List +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackedEntityInstanceTrackerImporterPostCall.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackedEntityInstanceTrackerImporterPostCall.kt index 05235df7ec..4ef21386b4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackedEntityInstanceTrackerImporterPostCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackedEntityInstanceTrackerImporterPostCall.kt @@ -30,12 +30,15 @@ package org.hisp.dhis.android.core.tracker.importer.internal import dagger.Reusable import io.reactivex.Observable import io.reactivex.Single +import java.util.* import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor import org.hisp.dhis.android.core.arch.call.D2Progress -import org.hisp.dhis.android.core.relationship.internal.RelationshipDeleteCall +import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.common.ObjectWithUidInterface +import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance -import org.hisp.dhis.android.core.trackedentity.internal.NewTrackerImporterTrackedEntityPayload +import org.hisp.dhis.android.core.trackedentity.internal.NewTrackerImporterPayload import org.hisp.dhis.android.core.trackedentity.internal.NewTrackerImporterTrackedEntityPostPayloadGenerator import org.hisp.dhis.android.core.trackedentity.internal.NewTrackerImporterTrackedEntityPostStateManager @@ -46,30 +49,79 @@ internal class TrackedEntityInstanceTrackerImporterPostCall @Inject internal con private val service: TrackerImporterService, private val apiCallExecutor: APICallExecutor, private val jobQueryCall: JobQueryCall, - private val relationshipDeleteCall: RelationshipDeleteCall + private val jobObjectHandler: Handler ) { fun uploadTrackedEntityInstances( filteredTrackedEntityInstances: List ): Observable { return Observable.defer { - val trackedEntitiesToPost = payloadGenerator.getTrackedEntities(filteredTrackedEntityInstances) + val payloadWrapper = payloadGenerator.getTrackedEntities(filteredTrackedEntityInstances) + + Observable.concat( + doPostCall(payloadWrapper.deleted, IMPORT_STRATEGY_DELETE), + doPostCall(payloadWrapper.updated, IMPORT_STRATEGY_CREATE_AND_UPDATE) + ) + } + } + + private fun doPostCall( + payload: NewTrackerImporterPayload, + importStrategy: String + ): Observable { + return if (payload.isEmpty()) { + Observable.empty() + } else { + stateManager.setStates(payload, State.UPLOADING) Single.fromCallable { - // TODO HANDLE DELETIONS - // TODO HANDLE RELATIONSHIPS - // TODO HANDLE DELETED RELATIONSHIPS - // relationshipDeleteCall.postDeletedRelationships(partition) - val trackedEntityInstancePayload = NewTrackerImporterTrackedEntityPayload(trackedEntitiesToPost) - val res = apiCallExecutor.executeObjectCall( - service.postTrackedEntityInstances(trackedEntityInstancePayload) - ) - val jobId = res.response().uid() - jobQueryCall.storeJob(jobId) - jobId + doPostCallInternal(payload, importStrategy) }.doOnError { - stateManager.restoreStates(trackedEntitiesToPost) + stateManager.restoreStates(payload) }.flatMapObservable { jobQueryCall.queryJob(it) } } } + + private fun doPostCallInternal( + payload: NewTrackerImporterPayload, + importStrategy: String + ): String { + val res = apiCallExecutor.executeObjectCall( + service.postTrackedEntityInstances(payload, ATOMIC_MODE_OBJECT, importStrategy) + ) + val jobId = res.response().uid() + jobObjectHandler.handleMany(generateJobObjects(payload, jobId)) + return jobId + } + + private fun generateJobObjects( + payload: NewTrackerImporterPayload, + jobUid: String + ): List { + val builder = TrackerJobObject + .builder() + .jobUid(jobUid) + .lastUpdated(Date()) + + val enrollments = payload.trackedEntities.flatMap { it.enrollments() ?: emptyList() } + payload.enrollments + val events = enrollments.flatMap { it.events() ?: emptyList() } + payload.events + + return generateTypeObjects(builder, TrackerImporterObjectType.TRACKED_ENTITY, payload.trackedEntities) + + generateTypeObjects(builder, TrackerImporterObjectType.ENROLLMENT, enrollments) + + generateTypeObjects(builder, TrackerImporterObjectType.EVENT, events) + + generateTypeObjects(builder, TrackerImporterObjectType.RELATIONSHIP, payload.relationships) + } + + private fun generateTypeObjects( + builder: TrackerJobObject.Builder, + objectType: TrackerImporterObjectType, + objects: List + ): List { + return objects.map { + builder + .trackerType(objectType) + .objectUid(it.uid()) + .build() + } + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerConflictHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerConflictHelper.kt new file mode 100644 index 0000000000..2aad915126 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerConflictHelper.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal + +import dagger.Reusable +import java.util.Date +import javax.inject.Inject +import org.hisp.dhis.android.core.imports.ImportStatus +import org.hisp.dhis.android.core.imports.TrackerImportConflict + +@Reusable +internal class TrackerConflictHelper @Inject internal constructor() { + + fun getConflictBuilder(errorReport: JobValidationError): TrackerImportConflict.Builder { + return TrackerImportConflict.builder() + .conflict(errorReport.message) + .displayDescription(errorReport.message) + .value(errorReport.uid) + .errorCode(errorReport.errorCode) + .status(ImportStatus.ERROR) + .created(Date()) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterObjectType.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterObjectType.kt new file mode 100644 index 0000000000..e60663b2cd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterObjectType.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal + +internal enum class TrackerImporterObjectType { + EVENT, + TRACKED_ENTITY, + ENROLLMENT, + RELATIONSHIP +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterPackageDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterPackageDIModule.java index 73db0bbc7a..66330da190 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterPackageDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterPackageDIModule.java @@ -29,8 +29,9 @@ package org.hisp.dhis.android.core.tracker.importer.internal; 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.common.StorableObjectWithUid; +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore; +import org.hisp.dhis.android.core.arch.handlers.internal.Handler; +import org.hisp.dhis.android.core.arch.handlers.internal.ObjectWithoutUidHandlerImpl; import dagger.Module; import dagger.Provides; @@ -48,7 +49,13 @@ TrackerImporterService service(Retrofit retrofit) { @Provides @Reusable - IdentifiableObjectStore store(DatabaseAdapter databaseAdapter) { - return TrackerJobStore.create(databaseAdapter); + ObjectWithoutUidStore store(DatabaseAdapter databaseAdapter) { + return TrackerJobObjectStore.create(databaseAdapter); + } + + @Provides + @Reusable + Handler handler(ObjectWithoutUidStore store) { + return new ObjectWithoutUidHandlerImpl<>(store); } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterService.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterService.kt index 3bbe60517e..5c076d5785 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerImporterService.kt @@ -27,30 +27,38 @@ */ package org.hisp.dhis.android.core.tracker.importer.internal -import org.hisp.dhis.android.core.event.internal.NewTrackerImporterEventPayload -import org.hisp.dhis.android.core.trackedentity.internal.NewTrackerImporterTrackedEntityPayload +import org.hisp.dhis.android.core.trackedentity.internal.NewTrackerImporterPayload import org.hisp.dhis.android.core.trackedentity.internal.ObjectWithUidWebResponse import retrofit2.Call -import retrofit2.http.Body -import retrofit2.http.GET -import retrofit2.http.POST -import retrofit2.http.Path +import retrofit2.http.* + +internal const val TRACKER_URL = "tracker" +internal const val JOBS_URL = "tracker/jobs/" + +internal const val ATOMIC_MODE = "atomicMode" +internal const val ATOMIC_MODE_OBJECT = "OBJECT" + +internal const val IMPORT_STRATEGY = "importStrategy" +internal const val IMPORT_STRATEGY_CREATE_AND_UPDATE = "CREATE_AND_UPDATE" +internal const val IMPORT_STRATEGY_DELETE = "DELETE" +internal const val JOB_ID = "jobId" internal interface TrackerImporterService { - @POST("tracker") + @POST(TRACKER_URL) fun postTrackedEntityInstances( - @Body payload: NewTrackerImporterTrackedEntityPayload + @Body payload: NewTrackerImporterPayload, + @Query(ATOMIC_MODE) atomicMode: String, + @Query(IMPORT_STRATEGY) importStrategy: String ): Call - @POST("tracker") + @POST(TRACKER_URL) fun postEvents( - @Body events: NewTrackerImporterEventPayload + @Body events: NewTrackerImporterPayload, + @Query(ATOMIC_MODE) atomicMode: String, + @Query(IMPORT_STRATEGY) importStrategy: String ): Call - @GET("tracker/jobs/{jobId}") - fun getJob(@Path("jobId") jobId: String): Call> - - @GET("tracker/jobs/{jobId}/report") - fun getJobReport(@Path("jobId") jobId: String): Call + @GET("$JOBS_URL{jobId}/report") + fun getJobReport(@Path(JOB_ID) jobId: String): Call } diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobModuleWiper.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobModuleWiper.kt index a013df38af..a8e324320a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobModuleWiper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobModuleWiper.kt @@ -40,7 +40,7 @@ class TrackerJobModuleWiper @Inject internal constructor(private val tableWiper: override fun wipeData() { tableWiper.wipeTables( - TrackerJobTableInfo.TABLE_INFO + TrackerJobObjectTableInfo.TABLE_INFO ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObject.java b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObject.java new file mode 100644 index 0000000000..b2f3f8ab2c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObject.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal; + +import android.database.Cursor; + +import androidx.annotation.NonNull; + +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.custom.internal.DbDateColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.enums.internal.TrackerImporterObjectTypeColumnAdapter; +import org.hisp.dhis.android.core.common.BaseObject; + +import java.util.Date; + +@AutoValue +@JsonDeserialize(builder = $$AutoValue_TrackerJobObject.Builder.class) +public abstract class TrackerJobObject extends BaseObject { + + @NonNull + @ColumnAdapter(TrackerImporterObjectTypeColumnAdapter.class) + public abstract TrackerImporterObjectType trackerType(); + + @NonNull + public abstract String objectUid(); + + @NonNull + public abstract String jobUid(); + + @NonNull + @ColumnAdapter(DbDateColumnAdapter.class) + public abstract Date lastUpdated(); + + + @NonNull + public static TrackerJobObject create(Cursor cursor) { + return AutoValue_TrackerJobObject.createFromCursor(cursor); + } + + + public static Builder builder() { + return new $$AutoValue_TrackerJobObject.Builder(); + } + + abstract Builder toBuilder(); + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public abstract static class Builder extends BaseObject.Builder { + public abstract Builder trackerType(TrackerImporterObjectType trackerType); + + public abstract Builder objectUid(String objectUid); + + public abstract Builder jobUid(String jobUid); + + public abstract Builder lastUpdated(Date lastUpdated); + + public abstract TrackerJobObject build(); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectStore.kt b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectStore.kt new file mode 100644 index 0000000000..080ce64bde --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectStore.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.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.binders.internal.WhereStatementBinder +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory.objectWithoutUidStore + +@Suppress("MagicNumber") +internal object TrackerJobObjectStore { + + private val BINDER = StatementBinder { o: TrackerJobObject, w: StatementWrapper -> + w.bind(1, o.trackerType()) + w.bind(2, o.objectUid()) + w.bind(3, o.jobUid()) + w.bind(4, o.lastUpdated()) + } + + private val WHERE_UPDATE_BINDER = WhereStatementBinder { o: TrackerJobObject, w: StatementWrapper -> + w.bind(5, o.trackerType()) + w.bind(6, o.objectUid()) + } + + private val DELETE_UPDATE_BINDER = WhereStatementBinder { o: TrackerJobObject, w: StatementWrapper -> + w.bind(1, o.trackerType()) + w.bind(2, o.objectUid()) + } + + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): ObjectWithoutUidStore { + return objectWithoutUidStore( + databaseAdapter, + TrackerJobObjectTableInfo.TABLE_INFO, + BINDER, + WHERE_UPDATE_BINDER, + DELETE_UPDATE_BINDER + ) { TrackerJobObject.create(it) } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectTableInfo.java similarity index 81% rename from core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobTableInfo.java rename to core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectTableInfo.java index 73710c7cdf..493f59ddb4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/tracker/importer/internal/TrackerJobObjectTableInfo.java @@ -32,18 +32,18 @@ import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper; import org.hisp.dhis.android.core.common.CoreColumns; -import static org.hisp.dhis.android.core.common.IdentifiableColumns.UID; +import static org.hisp.dhis.android.core.common.BaseIdentifiableObject.LAST_UPDATED; -public final class TrackerJobTableInfo { +public final class TrackerJobObjectTableInfo { - private TrackerJobTableInfo() { + private TrackerJobObjectTableInfo() { } public static final TableInfo TABLE_INFO = new TableInfo() { @Override public String name() { - return "TrackerJob"; + return "TrackerJobObject"; } @Override @@ -54,14 +54,20 @@ public CoreColumns columns() { public static class Columns extends CoreColumns { + public static final String TRACKER_TYPE = "trackerType"; + public static final String OBJECT_UID = "objectUid"; + public static final String JOB_UID = "jobUid"; + + @Override public String[] all() { - return CollectionsHelper.appendInNewArray(super.all(), UID); + return CollectionsHelper.appendInNewArray(super.all(), + TRACKER_TYPE, OBJECT_UID, JOB_UID, LAST_UPDATED); } @Override public String[] whereUpdate() { - return new String[]{UID}; + return new String[]{TRACKER_TYPE, OBJECT_UID}; } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/CategoryDimension.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/CategoryDimension.java new file mode 100644 index 0000000000..2cfb87c924 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/CategoryDimension.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004-2021, 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 org.hisp.dhis.android.core.common.ObjectWithUid; + +import java.util.List; + +@AutoValue +@JsonDeserialize(builder = AutoValue_CategoryDimension.Builder.class) +public abstract class CategoryDimension { + + @Nullable + @JsonProperty() + public abstract ObjectWithUid category(); + + @Nullable + @JsonProperty() + public abstract List categoryOptions(); + + public static Builder builder() { + return new AutoValue_CategoryDimension.Builder(); + } + + public abstract Builder toBuilder(); + + @AutoValue.Builder + @JsonPOJOBuilder(withPrefix = "") + public static abstract class Builder { + + public abstract Builder category(ObjectWithUid category); + + public abstract Builder categoryOptions(List categoryOptions); + + public abstract CategoryDimension build(); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..90b99c6869 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItem.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2004-2021, 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.ObjectWithUidColumnAdapter; +import org.hisp.dhis.android.core.common.CoreObject; +import org.hisp.dhis.android.core.common.ObjectWithUid; + +@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(ObjectWithUidColumnAdapter.class) + public abstract ObjectWithUid programDataElement(); + + @Nullable + @JsonProperty() + @ColumnAdapter(ObjectWithUidColumnAdapter.class) + public abstract ObjectWithUid programAttribute(); + + @Nullable + @JsonProperty() + @ColumnAdapter(ObjectWithUidColumnAdapter.class) + public abstract ObjectWithUid validationRule(); + + @Nullable + public String dataDimensionItem() { + ObjectWithUid item = dataDimensionItemObject(); + if (item == null) { + return null; + } else { + return item.uid(); + } + } + + @Nullable + private ObjectWithUid 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(ObjectWithUid programDataElement); + + public abstract Builder programAttribute(ObjectWithUid 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/DataDimensionItemTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemTableInfo.kt new file mode 100644 index 0000000000..7b734bce8f --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemTableInfo.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2004-2021, 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/DataDimensionItemType.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemType.kt new file mode 100644 index 0000000000..c883f4dd91 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemType.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2021, 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 DataDimensionItemType { + INDICATOR, + DATA_ELEMENT, + DATA_ELEMENT_OPERAND, + REPORTING_RATE, + PROGRAM_INDICATOR, + PROGRAM_DATA_ELEMENT, + PROGRAM_ATTRIBUTE, + VALIDATION_RULE +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DigitGroupSeparator.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/DigitGroupSeparator.kt new file mode 100644 index 0000000000..dcd0ab8dd8 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/DigitGroupSeparator.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2021, 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 DigitGroupSeparator { + COMMA, + SPACE, + NONE +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DisplayDensity.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/DisplayDensity.kt new file mode 100644 index 0000000000..de46799620 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/DisplayDensity.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2021, 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 DisplayDensity { + COMFORTABLE, + NORMAL, + COMPACT, + NONE +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/HideEmptyItemStrategy.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/HideEmptyItemStrategy.kt new file mode 100644 index 0000000000..c4bc87875d --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/HideEmptyItemStrategy.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004-2021, 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 HideEmptyItemStrategy { + NONE, + BEFORE_FIRST, + AFTER_LAST, + BEFORE_FIRST_AFTER_LAST, + ALL; + + open fun isHide(): Boolean { + return this == BEFORE_FIRST || this == AFTER_LAST || this == BEFORE_FIRST_AFTER_LAST || this == ALL + } +} 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 new file mode 100644 index 0000000000..269abd3d47 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/Visualization.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2004-2021, 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.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.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.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"}) +public abstract class Visualization extends BaseIdentifiableObject implements CoreObject { + + @Nullable + @JsonProperty() + public abstract String description(); + + @Nullable + @JsonProperty() + public abstract String displayDescription(); + + @Nullable + @JsonProperty() + public abstract String displayFormName(); + + @Nullable + @JsonProperty() + @ColumnAdapter(VisualizationTypeColumnAdapter.class) + public abstract VisualizationType type(); + + @Nullable + @JsonProperty() + public abstract Boolean hideTitle(); + + @Nullable + @JsonProperty() + public abstract Boolean hideSubtitle(); + + @Nullable + @JsonProperty() + public abstract Boolean hideEmptyColumns(); + + @Nullable + @JsonProperty() + public abstract Boolean hideEmptyRows(); + + @Nullable + @JsonProperty() + @ColumnAdapter(HideEmptyItemStrategyColumnAdapter.class) + public abstract HideEmptyItemStrategy hideEmptyRowItems(); + + @Nullable + @JsonProperty() + public abstract Boolean hideLegend(); + + @Nullable + @JsonProperty() + public abstract Boolean showHierarchy(); + + @Nullable + @JsonProperty() + public abstract Boolean rowTotals(); + + @Nullable + @JsonProperty() + public abstract Boolean rowSubTotals(); + + @Nullable + @JsonProperty() + public abstract Boolean colTotals(); + + @Nullable + @JsonProperty() + public abstract Boolean colSubTotals(); + + @Nullable + @JsonProperty() + public abstract Boolean showDimensionLabels(); + + @Nullable + @JsonProperty() + public abstract Boolean percentStackedValues(); + + @Nullable + @JsonProperty() + public abstract Boolean noSpaceBetweenColumns(); + + @Nullable + @JsonProperty() + public abstract Boolean skipRounding(); + + @Nullable + @JsonProperty() + @ColumnAdapter(DisplayDensityColumnAdapter.class) + public abstract DisplayDensity displayDensity(); + + @Nullable + @JsonProperty() + @ColumnAdapter(DigitGroupSeparatorColumnAdapter.class) + public abstract DigitGroupSeparator digitGroupSeparator(); + + @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(); + + @Nullable + @JsonProperty() + @ColumnAdapter(ObjectWithUidListColumnAdapter.class) + public abstract List organisationUnits(); + + @Nullable + @JsonProperty() + @ColumnAdapter(ObjectWithUidListColumnAdapter.class) + public abstract List periods(); + + public static Builder builder() { + return new $$AutoValue_Visualization.Builder(); + } + + public static Visualization create(Cursor cursor) { + return AutoValue_Visualization.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 description(String description); + + public abstract Builder displayDescription(String displayDescription); + + public abstract Builder displayFormName(String displayFormName); + + public abstract Builder type(VisualizationType type); + + public abstract Builder hideTitle(Boolean hideTitle); + + public abstract Builder hideSubtitle(Boolean hideSubtitle); + + public abstract Builder hideEmptyColumns(Boolean hideEmptyColumns); + + public abstract Builder hideEmptyRows(Boolean hideEmptyRows); + + public abstract Builder hideEmptyRowItems(HideEmptyItemStrategy hideEmptyRowItems); + + public abstract Builder hideLegend(Boolean hideLegend); + + public abstract Builder showHierarchy(Boolean showHierarchy); + + public abstract Builder rowTotals(Boolean rowTotals); + + public abstract Builder rowSubTotals(Boolean rowSubTotals); + + public abstract Builder colTotals(Boolean colTotals); + + public abstract Builder colSubTotals(Boolean colSubTotals); + + public abstract Builder showDimensionLabels(Boolean showDimensionLabels); + + public abstract Builder percentStackedValues(Boolean percentStackedValues); + + public abstract Builder noSpaceBetweenColumns(Boolean noSpaceBetweenColumns); + + public abstract Builder skipRounding(Boolean skipRounding); + + public abstract Builder displayDensity(DisplayDensity displayDensity); + + public abstract Builder digitGroupSeparator(DigitGroupSeparator digitGroupSeparator); + + public abstract Builder relativePeriods(Map 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 organisationUnits(List organisationUnits); + + public abstract Builder periods(List periods); + + public abstract Visualization build(); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCategoryDimensionLink.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCategoryDimensionLink.java new file mode 100644 index 0000000000..6761aad2dd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCategoryDimensionLink.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004-2021, 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.NonNull; + +import com.google.auto.value.AutoValue; + +import org.hisp.dhis.android.core.common.BaseObject; +import org.hisp.dhis.android.core.common.CoreObject; + +@AutoValue +public abstract class VisualizationCategoryDimensionLink implements CoreObject { + + @NonNull + public abstract String visualization(); + + @NonNull + public abstract String category(); + + @NonNull + public abstract String categoryOption(); + + public static Builder builder() { + return new $$AutoValue_VisualizationCategoryDimensionLink.Builder(); + } + + public abstract Builder toBuilder(); + + public static VisualizationCategoryDimensionLink create(Cursor cursor) { + return $AutoValue_VisualizationCategoryDimensionLink.createFromCursor(cursor); + } + + @AutoValue.Builder + public static abstract class Builder extends BaseObject.Builder { + public abstract Builder id(Long id); + + public abstract Builder visualization(String visualization); + + public abstract Builder category(String category); + + public abstract Builder categoryOption(String categoryOption); + + public abstract VisualizationCategoryDimensionLink 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/VisualizationCategoryDimensionLinkTableInfo.kt new file mode 100644 index 0000000000..e79c35e78e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCategoryDimensionLinkTableInfo.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2004-2021, 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 VisualizationCategoryDimensionLinkTableInfo { + + @JvmField + val TABLE_INFO: TableInfo = object : TableInfo() { + override fun name(): String { + return "VisualizationCategoryDimensionLink" + } + + override fun columns(): CoreColumns { + return Columns() + } + } + + class Columns : CoreColumns() { + override fun all(): Array { + return CollectionsHelper.appendInNewArray( + super.all(), + VISUALIZATION, + CATEGORY, + CATEGORY_OPTION + ) + } + + override fun whereUpdate(): Array { + return CollectionsHelper.appendInNewArray( + super.all(), + VISUALIZATION, + CATEGORY, + CATEGORY_OPTION + ) + } + + companion object { + const val VISUALIZATION = "visualization" + const val CATEGORY = "category" + const val CATEGORY_OPTION = "categoryOption" + } + } +} 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 new file mode 100644 index 0000000000..55f1f732e7 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationCollectionRepository.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2004-2021, 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.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.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; +import org.hisp.dhis.android.core.visualization.internal.VisualizationFields; + +import java.util.Map; + +import javax.inject.Inject; + +import dagger.Reusable; + +@Reusable +public final class VisualizationCollectionRepository + extends ReadOnlyIdentifiableCollectionRepositoryImpl { + + @Inject + VisualizationCollectionRepository(final IdentifiableObjectStore store, + final Map> childrenAppenders, + final RepositoryScope scope) { + super(store, childrenAppenders, scope, new FilterConnectorFactory<>(scope, + s -> new VisualizationCollectionRepository(store, childrenAppenders, s))); + } + + public StringFilterConnector byDescription() { + return cf.string(Columns.DESCRIPTION); + } + + public StringFilterConnector byDisplayDescription() { + return cf.string(Columns.DISPLAY_DESCRIPTION); + } + + public StringFilterConnector byDisplayFormName() { + return cf.string(Columns.DISPLAY_FORM_NAME); + } + + public EnumFilterConnector byType() { + return cf.enumC(Columns.TYPE); + } + + public BooleanFilterConnector byHideTitle() { + return cf.bool(Columns.HIDE_TITLE); + } + + public BooleanFilterConnector byHideSubtitle() { + return cf.bool(Columns.HIDE_SUBTITLE); + } + + public BooleanFilterConnector byHideEmptyColumns() { + return cf.bool(Columns.HIDE_EMPTY_COLUMNS); + } + + public BooleanFilterConnector byHideEmptyRows() { + return cf.bool(Columns.HIDE_EMPTY_ROWS); + } + + public EnumFilterConnector byHideEmptyRowItems() { + return cf.enumC(Columns.HIDE_EMPTY_ROW_ITEMS); + } + + public BooleanFilterConnector byHideLegend() { + return cf.bool(Columns.HIDE_LEGEND); + } + + public BooleanFilterConnector byShowHierarchy() { + return cf.bool(Columns.SHOW_HIERARCHY); + } + + public BooleanFilterConnector byRowTotals() { + return cf.bool(Columns.ROW_TOTALS); + } + + public BooleanFilterConnector byRowSubTotals() { + return cf.bool(Columns.ROW_SUB_TOTALS); + } + + public BooleanFilterConnector byColTotals() { + return cf.bool(Columns.COL_TOTALS); + } + + public BooleanFilterConnector byColSubTotals() { + return cf.bool(Columns.COL_SUB_TOTALS); + } + + public BooleanFilterConnector byShowDimensionLabels() { + return cf.bool(Columns.SHOW_DIMENSION_LABELS); + } + + public BooleanFilterConnector byPercentStackedValues() { + return cf.bool(Columns.PERCENT_STACKED_VALUES); + } + + public BooleanFilterConnector byNoSpaceBetweenColumns() { + return cf.bool(Columns.NO_SPACE_BETWEEN_COLUMNS); + } + + public BooleanFilterConnector bySkipRounding() { + return cf.bool(Columns.SKIP_ROUNDING); + } + + public EnumFilterConnector byDisplayDensity() { + return cf.enumC(Columns.DISPLAY_DENSITY); + } + + public EnumFilterConnector byDigitGroupSeparator() { + return cf.enumC(Columns.DIGIT_GROUP_SEPARATOR); + } + + public StringFilterConnector 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); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationModule.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationModule.kt new file mode 100644 index 0000000000..93c6f23ed2 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationModule.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004-2021, 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 + +interface VisualizationModule { + fun visualizations(): VisualizationCollectionRepository +} 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 new file mode 100644 index 0000000000..1a3286b251 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationTableInfo.kt @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2004-2021, 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 +import org.hisp.dhis.android.core.common.IdentifiableColumns + +object VisualizationTableInfo { + + @JvmField + val TABLE_INFO: TableInfo = object : TableInfo() { + override fun name(): String { + return "Visualization" + } + + override fun columns(): CoreColumns { + return Columns() + } + } + + class Columns : IdentifiableColumns() { + override fun all(): Array { + return CollectionsHelper.appendInNewArray( + super.all(), + DESCRIPTION, + DISPLAY_DESCRIPTION, + DISPLAY_FORM_NAME, + TYPE, + HIDE_TITLE, + HIDE_SUBTITLE, + HIDE_EMPTY_COLUMNS, + HIDE_EMPTY_ROWS, + HIDE_EMPTY_ROW_ITEMS, + HIDE_LEGEND, + SHOW_HIERARCHY, + ROW_TOTALS, + ROW_SUB_TOTALS, + COL_TOTALS, + COL_SUB_TOTALS, + SHOW_DIMENSION_LABELS, + PERCENT_STACKED_VALUES, + NO_SPACE_BETWEEN_COLUMNS, + 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 + ) + } + + companion object { + const val DESCRIPTION = "description" + const val DISPLAY_DESCRIPTION = "displayDescription" + const val DISPLAY_FORM_NAME = "displayFormName" + const val TYPE = "type" + const val HIDE_TITLE = "hideTitle" + const val HIDE_SUBTITLE = "hideSubtitle" + const val HIDE_EMPTY_COLUMNS = "hideEmptyColumns" + const val HIDE_EMPTY_ROWS = "hideEmptyRows" + const val HIDE_EMPTY_ROW_ITEMS = "hideEmptyRowItems" + const val HIDE_LEGEND = "hideLegend" + const val SHOW_HIERARCHY = "showHierarchy" + const val ROW_TOTALS = "rowTotals" + const val ROW_SUB_TOTALS = "rowSubTotals" + const val COL_TOTALS = "colTotals" + const val COL_SUB_TOTALS = "colSubTotals" + const val SHOW_DIMENSION_LABELS = "showDimensionLabels" + const val PERCENT_STACKED_VALUES = "percentStackedValues" + const val NO_SPACE_BETWEEN_COLUMNS = "noSpaceBetweenColumns" + 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" + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationType.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationType.kt new file mode 100644 index 0000000000..c91c0e9bc7 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/VisualizationType.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-2021, 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 VisualizationType { + COLUMN, + STACKED_COLUMN, + BAR, + STACKED_BAR, + LINE, + AREA, + PIE, + RADAR, + GAUGE, + YEAR_OVER_YEAR_LINE, + YEAR_OVER_YEAR_COLUMN, + SINGLE_VALUE, + PIVOT_TABLE, + SCATTER, + BUBBLE +} 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/visualization/internal/CategoryDimensionFields.kt new file mode 100644 index 0000000000..dfb6d5d0ef --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/CategoryDimensionFields.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-2021, 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), + fh.field(* 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.CategoryDimension + +internal object CategoryDimensionFields { + internal const val CATEGORY = "category" + internal const val CATEGORY_OPTIONS = "categoryOptions" + private val fh = FieldsHelper() + + val allFields: Fields = + Fields.builder() + .fields( + fh.nestedFieldWithUid(CATEGORY), + fh.nestedFieldWithUid(CATEGORY_OPTIONS) + ) + .build() +} 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/visualization/internal/DataDimensionItemEntityDIModule.kt new file mode 100644 index 0000000000..03409a119c --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemEntityDIModule.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2004-2021, 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 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.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 + +@Module +internal class DataDimensionItemEntityDIModule { + + @Provides + @Reusable + fun store(databaseAdapter: DatabaseAdapter): LinkStore { + return DataDimensionItemStore.create(databaseAdapter) + } + + @Provides + @Reusable + fun handler(store: LinkStore): + LinkHandler { + return LinkHandlerImpl(store) + } +} 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 new file mode 100644 index 0000000000..451837b279 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemFields.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2021, 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), + fh.field(* 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.DataDimensionItem +import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo +import org.hisp.dhis.android.core.visualization.DataDimensionItemType + +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.nestedFieldWithUid(DataDimensionItemTableInfo.Columns.PROGRAM_DATA_ELEMENT), + fh.nestedFieldWithUid(DataDimensionItemTableInfo.Columns.PROGRAM_ATTRIBUTE), + 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 new file mode 100644 index 0000000000..a33be162f3 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemStore.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004-2021, 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/VisualizationCall.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCall.kt new file mode 100644 index 0000000000..96ee2b5a8e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCall.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2021, 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 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.systeminfo.DHISVersion +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager +import org.hisp.dhis.android.core.visualization.Visualization + +@Reusable +internal class VisualizationCall @Inject constructor( + private val handler: Handler, + private val service: VisualizationService, + private val dhis2VersionManager: DHISVersionManager, + private val apiDownloader: APIDownloader +) : UidsCall { + + companion object { + private const val MAX_UID_LIST_SIZE = 90 + } + + override fun download(uids: Set): Single> { + return if (dhis2VersionManager.isGreaterOrEqualThan(DHISVersion.V2_34)) { + apiDownloader.downloadPartitioned( + uids, + MAX_UID_LIST_SIZE, + handler + ) { partitionUids: Set -> + service.getVisualizations( + VisualizationFields.allFields, + VisualizationFields.uid.`in`(partitionUids), + paging = false + ) + } + } else { + Single.just(listOf()) + } + } +} 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 new file mode 100644 index 0000000000..8d7d267494 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionChildrenAppender.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2004-2021, 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.map { ObjectWithUid.create(it.categoryOption()) }) + .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/VisualizationCategoryDimensionEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionEntityDIModule.kt new file mode 100644 index 0000000000..c96544b9f6 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionEntityDIModule.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2021, 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 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.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.common.ObjectWithUid +import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLink + +@Module +internal class VisualizationCategoryDimensionEntityDIModule { + + @Provides + @Reusable + fun store(databaseAdapter: DatabaseAdapter): LinkStore { + return VisualizationCategoryDimensionLinkStore.create(databaseAdapter) + } + + @Provides + @Reusable + fun handler(store: LinkStore): + LinkHandler { + return LinkHandlerImpl(store) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryStore.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStore.kt similarity index 63% rename from core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryStore.java rename to core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStore.kt index 52bf03af27..c558e1772f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionLinkStore.kt @@ -25,33 +25,30 @@ * (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 -package org.hisp.dhis.android.core.category.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.visualization.VisualizationCategoryDimensionLink +import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLinkTableInfo -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; -import org.hisp.dhis.android.core.category.Category; -import org.hisp.dhis.android.core.category.CategoryTableInfo; - -import androidx.annotation.NonNull; - -final class CategoryStore { - - private CategoryStore() {} - - private static StatementBinder BINDER = new IdentifiableStatementBinder() { - @Override - public void bindToStatement(@NonNull Category o, @NonNull StatementWrapper w) { - super.bindToStatement(o, w); - w.bind(7, o.dataDimensionType()); - } - }; +@Suppress("MagicNumber") +internal object VisualizationCategoryDimensionLinkStore { + private val BINDER = StatementBinder { o: VisualizationCategoryDimensionLink, w: StatementWrapper -> + w.bind(1, o.visualization()) + w.bind(2, o.category()) + w.bind(3, o.categoryOption()) + } - public static IdentifiableObjectStore create(DatabaseAdapter databaseAdapter) { - return StoreFactory.objectWithUidStore(databaseAdapter, CategoryTableInfo.TABLE_INFO, BINDER, Category::create); + fun create(databaseAdapter: DatabaseAdapter): LinkStore { + return StoreFactory.linkStore( + databaseAdapter, + VisualizationCategoryDimensionLinkTableInfo.TABLE_INFO, + VisualizationCategoryDimensionLinkTableInfo.Columns.VISUALIZATION, + BINDER + ) { VisualizationCategoryDimensionLink.create(it) } } -} \ No newline at end of file +} 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/VisualizationDataDimensionItemChildrenAppender.kt new file mode 100644 index 0000000000..98cb5adfcf --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationDataDimensionItemChildrenAppender.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004-2021, 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.visualization.DataDimensionItem +import org.hisp.dhis.android.core.visualization.DataDimensionItemTableInfo +import org.hisp.dhis.android.core.visualization.Visualization + +internal class VisualizationDataDimensionItemChildrenAppender +private constructor(private val childStore: LinkStore) : + ChildrenAppender() { + + override fun appendChildren(visualization: Visualization): Visualization { + val builder = visualization.toBuilder() + builder.dataDimensionItems(getChildren(visualization)) + return builder.build() + } + + private fun getChildren(o: Visualization): List { + val whereClause = WhereClauseBuilder() + .appendKeyStringValue(DataDimensionItemTableInfo.Columns.VISUALIZATION, o.uid()) + .build() + return this.childStore.selectWhere(whereClause) + } + + companion object { + fun create(databaseAdapter: DatabaseAdapter): ChildrenAppender { + return VisualizationDataDimensionItemChildrenAppender( + DataDimensionItemStore.create(databaseAdapter) + ) + } + } +} 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 new file mode 100644 index 0000000000..2677d3e564 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationEntityDIModule.kt @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004-2021, 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 dagger.Module +import dagger.Provides +import dagger.Reusable +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleanerImpl +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.di.internal.IdentifiableStoreProvider +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.visualization.Visualization +import org.hisp.dhis.android.core.visualization.VisualizationTableInfo + +@Module +internal class VisualizationEntityDIModule : IdentifiableStoreProvider { + + @Provides + @Reusable + override fun store(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + return VisualizationStore.create(databaseAdapter) + } + + @Provides + @Reusable + fun handler(impl: VisualizationHandler): Handler { + return impl + } + + @Provides + @Reusable + fun collectionCleaner(databaseAdapter: DatabaseAdapter): CollectionCleaner { + return CollectionCleanerImpl(VisualizationTableInfo.TABLE_INFO.name(), databaseAdapter) + } + + @Provides + @Reusable + fun childrenAppenders(databaseAdapter: DatabaseAdapter): Map> { + return mapOf( + Pair( + VisualizationFields.CATEGORY_DIMENSIONS, + VisualizationCategoryDimensionChildrenAppender.create(databaseAdapter) + ), + Pair( + VisualizationFields.DATA_DIMENSION_ITEMS, + VisualizationDataDimensionItemChildrenAppender.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 new file mode 100644 index 0000000000..a47d27036d --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationFields.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2004-2021, 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), + fh.field(* 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 VisualizationFields { + internal const val CATEGORY_DIMENSIONS = "categoryDimensions" + internal const val DATA_DIMENSION_ITEMS = "dataDimensionItems" + private val fh = FieldsHelper() + val uid = fh.uid() + + val allFields: Fields = + Fields.builder() + .fields(fh.getIdentifiableFields()) + .fields( + fh.field(VisualizationTableInfo.Columns.DESCRIPTION), + fh.field(VisualizationTableInfo.Columns.DISPLAY_DESCRIPTION), + fh.field(VisualizationTableInfo.Columns.DISPLAY_FORM_NAME), + fh.field(VisualizationTableInfo.Columns.TYPE), + fh.field(VisualizationTableInfo.Columns.HIDE_TITLE), + fh.field(VisualizationTableInfo.Columns.HIDE_SUBTITLE), + fh.field(VisualizationTableInfo.Columns.HIDE_EMPTY_COLUMNS), + fh.field(VisualizationTableInfo.Columns.HIDE_EMPTY_ROWS), + fh.field(VisualizationTableInfo.Columns.HIDE_EMPTY_ROW_ITEMS), + fh.field(VisualizationTableInfo.Columns.HIDE_LEGEND), + fh.field(VisualizationTableInfo.Columns.SHOW_HIERARCHY), + fh.field(VisualizationTableInfo.Columns.ROW_TOTALS), + fh.field(VisualizationTableInfo.Columns.ROW_SUB_TOTALS), + fh.field(VisualizationTableInfo.Columns.COL_TOTALS), + fh.field(VisualizationTableInfo.Columns.COL_SUB_TOTALS), + fh.field(VisualizationTableInfo.Columns.SHOW_DIMENSION_LABELS), + fh.field(VisualizationTableInfo.Columns.PERCENT_STACKED_VALUES), + fh.field(VisualizationTableInfo.Columns.NO_SPACE_BETWEEN_COLUMNS), + 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) + ) + .build() +} 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 new file mode 100644 index 0000000000..0ff6ce0ca7 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandler.kt @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2004-2021, 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 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.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 + +@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 +) : IdentifiableHandlerImpl(store) { + + override fun beforeCollectionHandled( + oCollection: Collection + ): Collection { + visualizationCategoryDimensionLinkStore.delete() + dataDimensionItemStore.delete() + return oCollection + } + + override fun afterObjectHandled(o: Visualization, action: HandleAction) { + o.categoryDimensions()?.forEach { categoryDimension: CategoryDimension -> + categoryDimension.category()?.let { + visualizationCategoryDimensionLinkHandler.handleMany( + it.uid(), categoryDimension.categoryOptions() + ) { categoryOption: ObjectWithUid -> + VisualizationCategoryDimensionLink.builder() + .visualization(o.uid()) + .category(categoryDimension.category()?.uid()) + .categoryOption(categoryOption.uid()) + .build() + } + } + } + + dataDimensionItemHandler.handleMany(o.uid(), o.dataDimensionItems()) { + it.toBuilder().visualization(o.uid()).build() + } + } + + override fun afterCollectionHandled(oCollection: Collection?) { + visualizationCollectionCleaner.deleteNotPresent(oCollection) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationInternalModule.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationInternalModule.kt new file mode 100644 index 0000000000..2394af507a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationInternalModule.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2021, 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 androidx.annotation.VisibleForTesting +import dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.call.factories.internal.UidsCall +import org.hisp.dhis.android.core.visualization.Visualization + +@Reusable +internal class VisualizationInternalModule @Inject internal constructor( + @field:VisibleForTesting val visualizationCall: UidsCall +) diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleDownloader.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleDownloader.kt new file mode 100644 index 0000000000..f28f4f2dfa --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleDownloader.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2021, 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 dagger.Reusable +import io.reactivex.Single +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.call.factories.internal.UidsCall +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.arch.modules.internal.TypedModuleDownloader +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization +import org.hisp.dhis.android.core.visualization.Visualization + +@Reusable +class VisualizationModuleDownloader @Inject internal constructor( + private val visualizationCall: UidsCall, + private val analyticsDhisVisualizationStore: ObjectWithoutUidStore +) : + TypedModuleDownloader> { + + override fun downloadMetadata(): Single> { + return visualizationCall.download(analyticsDhisVisualizationStore.selectAll().map { it.uid() }.toSet()) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleImpl.kt new file mode 100644 index 0000000000..07010c03f6 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleImpl.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004-2021, 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 dagger.Reusable +import javax.inject.Inject +import org.hisp.dhis.android.core.visualization.VisualizationCollectionRepository +import org.hisp.dhis.android.core.visualization.VisualizationModule + +@Reusable +internal class VisualizationModuleImpl @Inject internal constructor( + private val visualizations: VisualizationCollectionRepository +) : VisualizationModule { + + override fun visualizations(): VisualizationCollectionRepository = visualizations +} 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 new file mode 100644 index 0000000000..8312f83f22 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationModuleWiper.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004-2021, 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 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.VisualizationTableInfo +import org.hisp.dhis.android.core.wipe.internal.ModuleWiper +import org.hisp.dhis.android.core.wipe.internal.TableWiper + +@Reusable +class VisualizationModuleWiper @Inject internal constructor(private val tableWiper: TableWiper) : ModuleWiper { + override fun wipeMetadata() { + tableWiper.wipeTables( + VisualizationTableInfo.TABLE_INFO, + VisualizationCategoryDimensionLinkTableInfo.TABLE_INFO, + DataDimensionItemTableInfo.TABLE_INFO + ) + } + + override fun wipeData() { + // No data to wipe + } +} 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 new file mode 100644 index 0000000000..001857209a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationPackageDIModule.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2021, 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.call.factories.internal.UidsCall; +import org.hisp.dhis.android.core.visualization.Visualization; +import org.hisp.dhis.android.core.visualization.VisualizationModule; + +import dagger.Module; +import dagger.Provides; +import dagger.Reusable; +import retrofit2.Retrofit; + +@Module(includes = { + DataDimensionItemEntityDIModule.class, + VisualizationEntityDIModule.class, + VisualizationCategoryDimensionEntityDIModule.class +}) +public final class VisualizationPackageDIModule { + + @Provides + @Reusable + UidsCall visualizationCall(VisualizationCall impl) { + return impl; + } + + @Provides + @Reusable + VisualizationService visualizationService(Retrofit retrofit) { + return retrofit.create(VisualizationService.class); + } + + @Provides + @Reusable + VisualizationModule module(VisualizationModuleImpl impl) { + return impl; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..85b3f06fe8 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationService.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004-2021, 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 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 retrofit2.http.GET +import retrofit2.http.Query + +internal interface VisualizationService { + + @GET("visualizations") + fun getVisualizations( + @Query("fields") @Which fields: Fields, + @Query("filter") @Where uids: Filter, + @Query("paging") paging: Boolean + ): Single> +} 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 new file mode 100644 index 0000000000..87a4e63946 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationStore.kt @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2004-2021, 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 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 +import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory +import org.hisp.dhis.android.core.visualization.Visualization +import org.hisp.dhis.android.core.visualization.VisualizationTableInfo + +@Suppress("MagicNumber") +internal object VisualizationStore { + private val BINDER = object : IdentifiableStatementBinder() { + override fun bindToStatement(o: Visualization, w: StatementWrapper) { + super.bindToStatement(o, w) + w.bind(7, o.description()) + w.bind(8, o.displayDescription()) + w.bind(9, o.displayFormName()) + w.bind(10, o.type()) + w.bind(11, o.hideTitle()) + w.bind(12, o.hideSubtitle()) + w.bind(13, o.hideEmptyColumns()) + w.bind(14, o.hideEmptyRows()) + w.bind(15, o.hideEmptyRowItems()) + w.bind(16, o.hideLegend()) + w.bind(17, o.showHierarchy()) + w.bind(18, o.rowTotals()) + w.bind(19, o.rowSubTotals()) + w.bind(20, o.colTotals()) + w.bind(21, o.colSubTotals()) + w.bind(22, o.showDimensionLabels()) + w.bind(23, o.percentStackedValues()) + w.bind(24, o.noSpaceBetweenColumns()) + w.bind(25, o.skipRounding()) + w.bind(26, o.displayDensity()) + w.bind(27, o.digitGroupSeparator()) + w.bind(28, RelativePeriodsColumnAdapter.serialize(o.relativePeriods())) + w.bind(29, StringListColumnAdapter.serialize(o.filterDimensions())) + w.bind(30, StringListColumnAdapter.serialize(o.rowDimensions())) + w.bind(31, StringListColumnAdapter.serialize(o.columnDimensions())) + w.bind(32, IntegerListColumnAdapter.serialize(o.organisationUnitLevels())) + w.bind(33, o.userOrganisationUnit()) + w.bind(34, o.userOrganisationUnitChildren()) + w.bind(35, o.userOrganisationUnitGrandChildren()) + w.bind(36, ObjectWithUidListColumnAdapter.serialize(o.organisationUnits())) + w.bind(37, ObjectWithUidListColumnAdapter.serialize(o.periods())) + } + } + + @JvmStatic + fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + return StoreFactory.objectWithUidStore( + databaseAdapter, VisualizationTableInfo.TABLE_INFO, BINDER + ) { cursor: Cursor -> Visualization.create(cursor) } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.java b/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.java index 513d9eb9cf..cddb9356a8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.java +++ b/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.java @@ -34,6 +34,7 @@ import org.hisp.dhis.android.core.constant.internal.ConstantModuleWiper; import org.hisp.dhis.android.core.dataelement.internal.DataElementModuleWiper; import org.hisp.dhis.android.core.dataset.internal.DataSetModuleWiper; +import org.hisp.dhis.android.core.datastore.internal.LocalDataStoreModuleWiper; 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; @@ -55,6 +56,7 @@ import org.hisp.dhis.android.core.tracker.importer.internal.TrackerJobModuleWiper; import org.hisp.dhis.android.core.user.internal.UserModuleWiper; import org.hisp.dhis.android.core.validation.internal.ValidationModuleWiper; +import org.hisp.dhis.android.core.visualization.internal.VisualizationModuleWiper; import java.util.Arrays; import java.util.List; @@ -84,6 +86,7 @@ final class D2ModuleWipers { ImportModuleWiper importModule, IndicatorModuleWiper indicator, LegendSetModuleWiper legendSet, + LocalDataStoreModuleWiper localDataStore, MaintenanceModuleWiper maintenance, OptionModuleWiper option, @@ -99,7 +102,8 @@ final class D2ModuleWipers { UserModuleWiper user, TrackedEntityModuleWiper trackedEntity, AttributeModuleWiper attribute, - TrackerJobModuleWiper trackerJob) { + TrackerJobModuleWiper trackerJob, + VisualizationModuleWiper visualization) { this.wipers = Arrays.asList( category, @@ -116,6 +120,7 @@ final class D2ModuleWipers { importModule, indicator, legendSet, + localDataStore, maintenance, option, @@ -131,6 +136,7 @@ final class D2ModuleWipers { user, trackedEntity, attribute, - trackerJob); + trackerJob, + visualization); } } \ No newline at end of file diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datastore/KeyValuePairSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datastore/KeyValuePairSamples.kt new file mode 100644 index 0000000000..4263a1a2e9 --- /dev/null +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datastore/KeyValuePairSamples.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004-2021, 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.datastore + +import org.hisp.dhis.android.core.datastore.KeyValuePair + +object KeyValuePairSamples { + + val keyValuePairSample: KeyValuePair = + KeyValuePair.builder() + .id(1L) + .key("key1") + .value("value1") + .build() +} diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datavalue/DataValueConflictSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datavalue/DataValueConflictSamples.kt new file mode 100644 index 0000000000..d96550f294 --- /dev/null +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datavalue/DataValueConflictSamples.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue + +import java.text.ParseException +import java.util.Date +import org.hisp.dhis.android.core.common.BaseIdentifiableObject +import org.hisp.dhis.android.core.datavalue.DataValueConflict + +object DataValueConflictSamples { + + fun get(): DataValueConflict { + return DataValueConflict.builder() + .value("KKK") + .attributeOptionCombo("HllvX50cXC0") + .categoryOptionCombo("Prlt0C1RF0s") + .created(getDate("021-06-02T12:38:53.743")) + .dataElement("UOlfIjgN8X6") + .period("202101") + .orgUnit("DiszpKrYNg8").build() + } + + private fun getDate(dateStr: String): Date? { + return try { + BaseIdentifiableObject.DATE_FORMAT.parse(dateStr) + } catch (e: ParseException) { + e.printStackTrace() + null + } + } +} diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datavalue/DataValueSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datavalue/DataValueSamples.kt index 310bfb9c35..600ccdc3c0 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datavalue/DataValueSamples.kt +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/datavalue/DataValueSamples.kt @@ -45,7 +45,7 @@ object DataValueSamples { return DataValue.builder() .id(1L) - .state(State.TO_POST) + .syncState(State.TO_POST) .deleted(false) .dataElement(dataElement) .period(period) diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/enrollment/EnrollmentSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/enrollment/EnrollmentSamples.java index 0d69011015..5ec34bd0b4 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/enrollment/EnrollmentSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/enrollment/EnrollmentSamples.java @@ -65,7 +65,8 @@ public static Enrollment get(String uid, String organisationUnit, String program .geometry(Geometry.builder() .type(FeatureType.POINT) .coordinates("[21.21, 23.23]").build()) - .state(State.TO_POST) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) .deleted(false) .build(); } diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/fileresource/FileResourceSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/fileresource/FileResourceSamples.java index 248ff9bde5..65843bb120 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/fileresource/FileResourceSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/fileresource/FileResourceSamples.java @@ -43,7 +43,7 @@ public static FileResource get() { .uid("file_resource_uid") .created(getDate("2014-08-20T12:28:56.409")) .lastUpdated(getDate("2015-10-14T13:36:53.063")) - .state(State.TO_POST) + .syncState(State.TO_POST) .contentLength(1024L) .contentType("image/*") .path("path") diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/note/NoteSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/note/NoteSamples.java index a6e0b46bf2..2c8f6c317a 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/note/NoteSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/note/NoteSamples.java @@ -43,7 +43,7 @@ public static Note getNote() { .storedBy("user") .storedDate("2018-03-19T15:20:55.058") .uid("noteUId") - .state(State.TO_POST) + .syncState(State.TO_POST) .build(); } } \ No newline at end of file diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/relationship/RelationshipSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/relationship/RelationshipSamples.java index 1bff3fb60c..ef20c5368a 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/relationship/RelationshipSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/relationship/RelationshipSamples.java @@ -64,7 +64,7 @@ public class RelationshipSamples { .created(CREATED) .lastUpdated(LAST_UPDATED) .name(NAME) - .state(STATE) + .syncState(STATE) .deleted(DELETED); private static Relationship.Builder commonBuilder = Relationship @@ -72,7 +72,7 @@ public class RelationshipSamples { .created(CREATED) .lastUpdated(LAST_UPDATED) .name(NAME) - .state(STATE) + .syncState(STATE) .deleted(DELETED); public Relationship229Compatible get229Compatible() { @@ -102,11 +102,15 @@ protected Relationship get230() { } public static Relationship get230(String uid, String fromUid, String toUid) { + return get230(uid, RelationshipHelper.teiItem(fromUid), RelationshipHelper.teiItem(toUid)); + } + + public static Relationship get230(String uid, RelationshipItem from, RelationshipItem to) { return commonBuilder .uid(uid) .relationshipType(TYPE) - .from(RelationshipHelper.teiItem(fromUid)) - .to(RelationshipHelper.teiItem(toUid)) + .from(from) + .to(to) .build(); } diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/settings/AnalyticsSettingsSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/settings/AnalyticsSettingsSamples.kt index 4ff3793de1..3a0a077e41 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/settings/AnalyticsSettingsSamples.kt +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/settings/AnalyticsSettingsSamples.kt @@ -28,7 +28,20 @@ package org.hisp.dhis.android.core.data.settings import org.hisp.dhis.android.core.period.PeriodType -import org.hisp.dhis.android.core.settings.* +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationScope +import org.hisp.dhis.android.core.settings.AnalyticsTeiAttribute +import org.hisp.dhis.android.core.settings.AnalyticsTeiData +import org.hisp.dhis.android.core.settings.AnalyticsTeiDataElement +import org.hisp.dhis.android.core.settings.AnalyticsTeiIndicator +import org.hisp.dhis.android.core.settings.AnalyticsTeiSetting +import org.hisp.dhis.android.core.settings.AnalyticsTeiWHONutritionData +import org.hisp.dhis.android.core.settings.AnalyticsTeiWHONutritionGender +import org.hisp.dhis.android.core.settings.AnalyticsTeiWHONutritionGenderValues +import org.hisp.dhis.android.core.settings.AnalyticsTeiWHONutritionItem +import org.hisp.dhis.android.core.settings.ChartType +import org.hisp.dhis.android.core.settings.WHONutritionChartType +import org.hisp.dhis.android.core.settings.WHONutritionComponent object AnalyticsSettingsSamples { @@ -89,4 +102,14 @@ object AnalyticsSettingsSamples { .x(AnalyticsTeiWHONutritionItem.builder().build()) .y(AnalyticsTeiWHONutritionItem.builder().build()) .build() + + val analyticsDhisVisualization: AnalyticsDhisVisualization = + AnalyticsDhisVisualization.builder() + .id(1L) + .groupName("Otro grupo") + .groupUid("123456") + .scope(AnalyticsDhisVisualizationScope.HOME) + .timestamp("2021-07-01T02:55:16.8770") + .uid("PYBH8ZaAQnC") + .build() } diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/EventSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/EventSamples.java index 70559219a6..fb90f48ac6 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/EventSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/EventSamples.java @@ -64,7 +64,8 @@ public static Event get(String uid, String enrollment, String organisationUnit, .completedDate(getDate("2014-08-20T12:28:56.409")) .dueDate(getDate("2014-08-20T12:28:56.409")) .attributeOptionCombo(attributeOptionCombo) - .state(State.TO_POST) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) .deleted(false) .build(); } diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/TrackedEntityInstanceSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/TrackedEntityInstanceSamples.java index 972a66e56b..4e8e179a51 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/TrackedEntityInstanceSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/TrackedEntityInstanceSamples.java @@ -58,7 +58,8 @@ public static TrackedEntityInstance get(String uid, String organisationUnit, Str .type(FeatureType.POLYGON) .coordinates("[11.0, 11.0]") .build()) - .state(State.TO_POST) + .syncState(State.TO_POST) + .aggregatedSyncState(State.TO_POST) .deleted(false) .build(); } diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/tracker/importer/internal/TrackerJobObjectSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/tracker/importer/internal/TrackerJobObjectSamples.java new file mode 100644 index 0000000000..8f2f88f40f --- /dev/null +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/tracker/importer/internal/TrackerJobObjectSamples.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004-2021, 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.tracker.importer.internal; + +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerJobObject; + +import static org.hisp.dhis.android.core.data.utils.FillPropertiesTestUtils.parseDate; +import static org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType.EVENT; + +public class TrackerJobObjectSamples { + + public static TrackerJobObject get1() { + return TrackerJobObject.builder() + .id(1L) + .trackerType(EVENT) + .objectUid("oUid") + .jobUid("jUid") + .lastUpdated(parseDate("2017-11-29T11:27:46.935")) + .build(); + } +} 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 new file mode 100644 index 0000000000..2593f7f933 --- /dev/null +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/DataDimensionItemSamples.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-2021, 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.DataDimensionItemType + +object DataDimensionItemSamples { + + fun dataDimensionItem(): DataDimensionItem = + DataDimensionItem.builder() + .id(1L) + .visualization("visualization_uid") + .dataDimensionItemType(DataDimensionItemType.DATA_ELEMENT) + .dataElement(ObjectWithUid.create("data_element_uid")) + .build() +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStore.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationCategoryDimensionLinkSamples.kt similarity index 75% rename from core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStore.java rename to core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationCategoryDimensionLinkSamples.kt index 04f4fc9159..51d3161136 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentStore.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationCategoryDimensionLinkSamples.kt @@ -25,18 +25,17 @@ * (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 -package org.hisp.dhis.android.core.enrollment.internal; +import org.hisp.dhis.android.core.visualization.VisualizationCategoryDimensionLink -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDeletableDataObjectStore; -import org.hisp.dhis.android.core.enrollment.Enrollment; +object VisualizationCategoryDimensionLinkSamples { -import java.util.List; -import java.util.Map; - -public interface EnrollmentStore extends IdentifiableDeletableDataObjectStore { - - Map> queryEnrollmentsToPost(); - - List queryMissingRelationshipsUids(); -} \ No newline at end of file + fun visualizationCategoryDimensionLinkSamples(): VisualizationCategoryDimensionLink = + VisualizationCategoryDimensionLink.builder() + .id(1L) + .visualization("visualization_uid") + .category("category_uid") + .categoryOption("category_option_uid") + .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 new file mode 100644 index 0000000000..8b876a64db --- /dev/null +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/visualization/VisualizationSamples.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2004-2021, 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.common.RelativePeriod +import org.hisp.dhis.android.core.data.utils.FillPropertiesTestUtils +import org.hisp.dhis.android.core.visualization.* + +object VisualizationSamples { + + private const val DATE_STR = "2021-06-16T14:26:50.195" + private val DATE = FillPropertiesTestUtils.parseDate(DATE_STR) + + fun visualization(): Visualization = Visualization.builder() + .id(1L) + .uid("PYBH8ZaAQnC") + .name("Android SDK Visualization sample") + .displayName("Android SDK Visualization sample") + .created(DATE) + .lastUpdated(DATE) + .description("Sample visualization for the Android SDK") + .displayDescription("Sample visualization for the Android SDK") + .displayFormName("Android SDK Visualization sample") + .type(VisualizationType.PIVOT_TABLE) + .hideTitle(false) + .hideSubtitle(false) + .hideEmptyColumns(false) + .hideEmptyRows(false) + .hideEmptyRowItems(HideEmptyItemStrategy.NONE) + .hideLegend(false) + .showHierarchy(false) + .rowTotals(true) + .rowSubTotals(false) + .colTotals(false) + .colSubTotals(false) + .showDimensionLabels(false) + .percentStackedValues(false) + .noSpaceBetweenColumns(false) + .skipRounding(false) + .displayDensity(DisplayDensity.NORMAL) + .digitGroupSeparator(DigitGroupSeparator.COMMA) + .relativePeriods(hashMapOf(RelativePeriod.THIS_YEAR to false, RelativePeriod.LAST_12_MONTHS to true)) + .categoryDimensions( + listOf( + CategoryDimension.builder() + .category(ObjectWithUid.create("fMZEcRHuamy")) + .categoryOptions(listOf(ObjectWithUid.create("qkPbeWaFsnU"), ObjectWithUid.create("wbrDrL2aYEc"))) + .build() + ) + ) + .filterDimensions(listOf("ou")) + .rowDimensions(listOf("pe")) + .columnDimensions(listOf("dx", "fMZEcRHuamy", "fkAkrdC7eJF")) + .dataDimensionItems( + listOf( + DataDimensionItem.builder() + .dataDimensionItemType(DataDimensionItemType.INDICATOR) + .indicator(ObjectWithUid.create("Uvn6LCg7dVU")) + .build(), + DataDimensionItem.builder() + .dataDimensionItemType(DataDimensionItemType.DATA_ELEMENT) + .dataElement(ObjectWithUid.create("cYeuwXTCPkU")) + .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"))) + .build() +} diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/DataValueImportConflictSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/DataValueImportConflictSamples.kt new file mode 100644 index 0000000000..3b38427cdd --- /dev/null +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/DataValueImportConflictSamples.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal.conflicts + +import org.hisp.dhis.android.core.imports.internal.ImportConflict + +object DataValueImportConflictSamples { + + fun invalidDataElementType(): ImportConflict = ImportConflict.create( + "40L", + "Data value is not a positive integer, must match data element type: vANAXwtLwcT" + ) + + fun pastExpiryDate(): ImportConflict = ImportConflict.create( + "202104", + "Current date is past expiry days for period 202104 and data set: BfMAe6Itzgt" + ) + + fun periodAfterLatestOpenFuture(): ImportConflict = ImportConflict.create( + "202111", + "Period: 202111 is after latest open future period: 202105 for data element: UOlfIjgN8X6" + ) +} diff --git a/core/src/sharedTest/resources/datavalueset/data_value_set_warning.json b/core/src/sharedTest/resources/datavalueset/data_value_set_warning.json index dabfdd390a..29ceb60547 100644 --- a/core/src/sharedTest/resources/datavalueset/data_value_set_warning.json +++ b/core/src/sharedTest/resources/datavalueset/data_value_set_warning.json @@ -38,7 +38,7 @@ "conflicts": [ { "object": "40L", - "value": "El valor de dato no es un entero, must match data element type: vANAXwtLwcT" + "value": "Data value is not a positive integer, must match data element type: vANAXwtLwcT" } ], "dataSetComplete": "false" diff --git a/core/src/sharedTest/resources/imports/relationship_delete_web_response.json b/core/src/sharedTest/resources/imports/relationship_delete_web_response.json new file mode 100644 index 0000000000..1c48897133 --- /dev/null +++ b/core/src/sharedTest/resources/imports/relationship_delete_web_response.json @@ -0,0 +1,19 @@ +{ + "httpStatus": "OK", + "httpStatusCode": 200, + "status": "OK", + "message": "Import was successful.", + "response": { + "responseType": "ImportSummary", + "status": "SUCCESS", + "description": "Deletion of relationship wOhCbIpmVCp was successful", + "importCount": { + "imported": 0, + "updated": 0, + "ignored": 0, + "deleted": 1 + }, + "conflicts": [], + "reference": "wOhCbIpmVCp" + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/relationship_web_response.json b/core/src/sharedTest/resources/imports/relationship_web_response.json new file mode 100644 index 0000000000..8f52a68fb0 --- /dev/null +++ b/core/src/sharedTest/resources/imports/relationship_web_response.json @@ -0,0 +1,43 @@ +{ + "httpStatus": "OK", + "httpStatusCode": 200, + "status": "OK", + "message": "Import was successful.", + "response": { + "responseType": "ImportSummaries", + "status": "SUCCESS", + "imported": 2, + "updated": 0, + "deleted": 0, + "ignored": 0, + "importSummaries": [ + { + "responseType": "ImportSummary", + "status": "SUCCESS", + "importCount": { + "imported": 1, + "updated": 0, + "ignored": 0, + "deleted": 0 + }, + "conflicts": [], + "reference": "wOhCbIpmVCp", + "href": "https://play.dhis2.org/2.36.3/api/relationships/wOhCbIpmVCp" + }, + { + "responseType": "ImportSummary", + "status": "SUCCESS", + "importCount": { + "imported": 1, + "updated": 0, + "ignored": 0, + "deleted": 0 + }, + "conflicts": [], + "reference": "EdG77xZE29R", + "href": "https://play.dhis2.org/2.36.3/api/relationships/EdG77xZE29R" + } + ], + "total": 2 + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/relationship_web_response_with_errors.json b/core/src/sharedTest/resources/imports/relationship_web_response_with_errors.json new file mode 100644 index 0000000000..135065d109 --- /dev/null +++ b/core/src/sharedTest/resources/imports/relationship_web_response_with_errors.json @@ -0,0 +1,43 @@ +{ + "httpStatus": "Conflict", + "httpStatusCode": 409, + "status": "ERROR", + "message": "An error occurred, please check import summary.", + "response": { + "responseType": "ImportSummaries", + "status": "ERROR", + "imported": 0, + "updated": 0, + "deleted": 0, + "ignored": 2, + "importSummaries": [ + { + "responseType": "ImportSummary", + "status": "ERROR", + "description": "Relationship wOhCbIpmVCp already exists", + "importCount": { + "imported": 0, + "updated": 0, + "ignored": 1, + "deleted": 0 + }, + "conflicts": [], + "reference": "wOhCbIpmVCp" + }, + { + "responseType": "ImportSummary", + "status": "ERROR", + "description": "Relationship EdG77xZE29R already exists", + "importCount": { + "imported": 0, + "updated": 0, + "ignored": 1, + "deleted": 0 + }, + "conflicts": [], + "reference": "EdG77xZE29R" + } + ], + "total": 2 + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/tracker-importer/event-conflict-new.json b/core/src/sharedTest/resources/imports/tracker-importer/event-conflict-new.json new file mode 100644 index 0000000000..339e04b773 --- /dev/null +++ b/core/src/sharedTest/resources/imports/tracker-importer/event-conflict-new.json @@ -0,0 +1,20 @@ +{ + "status": "ERROR", + "validationReport": { + "errorReports": [ + { + "message": "Event OrganisationUnit: `OrganisationUnit (ABJTilhqOCW)`, and Program: `Program (q04UBOqq3rp)`, dont match.", + "errorCode": "E1029", + "trackerType": "EVENT", + "uid": "STPIK5YTi4f" + } + ] + }, + "stats": { + "created": 0, + "updated": 0, + "deleted": 0, + "ignored": 1, + "total": 1 + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/tracker-importer/event-conflict-old.json b/core/src/sharedTest/resources/imports/tracker-importer/event-conflict-old.json new file mode 100644 index 0000000000..9922148b08 --- /dev/null +++ b/core/src/sharedTest/resources/imports/tracker-importer/event-conflict-old.json @@ -0,0 +1,70 @@ +{ + "httpStatus": "Conflict", + "httpStatusCode": 409, + "status": "ERROR", + "message": "An error occurred, please check import summary.", + "response": { + "responseType": "ImportSummaries", + "status": "ERROR", + "imported": 0, + "updated": 0, + "deleted": 0, + "ignored": 2, + "importOptions": { + "idSchemes": {}, + "dryRun": false, + "async": false, + "importStrategy": "SYNC", + "mergeMode": "REPLACE", + "reportMode": "FULL", + "skipExistingCheck": false, + "sharing": false, + "skipNotifications": false, + "skipAudit": false, + "datasetAllowsPeriods": false, + "strictPeriods": false, + "strictDataElements": false, + "strictCategoryOptionCombos": false, + "strictAttributeOptionCombos": false, + "strictOrganisationUnits": false, + "requireCategoryOptionCombo": false, + "requireAttributeOptionCombo": false, + "skipPatternValidation": false, + "ignoreEmptyCollection": false, + "force": false, + "firstRowIsHeader": true, + "skipLastUpdated": false, + "mergeDataValues": false, + "skipCache": false + }, + "importSummaries": [ + { + "responseType": "ImportSummary", + "status": "ERROR", + "description": "Program is not assigned to this Organisation Unit: ABJTilhqOCW", + "importCount": { + "imported": 0, + "updated": 0, + "ignored": 1, + "deleted": 0 + }, + "conflicts": [], + "reference": "STPIK5YTi4f" + }, + { + "responseType": "ImportSummary", + "status": "ERROR", + "description": "Program is not assigned to this Organisation Unit: ABJTilhqOCW", + "importCount": { + "imported": 0, + "updated": 0, + "ignored": 1, + "deleted": 0 + }, + "conflicts": [], + "reference": "sjSyAZLTOfK" + } + ], + "total": 2 + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/tracker-importer/event-ok-new.json b/core/src/sharedTest/resources/imports/tracker-importer/event-ok-new.json new file mode 100644 index 0000000000..133f20fcfa --- /dev/null +++ b/core/src/sharedTest/resources/imports/tracker-importer/event-ok-new.json @@ -0,0 +1,76 @@ +{ + "status": "OK", + "validationReport": { + "errorReports": [] + }, + "stats": { + "created": 1, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 1 + }, + "bundleReport": { + "status": "OK", + "typeReportMap": { + "EVENT": { + "trackerType": "EVENT", + "stats": { + "created": 1, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 1 + }, + "objectReports": [ + { + "trackerType": "EVENT", + "uid": "Y8FRzr4y2jG", + "index": 0, + "errorReports": [] + } + ] + }, + "ENROLLMENT": { + "trackerType": "ENROLLMENT", + "stats": { + "created": 0, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 0 + }, + "objectReports": [] + }, + "RELATIONSHIP": { + "trackerType": "RELATIONSHIP", + "stats": { + "created": 0, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 0 + }, + "objectReports": [] + }, + "TRACKED_ENTITY": { + "trackerType": "TRACKED_ENTITY", + "stats": { + "created": 0, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 0 + }, + "objectReports": [] + } + }, + "stats": { + "created": 1, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 1 + } + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/tracker-importer/event-ok-old.json b/core/src/sharedTest/resources/imports/tracker-importer/event-ok-old.json new file mode 100644 index 0000000000..413f3284cf --- /dev/null +++ b/core/src/sharedTest/resources/imports/tracker-importer/event-ok-old.json @@ -0,0 +1,84 @@ +{ + "httpStatus": "OK", + "httpStatusCode": 200, + "status": "OK", + "message": "Import was successful.", + "response": { + "responseType": "ImportSummaries", + "status": "SUCCESS", + "imported": 1, + "updated": 0, + "deleted": 0, + "ignored": 0, + "importOptions": { + "idSchemes": {}, + "dryRun": false, + "async": false, + "importStrategy": "SYNC", + "mergeMode": "REPLACE", + "reportMode": "FULL", + "skipExistingCheck": false, + "sharing": false, + "skipNotifications": false, + "skipAudit": false, + "datasetAllowsPeriods": false, + "strictPeriods": false, + "strictDataElements": false, + "strictCategoryOptionCombos": false, + "strictAttributeOptionCombos": false, + "strictOrganisationUnits": false, + "requireCategoryOptionCombo": false, + "requireAttributeOptionCombo": false, + "skipPatternValidation": false, + "ignoreEmptyCollection": false, + "force": false, + "firstRowIsHeader": true, + "skipLastUpdated": false, + "mergeDataValues": false, + "skipCache": false + }, + "importSummaries": [ + { + "responseType": "ImportSummary", + "status": "SUCCESS", + "importOptions": { + "idSchemes": {}, + "dryRun": false, + "async": false, + "importStrategy": "SYNC", + "mergeMode": "REPLACE", + "reportMode": "FULL", + "skipExistingCheck": false, + "sharing": false, + "skipNotifications": false, + "skipAudit": false, + "datasetAllowsPeriods": false, + "strictPeriods": false, + "strictDataElements": false, + "strictCategoryOptionCombos": false, + "strictAttributeOptionCombos": false, + "strictOrganisationUnits": false, + "requireCategoryOptionCombo": false, + "requireAttributeOptionCombo": false, + "skipPatternValidation": false, + "ignoreEmptyCollection": false, + "force": false, + "firstRowIsHeader": true, + "skipLastUpdated": false, + "mergeDataValues": false, + "skipCache": false + }, + "importCount": { + "imported": 1, + "updated": 0, + "ignored": 0, + "deleted": 0 + }, + "conflicts": [], + "reference": "n62vX8eLI6R", + "href": "https://play.dhis2.org/android-dev/api/events/n62vX8eLI6R" + } + ], + "total": 1 + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/tracker-importer/tei-new.json b/core/src/sharedTest/resources/imports/tracker-importer/tei-new.json new file mode 100644 index 0000000000..0ccfb5a54d --- /dev/null +++ b/core/src/sharedTest/resources/imports/tracker-importer/tei-new.json @@ -0,0 +1,76 @@ +{ + "status": "OK", + "validationReport": { + "errorReports": [] + }, + "stats": { + "created": 1, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 1 + }, + "bundleReport": { + "status": "OK", + "typeReportMap": { + "EVENT": { + "trackerType": "EVENT", + "stats": { + "created": 0, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 0 + }, + "objectReports": [] + }, + "ENROLLMENT": { + "trackerType": "ENROLLMENT", + "stats": { + "created": 0, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 0 + }, + "objectReports": [] + }, + "RELATIONSHIP": { + "trackerType": "RELATIONSHIP", + "stats": { + "created": 0, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 0 + }, + "objectReports": [] + }, + "TRACKED_ENTITY": { + "trackerType": "TRACKED_ENTITY", + "stats": { + "created": 1, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 1 + }, + "objectReports": [ + { + "trackerType": "TRACKED_ENTITY", + "uid": "e34WObjXCwl", + "index": 0, + "errorReports": [] + } + ] + } + }, + "stats": { + "created": 1, + "updated": 0, + "deleted": 0, + "ignored": 0, + "total": 1 + } + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/tracker-importer/tei-old.json b/core/src/sharedTest/resources/imports/tracker-importer/tei-old.json new file mode 100644 index 0000000000..e9fce6315d --- /dev/null +++ b/core/src/sharedTest/resources/imports/tracker-importer/tei-old.json @@ -0,0 +1,90 @@ +{ + "httpStatus": "OK", + "httpStatusCode": 200, + "status": "OK", + "message": "Import was successful.", + "response": { + "responseType": "ImportSummaries", + "status": "SUCCESS", + "imported": 2, + "updated": 0, + "deleted": 0, + "ignored": 0, + "importOptions": { + "idSchemes": {}, + "dryRun": false, + "async": false, + "importStrategy": "SYNC", + "mergeMode": "REPLACE", + "reportMode": "FULL", + "skipExistingCheck": false, + "sharing": false, + "skipNotifications": false, + "skipAudit": false, + "datasetAllowsPeriods": false, + "strictPeriods": false, + "strictDataElements": false, + "strictCategoryOptionCombos": false, + "strictAttributeOptionCombos": false, + "strictOrganisationUnits": false, + "requireCategoryOptionCombo": false, + "requireAttributeOptionCombo": false, + "skipPatternValidation": false, + "ignoreEmptyCollection": false, + "force": false, + "firstRowIsHeader": true, + "skipLastUpdated": true, + "mergeDataValues": false, + "skipCache": false + }, + "importSummaries": [ + { + "responseType": "ImportSummary", + "status": "SUCCESS", + "importCount": { + "imported": 1, + "updated": 0, + "ignored": 0, + "deleted": 0 + }, + "conflicts": [], + "reference": "xiYp4jWt1Np", + "href": "https://play.dhis2.org/android-dev/api/trackedEntityInstances/xiYp4jWt1Np", + "enrollments": { + "responseType": "ImportSummaries", + "status": "SUCCESS", + "imported": 0, + "updated": 0, + "deleted": 0, + "ignored": 0, + "importSummaries": [], + "total": 0 + } + }, + { + "responseType": "ImportSummary", + "status": "SUCCESS", + "importCount": { + "imported": 1, + "updated": 0, + "ignored": 0, + "deleted": 0 + }, + "conflicts": [], + "reference": "FkLXx3rin3r", + "href": "https://play.dhis2.org/android-dev/api/trackedEntityInstances/FkLXx3rin3r", + "enrollments": { + "responseType": "ImportSummaries", + "status": "SUCCESS", + "imported": 0, + "updated": 0, + "deleted": 0, + "ignored": 0, + "importSummaries": [], + "total": 0 + } + } + ], + "total": 2 + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/imports/tracker-importer/tei-with-enrollment-error-new.json b/core/src/sharedTest/resources/imports/tracker-importer/tei-with-enrollment-error-new.json new file mode 100644 index 0000000000..41b34afea1 --- /dev/null +++ b/core/src/sharedTest/resources/imports/tracker-importer/tei-with-enrollment-error-new.json @@ -0,0 +1,20 @@ +{ + "status": "ERROR", + "validationReport": { + "errorReports": [ + { + "message": "Enrollment OrganisationUnit: `OrganisationUnit (ABJTilhqOCW)`, and Program: `Program (IpHINAT79UW)`, dont match.", + "errorCode": "E1041", + "trackerType": "ENROLLMENT", + "uid": "XJPm2lZzxtq" + } + ] + }, + "stats": { + "created": 0, + "updated": 0, + "deleted": 0, + "ignored": 2, + "total": 2 + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/relationship/relationship_types.json b/core/src/sharedTest/resources/relationship/relationship_types.json index 2c6e3c9f79..1be02ebd65 100644 --- a/core/src/sharedTest/resources/relationship/relationship_types.json +++ b/core/src/sharedTest/resources/relationship/relationship_types.json @@ -22,13 +22,13 @@ "write": false } }, - "toConstraint": { + "fromConstraint": { "relationshipEntity": "PROGRAM_INSTANCE", "program": { "id": "lxAQ7Zs9VYR" } }, - "fromConstraint": { + "toConstraint": { "relationshipEntity": "TRACKED_ENTITY_INSTANCE", "trackedEntityType": { "id": "nEenWmSyUEp" @@ -38,20 +38,72 @@ { "created": "2013-09-19T15:17:41.000", "lastUpdated": "2014-04-14T13:53:20.166", - "name": "Mother-Child", + "name": "Mother-Child_b-to-a_(Person-Person)", "id": "V2kkHafqs8G", - "displayName": "Mother-Child", - "bIsToA": "Child", - "aIsToB": "Mother" + "bidirectional": false, + "displayName": "Mother-Child_b-to-a_(Person-Person)", + "access": { + "read": true, + "update": true, + "externalize": false, + "delete": true, + "write": true, + "manage": true, + "data": { + "read": true, + "write": false + } + }, + "fromConstraint": { + "relationshipEntity": "TRACKED_ENTITY_INSTANCE", + "trackedEntityType": { + "id": "nEenWmSyUEp" + }, + "program": { + "id": "lxAQ7Zs9VYR" + } + }, + "toConstraint": { + "relationshipEntity": "PROGRAM_STAGE_INSTANCE", + "program": { + "id": "lxAQ7Zs9VYR" + } + } }, { - "created": "2014-04-14T13:53:38.659", - "lastUpdated": "2014-04-14T13:53:41.066", + "created": "2013-09-19T15:17:41.000", + "lastUpdated": "2014-04-14T13:53:20.166", "name": "Sibling", "id": "o51cUNONthg", + "bidirectional": false, "displayName": "Sibling", - "bIsToA": "Sibling", - "aIsToB": "Sibling" + "access": { + "read": true, + "update": true, + "externalize": false, + "delete": true, + "write": true, + "manage": true, + "data": { + "read": true, + "write": false + } + }, + "fromConstraint": { + "relationshipEntity": "PROGRAM_STAGE_INSTANCE", + "program": { + "id": "lxAQ7Zs9VYR" + } + }, + "toConstraint": { + "relationshipEntity": "TRACKED_ENTITY_INSTANCE", + "trackedEntityType": { + "id": "nEenWmSyUEp" + }, + "program": { + "id": "lxAQ7Zs9VYR" + } + } }, { "created": "2014-04-14T13:53:38.659", diff --git a/core/src/sharedTest/resources/settings/analytics_settings_v2.json b/core/src/sharedTest/resources/settings/analytics_settings_v2.json new file mode 100644 index 0000000000..7eac368845 --- /dev/null +++ b/core/src/sharedTest/resources/settings/analytics_settings_v2.json @@ -0,0 +1,124 @@ +{ + "tei": [ + { + "uid": "fqEx2avRp1L", + "data": { + "dataElements": [ + "dBwrot7S420.sWoqcoByYmD", + "dBwrot7S421.Ok9OQpitjQr" + ] + }, + "name": "Height evolution", + "type": "LINE", + "period": "Monthly", + "program": "IpHINAT79UW", + "programStage": "dBwrot7S420", + "shortName": "H. evolution" + }, + { + "uid": "XQUhloISaQJ", + "data": { + "indicators": [ + "dBwrot7S420.GSae40Fyppf" + ], + "attributes": [ + "cejWyOfXge6" + ] + }, + "name": "Weight gain", + "type": "BAR", + "period": "Weekly", + "program": "lxAQ7Zs9VYR", + "shortName": "W. gain" + }, + { + "WHONutrition": { + "chartType": "WFH", + "gender": { + "attribute": "cejWyOfXge6", + "values": { + "female": "female", + "male": "male" + } + }, + "x": { + "dataElements": [ + "dBwrot7S420.sWoqcoByYmD" + ] + }, + "y": { + "indicators": [ + "GSae40Fyppf" + ] + } + }, + "name": "Who chart", + "program": "IpHINAT79UW", + "programStage": "dBwrot7S420", + "shortName": "Who chart", + "type": "WHO_NUTRITION", + "uid": "yEdtdG7ql9K" + } + ], + "lastUpdated": "2021-06-02T04:30:16.877Z", + "dhisVisualizations": { + "home": [ + { + "id": "12345678910", + "name": "Ejemplo", + "visualizations": [ + { + "id": "FAFa11yFeFe", + "timestamp": "2021-07-01T03:01:16.8770" + }, + { + "id": "PYBH8ZaAQnC", + "timestamp": "2021-07-01T03:02:16.8770" + } + ] + }, + { + "id": "12345678911", + "name": "Otro ejemplo", + "visualizations": [ + { + "id": "PYBH8ZaAQnC", + "timestamp": "2021-07-01T03:04:16.8770" + } + ] + } + ], + "dataSet": { + "BfMAe6Itzgt": [ + { + "id": "0000000001", + "name": "default", + "visualizations": [ + { + "id": "FAFa11yFeFe", + "timestamp": "2021-07-01T02:55:16.8770" + } + ] + } + ] + }, + "program": { + "IpHINAT79UW": [ + { + "id": "0000000001", + "name": "default", + "visualizations": [ + { + "id": "PYBH8ZaAQnC", + "timestamp": "2021-07-01T02:55:16.8770" + }, + { + "id": "PYBH8ZaAQnC", + "timestamp": "2021-07-01T02:55:16.8770" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/visualization/visualization.json b/core/src/sharedTest/resources/visualization/visualization.json new file mode 100644 index 0000000000..533490fc2e --- /dev/null +++ b/core/src/sharedTest/resources/visualization/visualization.json @@ -0,0 +1,321 @@ +{ + "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", + "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, + "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, + "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": [ + { + "category": { + "id": "fMZEcRHuamy" + }, + "categoryOptions": [ + { + "id": "qkPbeWaFsnU" + }, + { + "id": "wbrDrL2aYEc" + } + ] + }, + { + "category": { + "id": "fkAkrdC7eJF" + }, + "categoryOptions": [ + { + "id": "FbLZS3ueWbQ" + }, + { + "id": "rEq3Hkd3XXH" + }, + { + "id": "ZZxYuoTCcDd" + }, + { + "id": "dUm5jaCTPBb" + } + ] + } + ], + "filterDimensions": [ + "ou" + ], + "rowDimensions": [ + "pe" + ], + "columnDimensions": [ + "dx", + "fMZEcRHuamy", + "fkAkrdC7eJF" + ], + "dataDimensionItems": [ + { + "dataDimensionItemType": "INDICATOR", + "indicator": { + "id": "Uvn6LCg7dVU" + } + }, + { + "dataDimensionItemType": "DATA_ELEMENT", + "dataElement": { + "id": "cYeuwXTCPkU" + } + }, + { + "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 + }, + "categoryOptionCombo": { + "id": "pq2XI5kz2BY" + }, + "dataElement": { + "id": "Jtf34kNZhzP" + }, + "favorites": [], + "translations": [], + "userGroupAccesses": [], + "attributeValues": [], + "userAccesses": [], + "legendSets": [] + } + }, + { + "dataDimensionItemType": "PROGRAM_INDICATOR", + "programIndicator": { + "id": "p2Zxg0wcPQ3" + } + } + ], + "organisationUnitLevels": [ + 3 + ], + "userOrganisationUnit": false, + "userOrganisationUnitChildren": false, + "userOrganisationUnitGrandChildren": false, + "organisationUnits": [ + { + "id": "YuQRtpLP10I" + }, + { + "id": "vWbkYPRmKyS" + } + ], + "periods": [ + { + "id": "202102" + }, + { + "id": "202103" + }, + { + "id": "2021S2" + } + ], + + + + "columns": [ + { + "id": "dx" + }, + { + "id": "fMZEcRHuamy" + }, + { + "id": "fkAkrdC7eJF" + } + ], + "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": "DEFAULT", + + + + + "legend": { + "hidden": false + }, + "legendDisplayStrategy": "FIXED", + "legendDisplayStyle": "FILL", + "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 new file mode 100644 index 0000000000..7bec14d6e4 --- /dev/null +++ b/core/src/sharedTest/resources/visualization/visualization_simplified.json @@ -0,0 +1,97 @@ +{ + "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", + "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, + "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 new file mode 100644 index 0000000000..355ac94450 --- /dev/null +++ b/core/src/sharedTest/resources/visualization/visualizations.json @@ -0,0 +1,174 @@ +{ + "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", + "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", + "relativePeriods": { + "thisYear": false, + "last12Months": true + }, + "categoryDimensions": [ + { + "category": { + "id": "KfdsGBcoiCa" + }, + "categoryOptions": [ + { + "id": "TNYQzTHdoxL" + }, + { + "id": "TXGfLxZlInA" + }, + { + "id": "uZUnebiT5DI" + } + ] + } + ], + "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" + } + ] + }, + { + "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", + "relativePeriods": { + }, + "categoryDimensions": [ + ], + "filterDimensions": [ + "ou" + ], + "rowDimensions": [ + "pe" + ], + "columnDimensions": [ + "dx" + ], + "dataDimensionItems": [ + { + "dataDimensionItemType": "DATA_ELEMENT", + "dataElement": { + "id": "g9eOBujte1U" + } + } + ], + "organisationUnitLevels": [ + ], + "userOrganisationUnit": false, + "userOrganisationUnitChildren": false, + "userOrganisationUnitGrandChildren": false, + "organisationUnits": [ + { + "id": "DiszpKrYNg8" + } + ], + "periods": [ + { + "id": "2016" + }, + { + "id": "2017" + }, + { + "id": "2018" + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceDimensionHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceDimensionHelperShould.kt new file mode 100644 index 0000000000..097c61374b --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceDimensionHelperShould.kt @@ -0,0 +1,198 @@ + +/* + * Copyright (c) 2004-2021, 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 + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.analytics.aggregated.AbsoluteDimensionItem +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.internal.AnalyticsServiceHelperSamples as s +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.internal.ParentPeriodGenerator +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class AnalyticsServiceDimensionHelperShould { + + private val periodGenerator: ParentPeriodGenerator = mock() + + private val organisationUnitHelper: AnalyticsOrganisationUnitHelper = mock() + + private val helper = AnalyticsServiceDimensionHelper(periodGenerator, organisationUnitHelper) + + @Test + fun `Should extract unique dimension values`() { + val params = AnalyticsRepositoryParams( + dimensions = listOf( + s.dataElementItem1, + s.dataElementItem2, + s.periodAbsolute1, + s.periodAbsolute2, + s.programIndicatorItem, + s.indicatorItem, + s.periodLast3Days, + s.categoryItem1_1, + s.orgunitAbsolute, + s.categoryItem1_2, + s.categoryItem2_1 + ), + filters = listOf() + ) + + val dimensionSet = helper.getDimensions(params) + + assertThat(dimensionSet).containsExactly( + Dimension.Data, + Dimension.Period, + Dimension.OrganisationUnit, + Dimension.Category(s.categoryItem1_1.uid), + Dimension.Category(s.categoryItem2_1.uid) + ) + } + + @Test + fun `Should generate cartesian product based on given dimensions using absolute arguments`() { + val params = AnalyticsRepositoryParams( + dimensions = listOf( + s.dataElementItem1, + s.periodAbsolute1, + s.indicatorItem, + s.periodAbsolute2, + s.orgunitAbsolute, + s.categoryItem1_1, + s.categoryItem1_2 + ), + filters = listOf() + ) + val dimensions = listOf( + Dimension.Period, Dimension.Data, Dimension.OrganisationUnit, + Dimension.Category(s.categoryItem1_1.uid) + ) + + val items = helper.getEvaluationItems(params, dimensions) + + assertThat(items).containsExactly( + item(s.periodAbsolute1, s.dataElementItem1, s.orgunitAbsolute, s.categoryItem1_1), + item(s.periodAbsolute1, s.dataElementItem1, s.orgunitAbsolute, s.categoryItem1_2), + item(s.periodAbsolute1, s.indicatorItem, s.orgunitAbsolute, s.categoryItem1_1), + item(s.periodAbsolute1, s.indicatorItem, s.orgunitAbsolute, s.categoryItem1_2), + item(s.periodAbsolute2, s.dataElementItem1, s.orgunitAbsolute, s.categoryItem1_1), + item(s.periodAbsolute2, s.dataElementItem1, s.orgunitAbsolute, s.categoryItem1_2), + item(s.periodAbsolute2, s.indicatorItem, s.orgunitAbsolute, s.categoryItem1_1), + item(s.periodAbsolute2, s.indicatorItem, s.orgunitAbsolute, s.categoryItem1_2) + ) + } + + @Test + fun `Should ignore missing dimensions`() { + val params = AnalyticsRepositoryParams( + dimensions = listOf( + s.dataElementItem1, + s.periodAbsolute1, + s.orgunitAbsolute, + s.categoryItem1_1 + ), + filters = listOf() + ) + val dimensions = listOf(Dimension.Period, Dimension.Data, Dimension.Category(s.categoryItem1_1.uid)) + + val items = helper.getEvaluationItems(params, dimensions) + + assertThat(items).containsExactly( + item(s.periodAbsolute1, s.dataElementItem1, s.categoryItem1_1) + ) + } + + @Test + fun `Should evaluate relative periods`() { + whenever(periodGenerator.generateRelativePeriods(s.periodLast3Days.relative)) + .thenReturn( + listOf( + Period.builder().periodId("20210701").build(), + Period.builder().periodId("20210702").build(), + Period.builder().periodId("20210703").build() + ) + ) + + val params = AnalyticsRepositoryParams( + dimensions = listOf( + s.dataElementItem1, + s.periodLast3Days, + s.orgunitAbsolute + ), + filters = listOf() + ) + val dimensions = listOf(Dimension.Data, Dimension.Period, Dimension.OrganisationUnit) + + val items = helper.getEvaluationItems(params, dimensions) + + assertThat(items).containsExactly( + item(s.dataElementItem1, DimensionItem.PeriodItem.Absolute("20210701"), s.orgunitAbsolute), + item(s.dataElementItem1, DimensionItem.PeriodItem.Absolute("20210702"), s.orgunitAbsolute), + item(s.dataElementItem1, DimensionItem.PeriodItem.Absolute("20210703"), s.orgunitAbsolute) + ) + } + + @Test + fun `Should evaluate orgunits by level`() { + whenever(organisationUnitHelper.getOrganisationUnitUidsByLevel(any())) + .thenReturn(listOf("orgunit1", "orgunit2", "orgunit3")) + + val params = AnalyticsRepositoryParams( + dimensions = listOf( + s.dataElementItem1, + s.periodAbsolute1, + s.orgunitLevel3 + ), + filters = listOf() + ) + val dimensions = listOf(Dimension.Data, Dimension.Period, Dimension.OrganisationUnit) + + val items = helper.getEvaluationItems(params, dimensions) + + assertThat(items).containsExactly( + item(s.dataElementItem1, s.periodAbsolute1, DimensionItem.OrganisationUnitItem.Absolute("orgunit1")), + item(s.dataElementItem1, s.periodAbsolute1, DimensionItem.OrganisationUnitItem.Absolute("orgunit2")), + item(s.dataElementItem1, s.periodAbsolute1, DimensionItem.OrganisationUnitItem.Absolute("orgunit3")) + ) + } + + private fun item(vararg items: AbsoluteDimensionItem): AnalyticsServiceEvaluationItem { + return AnalyticsServiceEvaluationItem( + dimensionItems = items.toList(), + filters = listOf() + ) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceHelperSamples.kt b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceHelperSamples.kt new file mode 100644 index 0000000000..5f0564a05d --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceHelperSamples.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2021, 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 + +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples +import org.hisp.dhis.android.core.common.RelativePeriod + +object AnalyticsServiceHelperSamples { + + val dataElementItem1 = DimensionItem.DataItem.DataElementItem(AggregatedSamples.dataElement1.uid()) + val dataElementItem2 = DimensionItem.DataItem.DataElementItem(AggregatedSamples.dataElement2.uid()) + val programIndicatorItem = DimensionItem.DataItem.ProgramIndicatorItem(AggregatedSamples.programIndicator1.uid()) + val indicatorItem = DimensionItem.DataItem.IndicatorItem(AggregatedSamples.indicator1.uid()) + + val periodAbsolute1 = DimensionItem.PeriodItem.Absolute(AggregatedSamples.period1.periodId()!!) + val periodAbsolute2 = DimensionItem.PeriodItem.Absolute(AggregatedSamples.period2.periodId()!!) + val periodLast3Days = DimensionItem.PeriodItem.Relative(RelativePeriod.LAST_3_DAYS) + + val orgunitAbsolute = DimensionItem.OrganisationUnitItem.Absolute(AggregatedSamples.orgunit1.uid()) + val orgunitLevel3 = DimensionItem.OrganisationUnitItem.Level(3) + + val categoryItem1_1 = DimensionItem.CategoryItem(AggregatedSamples.cc1.uid(), AggregatedSamples.co11.uid()) + val categoryItem1_2 = DimensionItem.CategoryItem(AggregatedSamples.cc1.uid(), AggregatedSamples.co12.uid()) + val categoryItem2_1 = DimensionItem.CategoryItem(AggregatedSamples.cc2.uid(), AggregatedSamples.co21.uid()) +} 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 new file mode 100644 index 0000000000..80f7e01dd4 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsServiceMetadataHelperShould.kt @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2004-2021, 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 + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceHelperSamples as s +import org.hisp.dhis.android.core.analytics.aggregated.mock.AggregatedSamples +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.category.Category +import org.hisp.dhis.android.core.category.CategoryOption +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.dataelement.DataElementOperand +import org.hisp.dhis.android.core.indicator.Indicator +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitLevel +import org.hisp.dhis.android.core.period.internal.ParentPeriodGenerator +import org.hisp.dhis.android.core.period.internal.PeriodHelper +import org.hisp.dhis.android.core.program.ProgramIndicator +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class AnalyticsServiceMetadataHelperShould { + + private val categoryStore: IdentifiableObjectStore = mock() + private val categoryOptionStore: IdentifiableObjectStore = mock() + private val dataElementStore: IdentifiableObjectStore = mock() + private val dataElementOperandStore: IdentifiableObjectStore = mock() + private val indicatorStore: IdentifiableObjectStore = mock() + private val organisationUnitStore: IdentifiableObjectStore = mock() + private val organisationUnitGroupStore: IdentifiableObjectStore = mock() + private val organisationUnitLevelStore: IdentifiableObjectStore = mock() + private val programIndicatorStore: IdentifiableObjectStore = mock() + private val analyticsOrganisationUnitHelper: AnalyticsOrganisationUnitHelper = mock() + private val parentPeriodGenerator: ParentPeriodGenerator = mock() + private val periodHelper: PeriodHelper = mock() + + private val helper = AnalyticsServiceMetadataHelper( + categoryStore, + categoryOptionStore, + dataElementStore, + dataElementOperandStore, + indicatorStore, + organisationUnitStore, + organisationUnitGroupStore, + organisationUnitLevelStore, + programIndicatorStore, + analyticsOrganisationUnitHelper, + parentPeriodGenerator, + periodHelper + ) + + @Test + fun `Should extract category and category option metadata items`() { + whenever(categoryStore.selectByUid(s.categoryItem1_1.uid)).thenReturn(AggregatedSamples.cc1) + whenever(categoryOptionStore.selectByUid(s.categoryItem1_1.categoryOption)).thenReturn(AggregatedSamples.co11) + whenever(categoryOptionStore.selectByUid(s.categoryItem1_2.categoryOption)).thenReturn(AggregatedSamples.co12) + + val evaluationItems = listOf( + AnalyticsServiceEvaluationItem( + dimensionItems = listOf(s.categoryItem1_1, s.categoryItem1_2), + filters = listOf() + ) + ) + + val metadataMap = helper.getMetadata(evaluationItems) + + assertThat(metadataMap.keys).containsExactly( + s.categoryItem1_1.uid, + s.categoryItem1_1.categoryOption, + s.categoryItem1_2.categoryOption + ) + } +} 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 new file mode 100644 index 0000000000..c9c53f7bcf --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/analytics/aggregated/internal/AnalyticsVisualizationsServiceDimensionHelperShould.kt @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2004-2021, 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 + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import junit.framework.Assert.fail +import org.hisp.dhis.android.core.analytics.aggregated.DimensionItem +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.category.Category +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.visualization.CategoryDimension +import org.hisp.dhis.android.core.visualization.DataDimensionItem +import org.hisp.dhis.android.core.visualization.DataDimensionItemType +import org.hisp.dhis.android.core.visualization.Visualization +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class AnalyticsVisualizationsServiceDimensionHelperShould { + + private val categoryStore: IdentifiableObjectStore = mock() + private val category: Category = mock() + private val visualization: Visualization = mock() + + private val uid1 = "GMpWZUg2QUf" + private val uid2 = "AC6H8zCDb3B" + private val uid3 = "eEIN8RQWxWp" + + private val helper = AnalyticsVisualizationsServiceDimensionHelper(categoryStore) + + @Test + fun `Should parse dataElement dimension items`() { + val dataDimensionItems = listOf( + DataDimensionItem.builder() + .dataElement(ObjectWithUid.create(uid1)) + .dataDimensionItemType(DataDimensionItemType.DATA_ELEMENT) + .build() + ) + + whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems + + val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + + assertThat(dimensionItems).hasSize(1) + when (val item = dimensionItems.first()) { + is DimensionItem.DataItem.DataElementItem -> + assertThat(item.uid).isEqualTo(uid1) + else -> + fail("Unexpected dimension item type") + } + } + + @Test + fun `Should parse dataElementOperand dimension items`() { + val dataDimensionItems = listOf( + DataDimensionItem.builder() + .dataElementOperand(ObjectWithUid.create("$uid1.$uid2")) + .dataDimensionItemType(DataDimensionItemType.DATA_ELEMENT_OPERAND) + .build() + ) + + whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems + + val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + + assertThat(dimensionItems).hasSize(1) + when (val item = dimensionItems.first()) { + is DimensionItem.DataItem.DataElementOperandItem -> { + assertThat(item.dataElement).isEqualTo(uid1) + assertThat(item.categoryOptionCombo).isEqualTo(uid2) + } + else -> + fail("Unexpected dimension item type") + } + } + + @Test + fun `Should parse indicator dimension items`() { + val dataDimensionItems = listOf( + DataDimensionItem.builder() + .indicator(ObjectWithUid.create(uid1)) + .dataDimensionItemType(DataDimensionItemType.INDICATOR) + .build() + ) + + whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems + + val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + + assertThat(dimensionItems).hasSize(1) + when (val item = dimensionItems.first()) { + is DimensionItem.DataItem.IndicatorItem -> + assertThat(item.uid).isEqualTo(uid1) + else -> + fail("Unexpected dimension item type") + } + } + + @Test + fun `Should parse programIndicator dimension items`() { + val dataDimensionItems = listOf( + DataDimensionItem.builder() + .programIndicator(ObjectWithUid.create(uid1)) + .dataDimensionItemType(DataDimensionItemType.PROGRAM_INDICATOR) + .build() + ) + + whenever(visualization.dataDimensionItems()) doReturn dataDimensionItems + + val dimensionItems = helper.getDimensionItems(visualization, listOf("dx")) + + assertThat(dimensionItems).hasSize(1) + when (val item = dimensionItems.first()) { + is DimensionItem.DataItem.ProgramIndicatorItem -> + 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 orgunitLevels = listOf(1, 3) + + whenever(visualization.organisationUnits()) doReturn orgunitItems + whenever(visualization.organisationUnitLevels()) doReturn orgunitLevels + + val dimensionItems = helper.getDimensionItems(visualization, listOf("ou")) + + assertThat(dimensionItems).hasSize(4) + + val absolute = dimensionItems.filterIsInstance() + assertThat(absolute.map { it.uid }).containsExactly(uid1, uid2) + + val levels = dimensionItems.filterIsInstance() + assertThat(levels.map { it.level }).containsExactlyElementsIn(orgunitLevels) + } + + @Test + fun `Should parse relative organisation unit`() { + whenever(visualization.userOrganisationUnit()) doReturn true + whenever(visualization.userOrganisationUnitChildren()) doReturn true + whenever(visualization.userOrganisationUnitGrandChildren()) doReturn true + + val dimensionItems = helper.getDimensionItems(visualization, listOf("ou")) + + assertThat(dimensionItems).hasSize(3) + + val relative = dimensionItems.filterIsInstance() + assertThat(relative.map { it.relative }).containsExactly( + RelativeOrganisationUnit.USER_ORGUNIT, + RelativeOrganisationUnit.USER_ORGUNIT_CHILDREN, + RelativeOrganisationUnit.USER_ORGUNIT_GRANDCHILDREN + ) + } + + @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 + ) + + whenever(visualization.periods()) doReturn periods + whenever(visualization.relativePeriods()) doReturn relativePeriods + + val dimensionItems = helper.getDimensionItems(visualization, listOf("pe")) + + assertThat(dimensionItems).hasSize(4) + + val absolute = dimensionItems.filterIsInstance() + assertThat(absolute.map { it.periodId }).containsExactly(uid1, uid2) + + val relative = dimensionItems.filterIsInstance() + assertThat(relative.map { it.relative }).containsExactly(RelativePeriod.THIS_MONTH, RelativePeriod.LAST_MONTH) + } + + @Test + fun `Should parse category dimension items`() { + val categoryDimensions = listOf( + CategoryDimension.builder() + .category(ObjectWithUid.create(uid1)) + .categoryOptions(listOf(ObjectWithUid.create(uid2), ObjectWithUid.create(uid3))) + .build() + ) + + whenever(visualization.categoryDimensions()) doReturn categoryDimensions + whenever(categoryStore.selectByUid(uid1)) doReturn category + + val dimensionItems = helper.getDimensionItems(visualization, listOf(uid1)) + + assertThat(dimensionItems).hasSize(2) + + val catDimension = dimensionItems.filterIsInstance() + catDimension.forEach { assertThat(it.uid).isEqualTo(uid1) } + assertThat(catDimension.map { it.categoryOption }).containsExactly(uid2, uid3) + } + + @Test + fun `Should combine dimensions items`() { + val periods = listOf( + ObjectWithUid.create(uid1), + ObjectWithUid.create(uid2) + ) + val orgunits = listOf( + ObjectWithUid.create(uid3) + ) + + whenever(visualization.periods()) doReturn periods + whenever(visualization.organisationUnits()) doReturn orgunits + + val dimensionItems = helper.getDimensionItems(visualization, listOf("pe", "ou")) + + assertThat(dimensionItems).hasSize(3) + + val periodItems = dimensionItems.filterIsInstance() + assertThat(periodItems).hasSize(2) + + val orgunitItems = dimensionItems.filterIsInstance() + assertThat(orgunitItems).hasSize(1) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/arch/helpers/ResultShould.kt b/core/src/test/java/org/hisp/dhis/android/core/arch/helpers/ResultShould.kt new file mode 100644 index 0000000000..00ceb74819 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/arch/helpers/ResultShould.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004-2021, 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.helpers + +import com.google.common.truth.Truth +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerPositiveFailure +import org.hisp.dhis.android.core.common.valuetype.validation.validators.IntegerPositiveValidator +import org.junit.Assert.fail +import org.junit.Test + +class ResultShould { + + private val validator = IntegerPositiveValidator + + @Test + fun `Should return the same value when succeeding using when`() { + val value = "5" + when (val result = validator.validate(value)) { + is Result.Success -> Truth.assertThat(result.value).isEqualTo(value) + is Result.Failure -> fail() + } + } + + @Test + fun `Should return a failure when the validator fails`() { + val value = "-5" + when (val result = validator.validate(value)) { + is Result.Success -> fail() + is Result.Failure -> Truth.assertThat(result.failure).isEqualTo(IntegerPositiveFailure.ValueIsNegative) + } + } + + @Test + fun `Should return the same value when succeeding using fold`() { + val value = "5" + validator.validate(value).fold( + onSuccess = { + Truth.assertThat(it).isEqualTo(value) + }, + onFailure = { + fail() + } + ) + } + + @Test + fun `Should succeed when entering the right value`() { + Truth.assertThat(validator.validate("5").succeeded).isTrue() + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/BooleanValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/BooleanValidatorShould.kt new file mode 100644 index 0000000000..30f0d40fbe --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/BooleanValidatorShould.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.BooleanFailure +import org.junit.Test + +class BooleanValidatorShould : ValidatorShouldHelper(BooleanValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("true") + valueShouldSuccess("false") + } + + @Test + fun `Should fail when passing zero as string`() { + valueShouldFail("0", BooleanFailure.ZeroIsNotFalseException) + } + + @Test + fun `Should fail when passing one as string`() { + valueShouldFail("1", BooleanFailure.OneIsNotTrueException) + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("asg", BooleanFailure.BooleanMalformedException) + valueShouldFail("", BooleanFailure.BooleanMalformedException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/CoordinateValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/CoordinateValidatorShould.kt new file mode 100644 index 0000000000..b20420747f --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/CoordinateValidatorShould.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.CoordinateFailure +import org.junit.Test + +class CoordinateValidatorShould : ValidatorShouldHelper(CoordinateValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("[-11.876444,9.032566]") + valueShouldSuccess("[+90.0, -127.554334]") + valueShouldSuccess("[45, 180]") + valueShouldSuccess("[-90, -180]") + valueShouldSuccess("[+90, +180]") + valueShouldSuccess("[0,0]") + valueShouldSuccess("[47.1231231, 179.99999999]") + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("[-90., -180.]", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("[-368.532519,16.858788]", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("[-1,181]", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("[-181,1]", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("[+90.1, -100.111]", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("[-91, 123.456]", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("[-91,123.456]", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("[045, 180]", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("", CoordinateFailure.CoordinateMalformedException) + valueShouldFail("egs", CoordinateFailure.CoordinateMalformedException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateTimeValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateTimeValidatorShould.kt new file mode 100644 index 0000000000..df652cdbb4 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateTimeValidatorShould.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.DateTimeFailure +import org.junit.Test + +class DateTimeValidatorShould : ValidatorShouldHelper(DateTimeValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("2021-04-15T23:59") + valueShouldSuccess("0000-04-15T23:59") + valueShouldSuccess("1995-1-1T1:01") + valueShouldSuccess("0004-1-1T1:01") + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("", DateTimeFailure.ParseException) + valueShouldFail("asd", DateTimeFailure.ParseException) + valueShouldFail("12021-04-15T23:59", DateTimeFailure.ParseException) + valueShouldFail("2021-40-15T23:59", DateTimeFailure.ParseException) + valueShouldFail("2021-04-80T25:59", DateTimeFailure.ParseException) + valueShouldFail("2021-04-15T23:70", DateTimeFailure.ParseException) + valueShouldFail("2021-00-15T23:20", DateTimeFailure.ParseException) + valueShouldFail("2021-01-00T23:20", DateTimeFailure.ParseException) + valueShouldFail("2021-01-02N23:20", DateTimeFailure.ParseException) + valueShouldFail("-2021-04-15T23:59", DateTimeFailure.ParseException) + valueShouldFail("-0000-04-15T23:59", DateTimeFailure.ParseException) + valueShouldFail("2021-04-15T23:59-", DateTimeFailure.ParseException) + valueShouldFail("2021/04/15T23:59", DateTimeFailure.ParseException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobInfoShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateValidatorShould.kt similarity index 57% rename from core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobInfoShould.kt rename to core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateValidatorShould.kt index ae47292e46..77a573be90 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobInfoShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/DateValidatorShould.kt @@ -1,56 +1,57 @@ -/* - * Copyright (c) 2004-2021, 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.tracker.importer - -import com.google.common.truth.Truth.assertThat -import java.io.IOException -import java.text.ParseException -import org.hisp.dhis.android.core.Inject -import org.hisp.dhis.android.core.common.BaseIdentifiableObject -import org.hisp.dhis.android.core.common.BaseObjectShould -import org.hisp.dhis.android.core.common.ObjectShould -import org.hisp.dhis.android.core.tracker.importer.internal.JobInfo -import org.junit.Test - -class JobInfoShould : BaseObjectShould("tracker/importer/jobinfo.json"), ObjectShould { - - @Test - @Throws(IOException::class, ParseException::class) - override fun map_from_json_string() { - val objectMapper = Inject.objectMapper() - val jobInfo = objectMapper.readValue(jsonStream, JobInfo::class.java) - - assertThat(jobInfo.id).isEqualTo("id") - assertThat(jobInfo.uid).isEqualTo("uid") - assertThat(jobInfo.level).isEqualTo("INFO") - assertThat(jobInfo.category).isEqualTo("TRACKER_IMPORT_JOB") - assertThat(jobInfo.time).isEqualTo(BaseIdentifiableObject.DATE_FORMAT.parse("2021-01-25T12:09:18.571")) - assertThat(jobInfo.message).isEqualTo("(android) Import:Done took 0.360910 sec.") - assertThat(jobInfo.completed).isTrue() - } -} +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.DateFailure +import org.junit.Test + +class DateValidatorShould : ValidatorShouldHelper(DateValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("2021-04-14") + valueShouldSuccess("0001-01-01") + valueShouldSuccess("9999-12-31") + valueShouldSuccess("0001-01-1") + valueShouldSuccess("0001-1-1") + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("", DateFailure.ParseException) + valueShouldFail("asd", DateFailure.ParseException) + valueShouldFail("-5221-01-14", DateFailure.ParseException) + valueShouldFail("5221-01-14-", DateFailure.ParseException) + valueShouldFail("2021/01/10", DateFailure.ParseException) + valueShouldFail("2021-01-94", DateFailure.ParseException) + valueShouldFail("2021-33-04", DateFailure.ParseException) + valueShouldFail("2021-00-04", DateFailure.ParseException) + valueShouldFail("2021-02-00", DateFailure.ParseException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/EmailValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/EmailValidatorShould.kt new file mode 100644 index 0000000000..b829001d07 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/EmailValidatorShould.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.EmailFailure +import org.junit.Test + +class EmailValidatorShould : ValidatorShouldHelper(EmailValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("email@email.com") + valueShouldSuccess("email.123@email.com") + valueShouldSuccess("email.email@email.123.com") + valueShouldSuccess(".@1.1") + } + + @Test + fun `Should fail when value is malformed`() { + valueShouldFail("5fe2", EmailFailure.MalformedEmailException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerNegativeValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerNegativeValidatorShould.kt new file mode 100644 index 0000000000..cf51e8e890 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerNegativeValidatorShould.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerNegativeFailure +import org.junit.Test + +class IntegerNegativeValidatorShould : ValidatorShouldHelper(IntegerNegativeValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("-15") + valueShouldSuccess(Integer.MIN_VALUE.toString()) + } + + @Test + fun `Should fail with a value is positive exception when value is positive`() { + valueShouldFail("5", IntegerNegativeFailure.ValueIsPositive) + } + + @Test + fun `Should fail with a value is zero exception when value is zero`() { + valueShouldFail("0", IntegerNegativeFailure.ValueIsZero) + } + + @Test + fun `Should fail with a integer overflow exception when value is too big`() { + valueShouldFail("2147483648", IntegerNegativeFailure.IntegerOverflow) + valueShouldFail("-2147483649", IntegerNegativeFailure.IntegerOverflow) + } + + @Test + fun `Should fail with a number format exception when value is malformed`() { + valueShouldFail("5fe2", IntegerNegativeFailure.NumberFormatException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerPositiveValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerPositiveValidatorShould.kt new file mode 100644 index 0000000000..7432e9cf5c --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerPositiveValidatorShould.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerPositiveFailure +import org.junit.Test + +class IntegerPositiveValidatorShould : ValidatorShouldHelper(IntegerPositiveValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("15") + valueShouldSuccess(Integer.MAX_VALUE.toString()) + } + + @Test + fun `Should fail with a value is negative exception when value is negative`() { + valueShouldFail("-5", IntegerPositiveFailure.ValueIsNegative) + } + + @Test + fun `Should fail with a value is zero exception when value is zero`() { + valueShouldFail("0", IntegerPositiveFailure.ValueIsZero) + } + + @Test + fun `Should fail with a integer overflow exception when value is too big`() { + valueShouldFail("2147483648", IntegerPositiveFailure.IntegerOverflow) + valueShouldFail("-2147483649", IntegerPositiveFailure.IntegerOverflow) + } + + @Test + fun `Should fail with a number format exception when value is malformed`() { + valueShouldFail("5fe2", IntegerPositiveFailure.NumberFormatException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidatorShould.kt new file mode 100644 index 0000000000..7d31a4615b --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerValidatorShould.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerFailure +import org.junit.Test + +class IntegerValidatorShould : ValidatorShouldHelper(IntegerValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("15") + valueShouldSuccess("-15") + valueShouldSuccess("0") + valueShouldSuccess(Integer.MAX_VALUE.toString()) + valueShouldSuccess(Integer.MIN_VALUE.toString()) + } + + @Test + fun `Should fail with a integer overflow exception when value is too big`() { + valueShouldFail("2147483648", IntegerFailure.IntegerOverflow) + valueShouldFail("-2147483649", IntegerFailure.IntegerOverflow) + } + + @Test + fun `Should fail with a number format exception when value is malformed`() { + valueShouldFail("5fe2", IntegerFailure.NumberFormatException) + valueShouldFail("15.3", IntegerFailure.NumberFormatException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerZeroOrPositiveValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerZeroOrPositiveValidatorShould.kt new file mode 100644 index 0000000000..b1f43280eb --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/IntegerZeroOrPositiveValidatorShould.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.IntegerZeroOrPositiveFailure +import org.junit.Test + +class IntegerZeroOrPositiveValidatorShould : + ValidatorShouldHelper(IntegerZeroOrPositiveValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("15") + valueShouldSuccess("0") + valueShouldSuccess(Integer.MAX_VALUE.toString()) + } + + @Test + fun `Should fail with a value is negative exception when value is negative`() { + valueShouldFail("-5", IntegerZeroOrPositiveFailure.ValueIsNegative) + } + + @Test + fun `Should fail with a integer overflow exception when value is too big`() { + valueShouldFail("2147483648", IntegerZeroOrPositiveFailure.IntegerOverflow) + valueShouldFail("-2147483649", IntegerZeroOrPositiveFailure.IntegerOverflow) + } + + @Test + fun `Should fail with a number format exception when value is malformed`() { + valueShouldFail("5fe2", IntegerZeroOrPositiveFailure.NumberFormatException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LetterValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LetterValidatorShould.kt new file mode 100644 index 0000000000..2796694a74 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LetterValidatorShould.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.LetterFailure +import org.junit.Test + +class LetterValidatorShould : ValidatorShouldHelper(LetterValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("a") + valueShouldSuccess("Z") + } + + @Test + fun `Should fail when passing an empty string`() { + valueShouldFail("", LetterFailure.EmptyStringException) + } + + @Test + fun `Should fail when passing a string with more than one char`() { + valueShouldFail("as", LetterFailure.MoreThanOneLetterException) + } + + @Test + fun `Should fail when passing chars that are not letters`() { + valueShouldFail("4", LetterFailure.StringIsNotALetterException) + valueShouldFail("ñ", LetterFailure.StringIsNotALetterException) + valueShouldFail("Á", LetterFailure.StringIsNotALetterException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LongTextValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LongTextValidatorShould.kt new file mode 100644 index 0000000000..a0b5a71def --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/LongTextValidatorShould.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.LongTextFailure +import org.junit.Test + +class LongTextValidatorShould : ValidatorShouldHelper(LongTextValidator) { + + @Test + fun `Should success when passing any value`() { + valueShouldSuccess("This is a long text") + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/NumberValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/NumberValidatorShould.kt new file mode 100644 index 0000000000..5a19281263 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/NumberValidatorShould.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.NumberFailure +import org.junit.Test + +class NumberValidatorShould : ValidatorShouldHelper(NumberValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("0") + valueShouldSuccess("4") + valueShouldSuccess("+4") + valueShouldSuccess("254.3") + valueShouldSuccess("98.000005") + valueShouldSuccess("0000005.20") + valueShouldSuccess("+0003") + valueShouldSuccess("-6.299") + } + + @Test + fun `Should fail when passing scientific notation values`() { + valueShouldFail("3e-01", NumberFailure.ScientificNotationException) + valueShouldFail("3e-1", NumberFailure.ScientificNotationException) + valueShouldFail("12e10", NumberFailure.ScientificNotationException) + valueShouldFail("3.2e23", NumberFailure.ScientificNotationException) + valueShouldFail("37.e88", NumberFailure.ScientificNotationException) + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("", NumberFailure.NumberFormatException) + valueShouldFail("sdf", NumberFailure.NumberFormatException) + valueShouldFail("254,3", NumberFailure.NumberFormatException) + valueShouldFail("25.035,21", NumberFailure.NumberFormatException) + valueShouldFail("25,035.21", NumberFailure.NumberFormatException) + valueShouldFail("10:45", NumberFailure.NumberFormatException) + valueShouldFail("10a", NumberFailure.NumberFormatException) + valueShouldFail("10USD", NumberFailure.NumberFormatException) + valueShouldFail("A1.23", NumberFailure.NumberFormatException) + valueShouldFail("π", NumberFailure.NumberFormatException) + valueShouldFail("ln(2)", NumberFailure.NumberFormatException) + valueShouldFail("2/3", NumberFailure.NumberFormatException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PercentageValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PercentageValidatorShould.kt new file mode 100644 index 0000000000..f14d90d040 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PercentageValidatorShould.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.PercentageFailure +import org.junit.Test + +class PercentageValidatorShould : ValidatorShouldHelper(PercentageValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("0") + valueShouldSuccess("3") + valueShouldSuccess("35") + valueShouldSuccess("100") + } + + @Test + fun `Should fail when the value is greater than 100`() { + valueShouldFail("101", PercentageFailure.ValueGreaterThan100) + valueShouldFail("345", PercentageFailure.ValueGreaterThan100) + } + + @Test + fun `Should fail with a value is zero exception when value is zero`() { + valueShouldFail("-12", PercentageFailure.ValueIsNegative) + } + + @Test + fun `Should fail with a number format exception when value is malformed`() { + valueShouldFail("sdf", PercentageFailure.NumberFormatException) + valueShouldFail("23,4", PercentageFailure.NumberFormatException) + valueShouldFail("23.4", PercentageFailure.NumberFormatException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PhoneNumberValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PhoneNumberValidatorShould.kt new file mode 100644 index 0000000000..a3fc58a700 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/PhoneNumberValidatorShould.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.PhoneNumberFailure +import org.junit.Test + +class PhoneNumberValidatorShould : ValidatorShouldHelper(PhoneNumberValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("652214478") + valueShouldSuccess("+34 521 587 422") + valueShouldSuccess("0034 521 587 422") + valueShouldSuccess("0034521587422") + valueShouldSuccess("+34521587422") + valueShouldSuccess("+34 (521)-58 7422") + valueShouldSuccess("+55 11 99999-5555") + valueShouldSuccess("+65 6511 9266") + valueShouldSuccess("+86 21 2230 1000") + valueShouldSuccess("+9124 4723300") + valueShouldSuccess("+821012345678") + valueShouldSuccess("+593 7 282-3889") + valueShouldSuccess("(+44) 0848 9123 456") + valueShouldSuccess("+1 284 852 5500") + valueShouldSuccess("+1 345 9490088") + valueShouldSuccess("+32 2 702-9200") + } + + @Test + fun `Should fail with a number format exception when value is malformed`() { + valueShouldFail("5fe2", PhoneNumberFailure.MalformedPhoneNumberException) + valueShouldFail("987", PhoneNumberFailure.MalformedPhoneNumberException) + valueShouldFail("984534534432324325347", PhoneNumberFailure.MalformedPhoneNumberException) + valueShouldFail("234234234234ó", PhoneNumberFailure.MalformedPhoneNumberException) + valueShouldFail("", PhoneNumberFailure.MalformedPhoneNumberException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TextValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TextValidatorShould.kt new file mode 100644 index 0000000000..d69433066a --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TextValidatorShould.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.TextFailure +import org.junit.Test + +class TextValidatorShould : ValidatorShouldHelper(TextValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("15") + valueShouldSuccess("0") + } + + @Test + fun `Should success when the string has exactly 50000 chars`() { + valueShouldSuccess(buildStringWithLength(50000)) + } + + @Test + fun `Should fail when the string has more than 50000 chars`() { + valueShouldFail(buildStringWithLength(50001), TextFailure.TooLargeTextException) + } + + private fun buildStringWithLength(stringLength: Int): String { + val builder = StringBuilder() + repeat(stringLength) { builder.append("a") } + return builder.toString() + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TimeValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TimeValidatorShould.kt new file mode 100644 index 0000000000..eb014274a8 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TimeValidatorShould.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.TimeFailure +import org.junit.Test + +class TimeValidatorShould : ValidatorShouldHelper(TimeValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("00:00") + valueShouldSuccess("0:00") + valueShouldSuccess("02:20") + valueShouldSuccess("2:20") + valueShouldSuccess("11:59") + valueShouldSuccess("12:00") + valueShouldSuccess("12:59") + valueShouldSuccess("23:59") + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("", TimeFailure.ParseException) + valueShouldFail("sdf", TimeFailure.ParseException) + valueShouldFail("00", TimeFailure.ParseException) + valueShouldFail("0:", TimeFailure.ParseException) + valueShouldFail(":0", TimeFailure.ParseException) + valueShouldFail(":", TimeFailure.ParseException) + valueShouldFail("2", TimeFailure.ParseException) + valueShouldFail("23", TimeFailure.ParseException) + valueShouldFail("24", TimeFailure.ParseException) + valueShouldFail("08:85", TimeFailure.ParseException) + valueShouldFail("26:25", TimeFailure.ParseException) + valueShouldFail("-05:25", TimeFailure.ParseException) + valueShouldFail("-00:00", TimeFailure.ParseException) + valueShouldFail("-00:00", TimeFailure.ParseException) + valueShouldFail("00:1985", TimeFailure.ParseException) + valueShouldFail("24:00", TimeFailure.ParseException) + valueShouldFail("02:2", TimeFailure.ParseException) + valueShouldFail("2:2", TimeFailure.ParseException) + valueShouldFail("0:0", TimeFailure.ParseException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TrueOnlyValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TrueOnlyValidatorShould.kt new file mode 100644 index 0000000000..415893140e --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/TrueOnlyValidatorShould.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.TrueOnlyFailure +import org.junit.Test + +class TrueOnlyValidatorShould : ValidatorShouldHelper(TrueOnlyValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("true") + } + + @Test + fun `Should fail when passing false as string`() { + valueShouldFail("false", TrueOnlyFailure.FalseIsNotAValidValueException) + } + + @Test + fun `Should fail when passing one as string`() { + valueShouldFail("1", TrueOnlyFailure.OneIsNotTrueException) + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("asg", TrueOnlyFailure.BooleanMalformedException) + valueShouldFail("", TrueOnlyFailure.BooleanMalformedException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UidValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UidValidatorShould.kt new file mode 100644 index 0000000000..166919c69a --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UidValidatorShould.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.UidFailure +import org.junit.Test + +class UidValidatorShould : ValidatorShouldHelper(UidValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("E2vUc6z2CV6") + valueShouldSuccess("FsDo0TAPHPI") + valueShouldSuccess("01234567890") + valueShouldSuccess("abcdefghijk") + valueShouldSuccess("LMNOPQRSTUW") + } + + @Test + fun `Should fail when passing values with less than eleven chars`() { + valueShouldFail("FsDo0TAPHP", UidFailure.LessThanElevenCharsException) + } + + @Test + fun `Should fail when passing values with more than eleven chars`() { + valueShouldFail("FsDo0TAPHPI2", UidFailure.MoreThanElevenCharsException) + valueShouldFail("FsDo0TAPHPI FsDo0TAPHPI", UidFailure.MoreThanElevenCharsException) + valueShouldFail(".FsDo0TAPHPI", UidFailure.MoreThanElevenCharsException) + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("ñsDo0TAPHPI", UidFailure.MalformedUidException) + valueShouldFail("ÁsDo0TAPHPI", UidFailure.MalformedUidException) + valueShouldFail("ásDo0TAPHPI", UidFailure.MalformedUidException) + valueShouldFail(".sDo0TAPHPI", UidFailure.MalformedUidException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UnitIntervalValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UnitIntervalValidatorShould.kt new file mode 100644 index 0000000000..6ac8bd3320 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UnitIntervalValidatorShould.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.UnitIntervalFailure +import org.junit.Test + +class UnitIntervalValidatorShould : ValidatorShouldHelper(UnitIntervalValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("0") + valueShouldSuccess("0.2") + valueShouldSuccess("0.6666666666") + valueShouldSuccess("1") + } + + @Test + fun `Should fail when passing scientific notation values`() { + valueShouldFail("3e-01", UnitIntervalFailure.ScientificNotationException) + } + + @Test + fun `Should fail when passing values greater than one`() { + valueShouldFail("20", UnitIntervalFailure.GreaterThanOneException) + valueShouldFail("1.0001", UnitIntervalFailure.GreaterThanOneException) + } + + @Test + fun `Should fail when passing values smaller than zero`() { + valueShouldFail("-0.0001", UnitIntervalFailure.SmallerThanZeroException) + valueShouldFail("-15", UnitIntervalFailure.SmallerThanZeroException) + } + + @Test + fun `Should fail when passing malformed values`() { + valueShouldFail("", UnitIntervalFailure.NumberFormatException) + valueShouldFail("sdf", UnitIntervalFailure.NumberFormatException) + valueShouldFail("254,3", UnitIntervalFailure.NumberFormatException) + valueShouldFail("25.035,21", UnitIntervalFailure.NumberFormatException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UrlValidatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UrlValidatorShould.kt new file mode 100644 index 0000000000..80c6c22374 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/UrlValidatorShould.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import org.hisp.dhis.android.core.common.valuetype.validation.failures.UrlFailure +import org.junit.Test + +class UrlValidatorShould : ValidatorShouldHelper(UrlValidator) { + + @Test + fun `Should success when passing valid values`() { + valueShouldSuccess("https://dhis2.org/resources/") + valueShouldSuccess("http://www.dhis2.org") + valueShouldSuccess("http://dhis2.org") + valueShouldSuccess("https://dhis2.org") + valueShouldSuccess("https://dhis2.org/demo") + valueShouldSuccess("https://dhis2.org/demo/yo-yo") + } + + @Test + fun `Should fail when value is malformed`() { + valueShouldFail("5fe2", UrlFailure.MalformedUrlException) + valueShouldFail("", UrlFailure.MalformedUrlException) + valueShouldFail("dhis2.org", UrlFailure.MalformedUrlException) + valueShouldFail("www.dhis2.org", UrlFailure.MalformedUrlException) + valueShouldFail("dhis2.org/demo", UrlFailure.MalformedUrlException) + valueShouldFail("http://localhost:4200/demo", UrlFailure.MalformedUrlException) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValidatorShouldHelper.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValidatorShouldHelper.kt new file mode 100644 index 0000000000..3fb6f0d668 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValidatorShouldHelper.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import com.google.common.truth.Truth +import org.hisp.dhis.android.core.arch.helpers.Result +import org.junit.Assert.fail + +open class ValidatorShouldHelper(v: ValueTypeValidator) { + + private val validator = v + + fun valueShouldSuccess(value: String) { + when (val result = validator.validate(value)) { + is Result.Success -> Truth.assertThat(result.value).isEqualTo(value) + is Result.Failure -> fail() + } + } + + fun valueShouldFail(value: String, failure: F) { + when (val result = validator.validate(value)) { + is Result.Success -> fail() + is Result.Failure -> Truth.assertThat(result.failure).isEqualTo(failure) + } + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValueTypeValidatorGetterShould.kt b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValueTypeValidatorGetterShould.kt new file mode 100644 index 0000000000..0e993ffb1f --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/common/valuetype/validation/validators/ValueTypeValidatorGetterShould.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2021, 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.common.valuetype.validation.validators + +import com.google.common.truth.Truth +import org.hisp.dhis.android.core.arch.helpers.Result +import org.hisp.dhis.android.core.common.ValueType +import org.junit.Assert.fail +import org.junit.Test + +class ValueTypeValidatorGetterShould { + + private val valueType = ValueType.INTEGER_POSITIVE + + @Test + fun `Should return a valid value type validator`() { + val value = "5" + when (val result = valueType.validator.validate(value)) { + is Result.Success -> Truth.assertThat(result.value).isEqualTo(value) + is Result.Failure -> fail() + } + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandlerShould.java b/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandlerShould.java index 49e3544fa1..0f6aefd0bb 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandlerShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandlerShould.java @@ -28,8 +28,10 @@ package org.hisp.dhis.android.core.datavalue.internal; +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.datavalue.DataValue; +import org.hisp.dhis.android.core.datavalue.DataValueConflict; import org.hisp.dhis.android.core.imports.ImportStatus; import org.hisp.dhis.android.core.imports.internal.DataValueImportSummary; import org.junit.Before; @@ -53,6 +55,12 @@ public class DataValueImportHandlerShould { @Mock DataValueStore dataValueStore; + @Mock + DataValueConflictParser dataValueConflictParser; + + @Mock + ObjectStore dataValueConflictStore; + @Mock DataValueImportSummary dataValueImportSummary; @@ -69,7 +77,7 @@ public void setUp() { dataValueSet = new DataValueSet(Collections.singletonList(dataValue)); - dataValueImportHandler = new DataValueImportHandler(dataValueStore); + dataValueImportHandler = new DataValueImportHandler(dataValueStore, dataValueConflictParser, dataValueConflictStore); } @Test diff --git a/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/InvalidDataElementTypeConflictShould.kt b/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/InvalidDataElementTypeConflictShould.kt new file mode 100644 index 0000000000..dbea42e521 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/InvalidDataElementTypeConflictShould.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal.conflicts + +import com.nhaarman.mockitokotlin2.mock +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.dataelement.DataElement +import org.junit.Before +import org.junit.Test + +internal class InvalidDataElementTypeConflictShould { + + private lateinit var invalidDataElementTypeConflict: InvalidDataElementTypeConflict + private val dataElementStore: IdentifiableObjectStore = mock() + + @Before + fun setUp() { + invalidDataElementTypeConflict = InvalidDataElementTypeConflict(dataElementStore) + } + + @Test + fun `Should match error messages`() { + assert(invalidDataElementTypeConflict.matches(DataValueImportConflictSamples.invalidDataElementType())) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PastExpiryDateConflictShould.kt b/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PastExpiryDateConflictShould.kt new file mode 100644 index 0000000000..f31afc1a5f --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PastExpiryDateConflictShould.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal.conflicts + +import com.nhaarman.mockitokotlin2.mock +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.dataset.DataSet +import org.hisp.dhis.android.core.datavalue.internal.DataValueStore +import org.junit.Before +import org.junit.Test + +internal class PastExpiryDateConflictShould { + + private lateinit var pastExpiryDateConflict: PastExpiryDateConflict + private val dataValueStore: DataValueStore = mock() + private val dataSetStore: IdentifiableObjectStore = mock() + + @Before + fun setUp() { + pastExpiryDateConflict = PastExpiryDateConflict(dataValueStore, dataSetStore) + } + + @Test + fun `Should match error messages`() { + assert(pastExpiryDateConflict.matches(DataValueImportConflictSamples.pastExpiryDate())) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PeriodAfterLatestOpenFutureConflictShould.kt b/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PeriodAfterLatestOpenFutureConflictShould.kt new file mode 100644 index 0000000000..161c8fa85b --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/datavalue/internal/conflicts/PeriodAfterLatestOpenFutureConflictShould.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2021, 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.datavalue.internal.conflicts + +import com.nhaarman.mockitokotlin2.mock +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.dataelement.DataElement +import org.junit.Before +import org.junit.Test + +internal class PeriodAfterLatestOpenFutureConflictShould { + private lateinit var periodAfterLatestOpenFutureConflict: PeriodAfterLatestOpenFutureConflict + private val dataElementStore: IdentifiableObjectStore = mock() + + @Before + fun setUp() { + periodAfterLatestOpenFutureConflict = PeriodAfterLatestOpenFutureConflict(dataElementStore) + } + + @Test + fun `Should match error messages`() { + assert( + periodAfterLatestOpenFutureConflict.matches(DataValueImportConflictSamples.periodAfterLatestOpenFuture()) + ) + } +} 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 79db4ace54..40e22adc63 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 @@ -51,6 +51,7 @@ import org.hisp.dhis.android.core.sms.domain.interactor.ConfigCase import org.hisp.dhis.android.core.systeminfo.internal.SystemInfoModuleDownloader import org.hisp.dhis.android.core.user.User import org.hisp.dhis.android.core.user.internal.UserModuleDownloader +import org.hisp.dhis.android.core.visualization.internal.VisualizationModuleDownloader import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -69,6 +70,7 @@ class MetadataCallShould : BaseCallShould() { private val programDownloader: ProgramModuleDownloader = mock() private val organisationUnitDownloader: OrganisationUnitModuleDownloader = mock() private val dataSetDownloader: DataSetModuleDownloader = mock() + private val visualizationDownloader: VisualizationModuleDownloader = mock() private val constantDownloader: ConstantModuleDownloader = mock() private val smsModule: SmsModule = mock() private val configCase: ConfigCase = mock() @@ -98,6 +100,9 @@ class MetadataCallShould : BaseCallShould() { whenever(dataSetDownloader.downloadMetadata(any())).thenReturn( Single.just(emptyList()) ) + whenever(visualizationDownloader.downloadMetadata()).thenReturn( + Single.just(emptyList()) + ) whenever(constantDownloader.downloadMetadata()).thenReturn(Single.just(emptyList())) whenever(categoryDownloader.downloadMetadata()).thenReturn(Completable.complete()) whenever(smsModule.configCase()).thenReturn(configCase) @@ -121,6 +126,7 @@ class MetadataCallShould : BaseCallShould() { programDownloader, organisationUnitDownloader, dataSetDownloader, + visualizationDownloader, constantDownloader, smsModule, databaseAdapter, @@ -165,6 +171,12 @@ class MetadataCallShould : BaseCallShould() { downloadAndAssertError() } + @Test + fun fail_when_visualization_download_call_fail() { + whenever(visualizationDownloader.downloadMetadata()).thenReturn(Single.error(d2Error)) + downloadAndAssertError() + } + @Test fun fail_when_program_call_fail() { whenever(programDownloader.downloadMetadata(any())).thenReturn(Single.error(d2Error)) diff --git a/core/src/test/java/org/hisp/dhis/android/core/enrollment/EnrollmentServiceShould.kt b/core/src/test/java/org/hisp/dhis/android/core/enrollment/EnrollmentServiceShould.kt index 431b2e8a81..bb469ad2f9 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/enrollment/EnrollmentServiceShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/enrollment/EnrollmentServiceShould.kt @@ -30,12 +30,19 @@ package org.hisp.dhis.android.core.enrollment import com.nhaarman.mockitokotlin2.doReturn import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever +import io.reactivex.Single import org.hisp.dhis.android.core.arch.helpers.AccessHelper +import org.hisp.dhis.android.core.common.Access +import org.hisp.dhis.android.core.common.DataAccess +import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.event.EventCollectionRepository import org.hisp.dhis.android.core.organisationunit.OrganisationUnit import org.hisp.dhis.android.core.organisationunit.OrganisationUnitCollectionRepository import org.hisp.dhis.android.core.program.AccessLevel import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.program.ProgramCollectionRepository +import org.hisp.dhis.android.core.program.ProgramStage +import org.hisp.dhis.android.core.program.ProgramStageCollectionRepository import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceCollectionRepository import org.junit.Assert.assertFalse @@ -54,7 +61,6 @@ class EnrollmentServiceShould { private val enrollment: Enrollment = mock() private val trackedEntityInstance: TrackedEntityInstance = mock() private val program: Program = mock() - private val organisationUnit: OrganisationUnit = mock() private val enrollmentRepository: EnrollmentCollectionRepository = mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) private val trackedEntityInstanceRepository: TrackedEntityInstanceCollectionRepository = @@ -62,10 +68,18 @@ class EnrollmentServiceShould { private val programRepository: ProgramCollectionRepository = mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) private val organisationUnitRepository: OrganisationUnitCollectionRepository = mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) + private val eventCollectionRepository: EventCollectionRepository = + mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) + private val programStageCollectionRepository: ProgramStageCollectionRepository = + mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) private val enrollmentService = EnrollmentService( - enrollmentRepository, trackedEntityInstanceRepository, - programRepository, organisationUnitRepository + enrollmentRepository, + trackedEntityInstanceRepository, + programRepository, + organisationUnitRepository, + eventCollectionRepository, + programStageCollectionRepository ) @Before @@ -152,4 +166,82 @@ class EnrollmentServiceShould { val access = enrollmentService.blockingGetEnrollmentAccess(trackedEntityInstanceUid, programUid) assert(access == EnrollmentAccess.PROTECTED_PROGRAM_DENIED) } + + @Test + fun `Enrollment has any events that allows events creation`() { + + whenever(enrollmentRepository.uid(enrollmentUid).blockingGet()) doReturn enrollment + whenever(enrollment.program()) doReturn programUid + + whenever( + programStageCollectionRepository.byProgramUid().eq(programUid) + ) doReturn programStageCollectionRepository + whenever( + programStageCollectionRepository.byAccessDataWrite().isTrue + ) doReturn programStageCollectionRepository + whenever( + programStageCollectionRepository.get() + ) doReturn Single.just(getProgramStages()) + + whenever( + eventCollectionRepository.byEnrollmentUid().eq(enrollmentUid) + ) doReturn eventCollectionRepository + whenever( + eventCollectionRepository.byDeleted().isFalse + ) doReturn eventCollectionRepository + whenever(eventCollectionRepository.get()) doReturn Single.just(getEventList()) + + assertTrue(enrollmentService.blockingGetAllowEventCreation(enrollmentUid, listOf("1"))) + } + + @Test + fun `Enrollment has not any events that allows events creation`() { + + whenever(enrollmentRepository.uid(enrollmentUid).blockingGet()) doReturn enrollment + whenever(enrollment.program()) doReturn programUid + + whenever( + programStageCollectionRepository.byProgramUid().eq(programUid) + ) doReturn programStageCollectionRepository + whenever( + programStageCollectionRepository.byAccessDataWrite().isTrue + ) doReturn programStageCollectionRepository + whenever( + programStageCollectionRepository.get() + ) doReturn Single.just(getProgramStages()) + + whenever( + eventCollectionRepository.byEnrollmentUid().eq(enrollmentUid) + ) doReturn eventCollectionRepository + whenever( + eventCollectionRepository.byDeleted().isFalse + ) doReturn eventCollectionRepository + whenever(eventCollectionRepository.get()) doReturn Single.just(getEventList()) + + assertFalse(enrollmentService.blockingGetAllowEventCreation(enrollmentUid, listOf("1", "2"))) + } + + private fun getEventList() = listOf( + Event.builder() + .uid("eventUid1") + .programStage("1") + .build(), + Event.builder() + .uid("eventUid2") + .programStage("2") + .build() + ) + + private fun getProgramStages() = listOf( + ProgramStage.builder() + .access(Access.create(true, true, DataAccess.create(true, true))) + .uid("1") + .repeatable(true) + .build(), + ProgramStage.builder() + .access(Access.create(true, true, DataAccess.create(true, true))) + .uid("2") + .repeatable(true) + .build() + ) } diff --git a/core/src/test/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandlerShould.java b/core/src/test/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandlerShould.java index 83e9b9bc0f..00cc31b67d 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandlerShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/enrollment/internal/EnrollmentImportHandlerShould.java @@ -29,19 +29,18 @@ package org.hisp.dhis.android.core.enrollment.internal; import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.common.internal.DataStatePropagator; import org.hisp.dhis.android.core.enrollment.Enrollment; import org.hisp.dhis.android.core.event.internal.EventImportHandler; import org.hisp.dhis.android.core.imports.ImportStatus; -import org.hisp.dhis.android.core.imports.TrackerImportConflict; import org.hisp.dhis.android.core.imports.internal.EnrollmentImportSummary; import org.hisp.dhis.android.core.imports.internal.EventImportSummaries; import org.hisp.dhis.android.core.imports.internal.EventImportSummary; import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser; +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore; import org.hisp.dhis.android.core.note.Note; -import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStore; +import org.hisp.dhis.android.core.tracker.importer.internal.JobReportEnrollmentHandler; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -68,12 +67,6 @@ public class EnrollmentImportHandlerShould { @Mock private EnrollmentStore enrollmentStore; - @Mock - private TrackedEntityInstanceStore trackedEntityInstanceStore; - - @Mock - private IdentifiableObjectStore noteStore; - @Mock private EventImportHandler eventImportHandler; @@ -87,11 +80,14 @@ public class EnrollmentImportHandlerShould { private EnrollmentImportSummary importSummary; @Mock - private ObjectStore trackerImportConflictStore; + private TrackerImportConflictStore trackerImportConflictStore; @Mock private TrackerImportConflictParser trackerImportConflictParser; + @Mock + private JobReportEnrollmentHandler jobReportEnrollmentHandler; + @Mock private DataStatePropagator dataStatePropagator; @@ -107,15 +103,16 @@ public class EnrollmentImportHandlerShould { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - enrollmentImportHandler = new EnrollmentImportHandler(enrollmentStore, trackedEntityInstanceStore, noteStore, - eventImportHandler, trackerImportConflictStore, trackerImportConflictParser, dataStatePropagator); + enrollmentImportHandler = new EnrollmentImportHandler(enrollmentStore, eventImportHandler, + trackerImportConflictStore, trackerImportConflictParser, + jobReportEnrollmentHandler, dataStatePropagator); } @Test - public void do_nothing_when_passing_null_arguments() { - enrollmentImportHandler.handleEnrollmentImportSummary(null, enrollments, null); + public void do_nothing_when_passing_null_arguments() throws Exception { + enrollmentImportHandler.handleEnrollmentImportSummary(null, enrollments, "tei_uid"); - verify(enrollmentStore, never()).setStateOrDelete(anyString(), any(State.class)); + verify(enrollmentStore, never()).setSyncStateOrDelete(anyString(), any(State.class)); } @Test @@ -126,7 +123,7 @@ public void invoke_set_state_when_enrollment_import_summary_is_success_with_refe enrollmentImportHandler.handleEnrollmentImportSummary(Collections.singletonList(importSummary), enrollments, "test_tei_uid"); - verify(enrollmentStore, times(1)).setStateOrDelete("test_enrollment_uid", State.SYNCED); + verify(enrollmentStore, times(1)).setSyncStateOrDelete("test_enrollment_uid", State.SYNCED); } @Test @@ -137,7 +134,7 @@ public void invoke_set_state_when_enrollment_import_summary_is_error_with_refer enrollmentImportHandler.handleEnrollmentImportSummary(Collections.singletonList(importSummary), enrollments,"test_tei_uid"); - verify(enrollmentStore, times(1)).setStateOrDelete("test_enrollment_uid", State.ERROR); + verify(enrollmentStore, times(1)).setSyncStateOrDelete("test_enrollment_uid", State.ERROR); } @Test @@ -153,7 +150,7 @@ public void invoke_set_state_and_handle_event_import_summaries_when_enrollment_i enrollmentImportHandler.handleEnrollmentImportSummary(Collections.singletonList(importSummary), enrollments, "test_tei_uid"); - verify(enrollmentStore, times(1)).setStateOrDelete("test_enrollment_uid", State.SYNCED); + verify(enrollmentStore, times(1)).setSyncStateOrDelete("test_enrollment_uid", State.SYNCED); verify(eventImportHandler, times(1)).handleEventImportSummaries( eq(eventSummaries), anyList(), anyString(), anyString() ); @@ -171,7 +168,7 @@ public void mark_as_to_update_enrollments_not_present_in_the_response() { enrollmentImportHandler.handleEnrollmentImportSummary(Collections.singletonList(importSummary), enrollments, "test_tei_uid"); - verify(enrollmentStore, times(1)).setStateOrDelete("test_enrollment_uid", State.SYNCED); - verify(enrollmentStore, times(1)).setStateOrDelete("missing_enrollment_uid", State.TO_UPDATE); + verify(enrollmentStore, times(1)).setSyncStateOrDelete("test_enrollment_uid", State.SYNCED); + verify(enrollmentStore, times(1)).setSyncStateOrDelete("missing_enrollment_uid", State.TO_UPDATE); } } \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventImportHandlerShould.java b/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventImportHandlerShould.java index 4f3f998343..7a04abe56c 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventImportHandlerShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/event/internal/EventImportHandlerShould.java @@ -28,17 +28,16 @@ package org.hisp.dhis.android.core.event.internal; -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; import org.hisp.dhis.android.core.common.State; +import org.hisp.dhis.android.core.common.internal.DataStatePropagator; import org.hisp.dhis.android.core.enrollment.internal.EnrollmentStore; import org.hisp.dhis.android.core.event.Event; import org.hisp.dhis.android.core.imports.ImportStatus; -import org.hisp.dhis.android.core.imports.TrackerImportConflict; import org.hisp.dhis.android.core.imports.internal.EventImportSummary; import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser; -import org.hisp.dhis.android.core.note.Note; +import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStore; import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityInstanceStore; +import org.hisp.dhis.android.core.tracker.importer.internal.JobReportEventHandler; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -66,16 +65,13 @@ public class EventImportHandlerShould { private EventStore eventStore; @Mock - private EnrollmentStore enrollmentStore; + private TrackerImportConflictStore trackerImportConflictStore; @Mock - private IdentifiableObjectStore noteStore; + private JobReportEventHandler jobReportEventHandler; @Mock - private TrackedEntityInstanceStore trackedEntityInstanceStore; - - @Mock - private ObjectStore trackerImportConflictStore; + private DataStatePropagator dataStatePropagator; @Mock private TrackerImportConflictParser trackerImportConflictParser; @@ -94,15 +90,15 @@ public void setUp() throws Exception { when(importSummary.status()).thenReturn(ImportStatus.SUCCESS); - eventImportHandler = new EventImportHandler(eventStore, enrollmentStore, noteStore, trackedEntityInstanceStore, - trackerImportConflictStore, trackerImportConflictParser); + eventImportHandler = new EventImportHandler(eventStore, trackerImportConflictStore, + trackerImportConflictParser, jobReportEventHandler, dataStatePropagator); } @Test public void do_nothing_when_passing_null_argument() { eventImportHandler.handleEventImportSummaries(null, events, null, null); - verify(eventStore, never()).setStateOrDelete(anyString(), any(State.class)); + verify(eventStore, never()).setSyncStateOrDelete(anyString(), any(State.class)); } @Test @@ -113,7 +109,7 @@ public void invoke_set_state_after_handle_event_import_summaries_with_success_st eventImportHandler.handleEventImportSummaries(Collections.singletonList(importSummary), events, "test_enrollment_uid", "test_tei_uid"); - verify(eventStore, times(1)).setStateOrDelete("test_event_uid", State.SYNCED); + verify(eventStore, times(1)).setSyncStateOrDelete("test_event_uid", State.SYNCED); } @Test @@ -124,7 +120,7 @@ public void invoke_set_state_after_handle_event_import_summaries_with_error_stat eventImportHandler.handleEventImportSummaries(Collections.singletonList(importSummary), events, "test_enrollment_uid", "test_tei_uid"); - verify(eventStore, times(1)).setStateOrDelete("test_event_uid", State.ERROR); + verify(eventStore, times(1)).setSyncStateOrDelete("test_event_uid", State.ERROR); } @Test @@ -139,7 +135,7 @@ public void mark_as_to_update_events_not_present_in_the_response() { eventImportHandler.handleEventImportSummaries(Collections.singletonList(importSummary), events, "test_enrollment_uid", "test_tei_uid"); - verify(eventStore, times(1)).setStateOrDelete("test_event_uid", State.SYNCED); - verify(eventStore, times(1)).setStateOrDelete("missing_event_uid", State.TO_UPDATE); + verify(eventStore, times(1)).setSyncStateOrDelete("test_event_uid", State.SYNCED); + verify(eventStore, times(1)).setSyncStateOrDelete("missing_event_uid", State.TO_UPDATE); } } \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/imports/internal/RelationshipDeleteWebResponseShould.java b/core/src/test/java/org/hisp/dhis/android/core/imports/internal/RelationshipDeleteWebResponseShould.java new file mode 100644 index 0000000000..46d41db3c4 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/imports/internal/RelationshipDeleteWebResponseShould.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2021, 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.imports.internal; + +import static com.google.common.truth.Truth.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.hisp.dhis.android.core.Inject; +import org.hisp.dhis.android.core.arch.file.ResourcesFileReader; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class RelationshipDeleteWebResponseShould { + + @Test + public void map_from_json_string() throws Exception { + ObjectMapper objectMapper = Inject.objectMapper(); + + String responseStr = new ResourcesFileReader().getStringFromFile("imports/relationship_delete_web_response.json"); + RelationshipDeleteWebResponse webResponse = objectMapper.readValue(responseStr, + RelationshipDeleteWebResponse.class); + + assertThat(webResponse.message()).isEqualTo("Import was successful."); + assertThat(webResponse.response()).isNotNull(); + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/imports/internal/RelationshipWebResponseShould.java b/core/src/test/java/org/hisp/dhis/android/core/imports/internal/RelationshipWebResponseShould.java new file mode 100644 index 0000000000..1d65a7b3b2 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/imports/internal/RelationshipWebResponseShould.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004-2021, 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.imports.internal; + +import static com.google.common.truth.Truth.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.hisp.dhis.android.core.Inject; +import org.hisp.dhis.android.core.arch.file.ResourcesFileReader; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class RelationshipWebResponseShould { + + @Test + public void map_from_json_string() throws Exception { + ObjectMapper objectMapper = Inject.objectMapper(); + + String responseStr = new ResourcesFileReader().getStringFromFile("imports/relationship_web_response.json"); + RelationshipWebResponse webResponse = objectMapper.readValue(responseStr, RelationshipWebResponse.class); + + assertThat(webResponse.message()).isEqualTo("Import was successful."); + assertThat(webResponse.response()).isNotNull(); + } + + @Test + public void map_from_json_string_with_errors() throws Exception { + ObjectMapper objectMapper = Inject.objectMapper(); + + String webResponseStr = new ResourcesFileReader().getStringFromFile( + "imports/relationship_web_response_with_errors.json"); + objectMapper.readValue(webResponseStr, RelationshipWebResponse.class); + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCallUnitShould.java b/core/src/test/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCallUnitShould.java index d32af5e718..d667828add 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCallUnitShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitCallUnitShould.java @@ -30,6 +30,7 @@ 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.payload.internal.Payload; +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; import org.hisp.dhis.android.core.user.User; import org.hisp.dhis.android.core.user.UserInternalAccessor; @@ -97,6 +98,9 @@ public class OrganisationUnitCallUnitShould { @Mock private Date lastUpdated; + @Mock + private CollectionCleaner collectionCleaner; + @Mock private OrganisationUnitHandler organisationUnitHandler; @@ -151,7 +155,7 @@ public void setUp() throws IOException { when(user.nationality()).thenReturn("user_nationality"); organisationUnitCall = new OrganisationUnitCall(organisationUnitService, organisationUnitHandler, - organisationUnitDisplayPathTransformer) + organisationUnitDisplayPathTransformer, collectionCleaner) .download(user); //Return only one organisationUnit. diff --git a/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDHISVersionManagerShould.java b/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDHISVersionManagerShould.java index 20cfc922b4..ee9586d438 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDHISVersionManagerShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipDHISVersionManagerShould.java @@ -102,7 +102,7 @@ public void transform_2_30_relationship_into_equivalent_compatible_relationship( assert230Fields(compatible); assertThat(compatible.uid()).isEqualTo(UID); assertThat(compatible.relative()).isNull(); - assertThat(compatible.state()).isEqualTo(STATE); + assertThat(compatible.syncState()).isEqualTo(STATE); assertThat(compatible.deleted()).isEqualTo(DELETED); } @@ -121,7 +121,7 @@ public void transform_2_30_relationship_into_compatible_229_relationship() { assertThat(compatible.trackedEntityInstanceA()).isEqualTo(FROM_UID); assertThat(compatible.trackedEntityInstanceB()).isEqualTo(TO_UID); assertThat(compatible.uid()).isEqualTo(TYPE); - assertThat(compatible.state()).isEqualTo(STATE); + assertThat(compatible.syncState()).isEqualTo(STATE); assertThat(compatible.deleted()).isEqualTo(DELETED); } diff --git a/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipImportHandlerShould.kt b/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipImportHandlerShould.kt new file mode 100644 index 0000000000..a5de5d6c5f --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipImportHandlerShould.kt @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import com.nhaarman.mockitokotlin2.* +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.internal.DataStatePropagator +import org.hisp.dhis.android.core.imports.ImportStatus +import org.hisp.dhis.android.core.imports.internal.RelationshipImportSummary +import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository +import org.hisp.dhis.android.core.relationship.RelationshipItem +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito + +@RunWith(JUnit4::class) +class RelationshipImportHandlerShould { + + private val relationshipStore: RelationshipStore = mock() + + private val dataStatePropagator: DataStatePropagator = mock() + + private val relationshipCollectionRepository: RelationshipCollectionRepository = + mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) + + private val importSummary: RelationshipImportSummary = mock() + + private val relationship: Relationship = mock() + private val relationshipItem: RelationshipItem = mock() + + private val relationships: List = emptyList() + + // object to test + private lateinit var relationshipImportHandler: RelationshipImportHandler + + @Before + @Throws(Exception::class) + fun setUp() { + relationshipImportHandler = + RelationshipImportHandler(relationshipStore, dataStatePropagator, relationshipCollectionRepository) + + whenever(relationshipCollectionRepository.withItems().uid(any()).blockingGet()).doReturn(relationship) + whenever(relationship.from()).doReturn(relationshipItem) + } + + @Test + fun do_nothing_when_passing_null_argument() { + relationshipImportHandler.handleRelationshipImportSummaries(null, relationships) + + verify(relationshipStore, never()).setSyncStateOrDelete(anyString(), any()) + verify(dataStatePropagator, never()).propagateRelationshipUpdate(any()) + } + + @Test + fun setStatus_shouldUpdateRelationshipStatusSuccess() { + whenever(importSummary.status()).doReturn(ImportStatus.SUCCESS) + whenever(importSummary.reference()).doReturn("test_uid") + + relationshipImportHandler.handleRelationshipImportSummaries( + listOf(importSummary), relationships + ) + + verify(relationshipStore, times(1)).setSyncStateOrDelete("test_uid", State.SYNCED) + verify(dataStatePropagator, times(1)).propagateRelationshipUpdate(any()) + } + + @Test + fun setStatus_shouldUpdateRelationshipStatusError() { + whenever(importSummary.status()).doReturn(ImportStatus.ERROR) + whenever(importSummary.reference()).doReturn("test_uid") + + relationshipImportHandler.handleRelationshipImportSummaries( + listOf(importSummary), relationships + ) + + verify(relationshipStore, times(1)).setSyncStateOrDelete("test_uid", State.ERROR) + verify(dataStatePropagator, times(1)).propagateRelationshipUpdate(any()) + } + + @Test + fun mark_as_to_update_relationships_not_present_in_the_response() { + whenever(importSummary.status()).doReturn(ImportStatus.SUCCESS) + whenever(importSummary.reference()).doReturn("test_uid") + + val relationships = listOf(relationship) + whenever(relationship.uid()).doReturn("missing_uid") + + relationshipImportHandler.handleRelationshipImportSummaries( + listOf(importSummary), relationships + ) + + verify(relationshipStore, times(1)).setSyncStateOrDelete("test_uid", State.SYNCED) + verify(relationshipStore, times(1)).setSyncStateOrDelete("missing_uid", State.TO_UPDATE) + + verify(dataStatePropagator, times(2)).propagateRelationshipUpdate(any()) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeCollectionRepositoryHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeCollectionRepositoryHelperShould.kt new file mode 100644 index 0000000000..c858b3a051 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/relationship/internal/RelationshipTypeCollectionRepositoryHelperShould.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2004-2021, 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.relationship.internal + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class RelationshipTypeCollectionRepositoryHelperShould { + + private val trackedEntityInstance: TrackedEntityInstance = mock() + + /** + * Test to showcase the generated sql query + */ + @Test + fun create_tracked_entity_instance_raw_query() { + val teiUid = "tei_uid" + val teiTypeUid = "tei_type_uid" + whenever(trackedEntityInstance.uid()).doReturn(teiUid) + whenever(trackedEntityInstance.trackedEntityType()).doReturn(teiTypeUid) + + val rawQuery = + RelationshipTypeCollectionRepositoryHelper.availableForTrackedEntityInstanceRawQuery(trackedEntityInstance) + + assertThat(rawQuery).isEqualTo( + "SELECT DISTINCT uid FROM RelationshipType " + + "WHERE uid IN " + + "(" + + "SELECT relationshipType FROM RelationshipConstraint " + + "WHERE constraintType = 'FROM' " + + "AND relationshipEntity = 'TRACKED_ENTITY_INSTANCE' " + + "AND trackedEntityType = '$teiTypeUid' " + + "AND (program IS NULL " + + "OR program IN (SELECT program FROM Enrollment WHERE trackedEntityInstance == '$teiUid'))" + + ") " + + "OR " + + "(" + + "bidirectional = 1 " + + "AND uid IN " + + "(" + + "SELECT relationshipType FROM RelationshipConstraint " + + "WHERE relationshipEntity = 'TRACKED_ENTITY_INSTANCE' " + + "AND trackedEntityType = '$teiTypeUid' " + + "AND (program IS NULL " + + "OR program IN (SELECT program FROM Enrollment WHERE trackedEntityInstance == '$teiUid'))" + + ")" + + ")" + ) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingShould.kt b/core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingV1Should.kt similarity index 98% rename from core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingShould.kt rename to core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingV1Should.kt index d6812b8e2b..50130d45cb 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingV1Should.kt @@ -36,7 +36,7 @@ import org.hisp.dhis.android.core.period.PeriodType import org.junit.Assert.fail import org.junit.Test -class AnalyticsSettingShould : BaseObjectShould("settings/analytics_settings.json"), ObjectShould { +class AnalyticsSettingV1Should : BaseObjectShould("settings/analytics_settings.json"), ObjectShould { @Test @Throws(IOException::class, ParseException::class) diff --git a/core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingV2Should.kt b/core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingV2Should.kt new file mode 100644 index 0000000000..b9244d7799 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/settings/AnalyticsSettingV2Should.kt @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2004-2021, 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.settings + +import com.google.common.truth.Truth +import java.io.IOException +import java.text.ParseException +import org.hisp.dhis.android.core.common.BaseObjectShould +import org.hisp.dhis.android.core.common.ObjectShould +import org.hisp.dhis.android.core.period.PeriodType +import org.junit.Test + +class AnalyticsSettingV2Should : BaseObjectShould("settings/analytics_settings_v2.json"), ObjectShould { + + @Test + @Throws(IOException::class, ParseException::class) + override fun map_from_json_string() { + val analyticsSettings = objectMapper.readValue(jsonStream, AnalyticsSettings::class.java) + + Truth.assertThat(analyticsSettings.tei().size).isEqualTo(3) + + analyticsSettings.tei().forEach { tei -> + when (tei.uid()) { + "fqEx2avRp1L" -> { + Truth.assertThat(tei.name()).isEqualTo("Height evolution") + Truth.assertThat(tei.shortName()).isEqualTo("H. evolution") + Truth.assertThat(tei.program()).isEqualTo("IpHINAT79UW") + Truth.assertThat(tei.programStage()).isEqualTo("dBwrot7S420") + Truth.assertThat(tei.period()).isEquivalentAccordingToCompareTo(PeriodType.Monthly) + Truth.assertThat(tei.type()).isEquivalentAccordingToCompareTo(ChartType.LINE) + } + } + } + + Truth.assertThat(analyticsSettings.dhisVisualizations().home().size).isEqualTo(2) + analyticsSettings.dhisVisualizations().home().forEach { group -> + when (group.id()) { + "12345678910" -> { + Truth.assertThat(group.name()).isEqualTo("Ejemplo") + Truth.assertThat(group.visualizations().size).isEqualTo(2) + } + "12345678911" -> { + Truth.assertThat(group.name()).isEqualTo("Otro ejemplo") + Truth.assertThat(group.visualizations().size).isEqualTo(1) + } + } + } + + Truth.assertThat(analyticsSettings.dhisVisualizations().dataSet().size).isEqualTo(1) + analyticsSettings.dhisVisualizations().dataSet().forEach { map -> + when (map.key) { + "BfMAe6Itzgt" -> { + Truth.assertThat(map.value.size).isEqualTo(1) + Truth.assertThat(map.value[0].visualizations().size).isEqualTo(1) + } + } + } + + Truth.assertThat(analyticsSettings.dhisVisualizations().program().size).isEqualTo(1) + analyticsSettings.dhisVisualizations().program().forEach { map -> + when (map.key) { + "IpHINAT79UW" -> { + Truth.assertThat(map.value.size).isEqualTo(1) + Truth.assertThat(map.value[0].visualizations().size).isEqualTo(2) + } + } + } + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingCallShould.kt b/core/src/test/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingCallShould.kt index 3990837840..b9957af871 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingCallShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/settings/internal/AnalyticsSettingCallShould.kt @@ -27,11 +27,17 @@ */ package org.hisp.dhis.android.core.settings.internal -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions +import com.nhaarman.mockitokotlin2.whenever import io.reactivex.Single import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor import org.hisp.dhis.android.core.arch.handlers.internal.Handler import org.hisp.dhis.android.core.data.maintenance.D2ErrorSamples +import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualization import org.hisp.dhis.android.core.settings.AnalyticsSettings import org.hisp.dhis.android.core.settings.AnalyticsTeiSetting import org.junit.Before @@ -42,6 +48,7 @@ import org.junit.runners.JUnit4 @RunWith(JUnit4::class) class AnalyticsSettingCallShould { private val handler: Handler = mock() + private val analyticsDhisVisualizationsSettingHandler: Handler = mock() private val service: SettingAppService = mock() private val analyticsSettingSingle: Single = mock() private val apiCallExecutor: RxAPICallExecutor = mock() @@ -53,7 +60,13 @@ class AnalyticsSettingCallShould { fun setUp() { whenever(appVersionManager.getDataStoreVersion()) doReturn Single.just(SettingsAppDataStoreVersion.V1_1) whenever(service.analyticsSettings(any())) doReturn analyticsSettingSingle - analyticsSettingCall = AnalyticsSettingCall(handler, service, apiCallExecutor, appVersionManager) + analyticsSettingCall = AnalyticsSettingCall( + handler, + analyticsDhisVisualizationsSettingHandler, + service, + apiCallExecutor, + appVersionManager + ) } @Test diff --git a/core/src/test/java/org/hisp/dhis/android/core/sms/mockrepos/MockLocalDbRepository.java b/core/src/test/java/org/hisp/dhis/android/core/sms/mockrepos/MockLocalDbRepository.java index e921a3bd79..bdc8b788a0 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/sms/mockrepos/MockLocalDbRepository.java +++ b/core/src/test/java/org/hisp/dhis/android/core/sms/mockrepos/MockLocalDbRepository.java @@ -159,6 +159,11 @@ public Completable updateEnrollmentSubmissionState(TrackedEntityInstance tracked return Completable.complete(); } + @Override + public Completable updateRelationshipSubmissionState(String relationshipUid, State state) { + return Completable.complete(); + } + @Override public Completable setMetadataDownloadConfig(WebApiRepository.GetMetadataIdsConfig metadataIdsConfig) { return Completable.fromAction(() -> this.metadataIdsConfig = metadataIdsConfig); diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGeneratorTaskShould.kt b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGeneratorTaskShould.kt new file mode 100644 index 0000000000..97334e3dd3 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/NewTrackerImporterTrackedEntityPostPayloadGeneratorTaskShould.kt @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.arch.helpers.UidGeneratorImpl +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.enrollment.NewTrackerImporterEnrollment +import org.hisp.dhis.android.core.event.NewTrackerImporterEvent +import org.hisp.dhis.android.core.relationship.NewTrackerImporterRelationship +import org.hisp.dhis.android.core.relationship.NewTrackerImporterRelationshipItem +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class NewTrackerImporterTrackedEntityPostPayloadGeneratorTaskShould { + + private val uidGenerator = UidGeneratorImpl() + + @Test + fun should_generate_granular_payload_for_enrollments() { + val syncedTei = generateTEI(State.SYNCED) + val updatedEnrollment = generateEnrollment(syncedTei.uid(), State.TO_UPDATE) + val updatedEvent = generateEvent(updatedEnrollment.uid()) + + val wrapper = getTaskFor( + listOf(syncedTei), + mapOf(updatedEnrollment.uid()!! to listOf(updatedEvent)), + mapOf(syncedTei.uid()!! to listOf(updatedEnrollment)), + emptyMap() + ).generate() + + assertThat(wrapper.deleted.isEmpty()).isTrue() + + assertThat(wrapper.updated.trackedEntities).isEmpty() + assertThat(wrapper.updated.enrollments).hasSize(1) + assertThat(wrapper.updated.events).hasSize(1) + } + + @Test + fun should_generate_deleted_payload_for_event() { + val syncedTei = generateTEI() + val syncedEnrollment = generateEnrollment(syncedTei.uid()) + val deletedEvent = generateEvent(syncedEnrollment.uid(), true) + + val wrapper = getTaskFor( + listOf(syncedTei), + mapOf(syncedEnrollment.uid()!! to listOf(deletedEvent)), + mapOf(syncedTei.uid()!! to listOf(syncedEnrollment)), + emptyMap() + ).generate() + + assertThat(wrapper.deleted.trackedEntities).isEmpty() + assertThat(wrapper.deleted.enrollments).isEmpty() + assertThat(wrapper.deleted.events).hasSize(1) + + assertThat(wrapper.updated.isEmpty()).isTrue() + } + + @Test + fun should_generate_granular_payload_for_relationships() { + val syncedTei = generateTEI() + val relationship = generateRelationship( + NewTrackerImporterRelationshipItem.builder().trackedEntity(syncedTei.uid()).build() + ) + + val wrapper = getTaskFor( + listOf(syncedTei), + emptyMap(), + emptyMap(), + mapOf(syncedTei.uid() to listOf(relationship)) + ).generate() + + assertThat(wrapper.deleted.isEmpty()).isTrue() + + assertThat(wrapper.updated.trackedEntities).isEmpty() + assertThat(wrapper.updated.enrollments).isEmpty() + assertThat(wrapper.updated.events).isEmpty() + assertThat(wrapper.updated.relationships).hasSize(1) + } + + private fun generateTEI( + synState: State = State.SYNCED, + deleted: Boolean = false + ): TrackedEntityInstance { + return TrackedEntityInstance.builder() + .uid(uidGenerator.generate()) + .syncState(synState) + .aggregatedSyncState(State.TO_UPDATE) + .deleted(deleted) + .build() + } + + private fun generateEnrollment( + teiUid: String, + synState: State = State.SYNCED, + deleted: Boolean = false + ): NewTrackerImporterEnrollment { + return NewTrackerImporterEnrollment.builder() + .uid(uidGenerator.generate()) + .trackedEntity(teiUid) + .syncState(synState) + .aggregatedSyncState(State.TO_UPDATE) + .deleted(deleted) + .build() + } + + private fun generateEvent( + enrollmentUid: String, + deleted: Boolean = false + ): NewTrackerImporterEvent { + return NewTrackerImporterEvent.builder() + .uid(uidGenerator.generate()) + .enrollment(enrollmentUid) + .syncState(State.TO_UPDATE) + .aggregatedSyncState(State.TO_UPDATE) + .deleted(deleted) + .build() + } + + private fun generateRelationship( + from: NewTrackerImporterRelationshipItem + ): NewTrackerImporterRelationship { + return NewTrackerImporterRelationship.builder() + .uid(uidGenerator.generate()) + .from(from) + .syncState(State.TO_UPDATE) + .deleted(false) + .build() + } + + private fun getTaskFor( + trackedEntities: List, + eventMap: Map>, + enrollmentMap: Map>, + relationshipMap: Map> + ): NewTrackerImporterTrackedEntityPostPayloadGeneratorTask { + return NewTrackerImporterTrackedEntityPostPayloadGeneratorTask( + trackedEntities, + mapOf(), + eventMap, + enrollmentMap, + mapOf(), + relationshipMap, + listOf() + ) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceHandlerShould.java b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceHandlerShould.java index 4edef305b1..1b5e7df30b 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceHandlerShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceHandlerShould.java @@ -123,7 +123,8 @@ public void setUp() throws Exception { when(trackedEntityInstance.uid()).thenReturn(TEI_UID); when(trackedEntityInstance.toBuilder()).thenReturn(teiBuilder); - when(teiBuilder.state(State.SYNCED)).thenReturn(teiBuilder); + when(teiBuilder.syncState(State.SYNCED)).thenReturn(teiBuilder); + when(teiBuilder.aggregatedSyncState(State.SYNCED)).thenReturn(teiBuilder); when(teiBuilder.build()).thenReturn(trackedEntityInstance); when(TrackedEntityInstanceInternalAccessor.accessEnrollments(trackedEntityInstance)) .thenReturn(Collections.singletonList(enrollment)); @@ -222,7 +223,8 @@ public void do_not_invoke_cleaners_if_not_full_update() { public void invoke_relationship_handler_with_relationship_from_version_manager() { when(relationshipVersionManager.getRelativeTei(relationship229Compatible, TEI_UID)).thenReturn(relative); when(relative.toBuilder()).thenReturn(relativeBuilder); - when(relativeBuilder.state(any(State.class))).thenReturn(relativeBuilder); + when(relativeBuilder.syncState(any(State.class))).thenReturn(relativeBuilder); + when(relativeBuilder.aggregatedSyncState(any(State.class))).thenReturn(relativeBuilder); when(relativeBuilder.build()).thenReturn(relative); trackedEntityInstanceHandler.handleMany(Collections.singletonList(trackedEntityInstance), false, diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandlerShould.java b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandlerShould.java deleted file mode 100644 index 9d73a54c2f..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandlerShould.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2004-2021, 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.trackedentity.internal; - -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectStore; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.common.internal.DataStatePropagator; -import org.hisp.dhis.android.core.enrollment.internal.EnrollmentImportHandler; -import org.hisp.dhis.android.core.imports.ImportStatus; -import org.hisp.dhis.android.core.imports.TrackerImportConflict; -import org.hisp.dhis.android.core.imports.internal.EnrollmentImportSummaries; -import org.hisp.dhis.android.core.imports.internal.EnrollmentImportSummary; -import org.hisp.dhis.android.core.imports.internal.TEIImportSummary; -import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictParser; -import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository; -import org.hisp.dhis.android.core.relationship.internal.RelationshipDHISVersionManager; -import org.hisp.dhis.android.core.relationship.internal.RelationshipStore; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(JUnit4.class) -public class TrackedEntityInstanceImportHandlerShould { - - @Mock - private TrackedEntityInstanceStore trackedEntityInstanceStore; - - @Mock - private EnrollmentImportHandler enrollmentImportHandler; - - @Mock - private TEIImportSummary importSummary; - - @Mock - private EnrollmentImportSummary enrollmentSummary; - - @Mock - private EnrollmentImportSummaries importEnrollment; - - @Mock - private ObjectStore trackerImportConflictStore; - - @Mock - private TrackerImportConflictParser trackerImportConflictParser; - - @Mock - private RelationshipStore relationshipStore; - - @Mock - private DataStatePropagator dataStatePropagator; - - @Mock - private RelationshipDHISVersionManager relationshipDHISVersionManager; - - @Mock - private RelationshipCollectionRepository relationshipCollectionRepository; - - @Mock - private TrackedEntityInstance trackedEntityInstance; - - private final List instances = new ArrayList<>(); - - // object to test - private TrackedEntityInstanceImportHandler trackedEntityInstanceImportHandler; - - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - trackedEntityInstanceImportHandler = - new TrackedEntityInstanceImportHandler(trackedEntityInstanceStore, enrollmentImportHandler, - trackerImportConflictStore, trackerImportConflictParser, relationshipStore, dataStatePropagator, - relationshipDHISVersionManager, relationshipCollectionRepository); - } - - @Test - public void do_nothing_when_passing_null_argument() { - trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries(null, instances); - - verify(trackedEntityInstanceStore, never()).setStateOrDelete(anyString(), any(State.class)); - } - - @Test - public void setStatus_shouldUpdateTrackedEntityInstanceStatusSuccess() throws Exception { - when(importSummary.status()).thenReturn(ImportStatus.SUCCESS); - when(importSummary.reference()).thenReturn("test_tei_uid"); - - trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( - Collections.singletonList(importSummary), instances - ); - - verify(trackedEntityInstanceStore, times(1)).setStateOrDelete("test_tei_uid", State.SYNCED); - } - - @Test - public void setStatus_shouldUpdateTrackedEntityInstanceStatusError() throws Exception { - when(importSummary.status()).thenReturn(ImportStatus.ERROR); - when(importSummary.reference()).thenReturn("test_tei_uid"); - - trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( - Collections.singletonList(importSummary), instances - ); - - verify(trackedEntityInstanceStore, times(1)).setStateOrDelete("test_tei_uid", State.ERROR); - } - - @Test - public void update_tracker_entity_instance_status_success_status_and_handle_import_enrollment_on_import_success() throws Exception { - when(importSummary.status()).thenReturn(ImportStatus.SUCCESS); - when(importSummary.reference()).thenReturn("test_tei_uid"); - when(importSummary.enrollments()).thenReturn(importEnrollment); - List enrollmentSummaries = Collections.singletonList(enrollmentSummary); - when(importEnrollment.importSummaries()).thenReturn(enrollmentSummaries); - - trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( - Collections.singletonList(importSummary), instances - ); - - verify(trackedEntityInstanceStore, times(1)).setStateOrDelete("test_tei_uid", State.SYNCED); - verify(enrollmentImportHandler, times(1)).handleEnrollmentImportSummary( - eq(enrollmentSummaries), anyList(), anyString()); - } - - @Test - public void mark_as_to_update_tracked_entity_instances_not_present_in_the_response() throws Exception { - when(importSummary.status()).thenReturn(ImportStatus.SUCCESS); - when(importSummary.reference()).thenReturn("test_tei_uid"); - - List instances = new ArrayList<>(); - instances.add(trackedEntityInstance); - when(trackedEntityInstance.uid()).thenReturn("missing_tei_uid"); - - trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( - Collections.singletonList(importSummary), instances - ); - - verify(trackedEntityInstanceStore, times(1)).setStateOrDelete("test_tei_uid", State.SYNCED); - verify(trackedEntityInstanceStore, times(1)).setStateOrDelete("missing_tei_uid", State.TO_UPDATE); - } -} \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandlerShould.kt b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandlerShould.kt new file mode 100644 index 0000000000..249590b390 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityInstanceImportHandlerShould.kt @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2004-2021, 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.trackedentity.internal + +import com.nhaarman.mockitokotlin2.* +import java.util.* +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.common.internal.DataStatePropagator +import org.hisp.dhis.android.core.enrollment.internal.EnrollmentImportHandler +import org.hisp.dhis.android.core.imports.ImportStatus +import org.hisp.dhis.android.core.imports.internal.* +import org.hisp.dhis.android.core.relationship.RelationshipCollectionRepository +import org.hisp.dhis.android.core.relationship.internal.RelationshipDHISVersionManager +import org.hisp.dhis.android.core.relationship.internal.RelationshipStore +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyList +import org.mockito.ArgumentMatchers.anyString + +@RunWith(JUnit4::class) +class TrackedEntityInstanceImportHandlerShould { + private val trackedEntityInstanceStore: TrackedEntityInstanceStore = mock() + + private val enrollmentImportHandler: EnrollmentImportHandler = mock() + + private val importSummary: TEIImportSummary = mock() + + private val enrollmentSummary: EnrollmentImportSummary = mock() + + private val importEnrollment: EnrollmentImportSummaries = mock() + + private val trackerImportConflictStore: TrackerImportConflictStore = mock() + + private val trackerImportConflictParser: TrackerImportConflictParser = mock() + + private val relationshipStore: RelationshipStore = mock() + + private val dataStatePropagator: DataStatePropagator = mock() + + private val relationshipDHISVersionManager: RelationshipDHISVersionManager = mock() + + private val relationshipCollectionRepository: RelationshipCollectionRepository = mock() + + private val trackedEntityInstance: TrackedEntityInstance = mock() + + private val instances: List = ArrayList() + + // object to test + private lateinit var trackedEntityInstanceImportHandler: TrackedEntityInstanceImportHandler + + @Before + @Throws(Exception::class) + fun setUp() { + trackedEntityInstanceImportHandler = TrackedEntityInstanceImportHandler( + trackedEntityInstanceStore, enrollmentImportHandler, + trackerImportConflictStore, trackerImportConflictParser, relationshipStore, dataStatePropagator, + relationshipDHISVersionManager, relationshipCollectionRepository + ) + } + + @Test + fun do_nothing_when_passing_null_argument() { + trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries(null, instances) + verify(trackedEntityInstanceStore, never()).setSyncStateOrDelete(anyString(), any()) + } + + @Test + @Throws(Exception::class) + fun setStatus_shouldUpdateTrackedEntityInstanceStatusSuccess() { + whenever(importSummary.status()).doReturn(ImportStatus.SUCCESS) + whenever(importSummary.reference()).doReturn("test_tei_uid") + + trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( + listOf(importSummary), instances + ) + + verify(trackedEntityInstanceStore, times(1)) + .setSyncStateOrDelete("test_tei_uid", State.SYNCED) + } + + @Test + @Throws(Exception::class) + fun setStatus_shouldUpdateTrackedEntityInstanceStatusError() { + whenever(importSummary.status()).doReturn(ImportStatus.ERROR) + whenever(importSummary.reference()).doReturn("test_tei_uid") + + trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( + listOf(importSummary), instances + ) + + verify(trackedEntityInstanceStore, times(1)) + .setSyncStateOrDelete("test_tei_uid", State.ERROR) + } + + @Test + @Throws(Exception::class) + fun update_tracker_entity_instance_status_success_status_and_handle_import_enrollment_on_import_success() { + whenever(importSummary.status()).doReturn(ImportStatus.SUCCESS) + whenever(importSummary.reference()).doReturn("test_tei_uid") + whenever(importSummary.enrollments()).thenReturn(importEnrollment) + + val enrollmentSummaries = listOf(enrollmentSummary) + whenever(importEnrollment.importSummaries()).doReturn(enrollmentSummaries) + + trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( + listOf(importSummary), instances + ) + + verify(trackedEntityInstanceStore, times(1)) + .setSyncStateOrDelete("test_tei_uid", State.SYNCED) + verify(enrollmentImportHandler, times(1)).handleEnrollmentImportSummary( + eq(enrollmentSummaries), anyList(), anyString() + ) + } + + @Test + @Throws(Exception::class) + fun mark_as_to_update_tracked_entity_instances_not_present_in_the_response() { + whenever(importSummary.status()).doReturn(ImportStatus.SUCCESS) + whenever(importSummary.reference()).doReturn("test_tei_uid") + + val instances = listOf(trackedEntityInstance) + whenever(trackedEntityInstance.uid()).thenReturn("missing_tei_uid") + + trackedEntityInstanceImportHandler.handleTrackedEntityInstanceImportSummaries( + listOf(importSummary), instances + ) + + verify(trackedEntityInstanceStore, times(1)) + .setSyncStateOrDelete("test_tei_uid", State.SYNCED) + verify(trackedEntityInstanceStore, times(1)) + .setSyncStateOrDelete("missing_tei_uid", State.TO_UPDATE) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelperShould.java b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelperShould.java index 4ba92ce1f0..61986b19eb 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelperShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelperShould.java @@ -113,7 +113,7 @@ public void build_sql_query_with_states() { .build(); String sqlQuery = localQueryHelper.getSqlQuery(scope, Collections.emptySet(), 50); - assertThat(sqlQuery).contains("state IN ('SYNCED', 'TO_POST', 'TO_UPDATE')"); + assertThat(sqlQuery).contains("aggregatedSyncState IN ('SYNCED', 'TO_POST', 'TO_UPDATE')"); } @Test @@ -123,7 +123,7 @@ public void build_sql_without_relationships_by_default() { .build(); String sqlQuery = localQueryHelper.getSqlQuery(scope, Collections.emptySet(), 50); - assertThat(sqlQuery).contains("state != 'RELATIONSHIP'"); + assertThat(sqlQuery).contains("aggregatedSyncState != 'RELATIONSHIP'"); } @Test diff --git a/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobReportErrorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobReportErrorShould.kt index 3ad66fa510..02fa4516f7 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobReportErrorShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobReportErrorShould.kt @@ -36,6 +36,7 @@ import org.hisp.dhis.android.core.common.ObjectShould import org.hisp.dhis.android.core.tracker.importer.internal.JobImportCount import org.hisp.dhis.android.core.tracker.importer.internal.JobReport import org.hisp.dhis.android.core.tracker.importer.internal.JobValidationError +import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterObjectType import org.junit.Test class JobReportErrorShould : BaseObjectShould("tracker/importer/jobreport-error.json"), ObjectShould { @@ -55,7 +56,7 @@ class JobReportErrorShould : BaseObjectShould("tracker/importer/jobreport-error. assertThat(error).isEqualTo( JobValidationError( "PXi7gfVIk1p", - "EVENT", + TrackerImporterObjectType.EVENT, "E1033", "Event: `PXi7gfVIk1p`, Enrollment value is NULL." ) diff --git a/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobReportSuccessShould.kt b/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobReportSuccessShould.kt index 026801bc95..4df5bea9a4 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobReportSuccessShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/tracker/importer/JobReportSuccessShould.kt @@ -33,10 +33,7 @@ import java.text.ParseException import org.hisp.dhis.android.core.Inject import org.hisp.dhis.android.core.common.BaseObjectShould import org.hisp.dhis.android.core.common.ObjectShould -import org.hisp.dhis.android.core.tracker.importer.internal.JobImportCount -import org.hisp.dhis.android.core.tracker.importer.internal.JobObjectReport -import org.hisp.dhis.android.core.tracker.importer.internal.JobReport -import org.hisp.dhis.android.core.tracker.importer.internal.JobTypeReport +import org.hisp.dhis.android.core.tracker.importer.internal.* import org.junit.Test class JobReportSuccessShould : BaseObjectShould("tracker/importer/jobreport-success.json"), ObjectShould { @@ -68,7 +65,7 @@ class JobReportSuccessShould : BaseObjectShould("tracker/importer/jobreport-succ JobTypeReport( "EVENT", JobImportCount(2, 2, 2, 2, 8), - listOf(JobObjectReport(emptyList(), 0, "EVENT", "UavzrupW3lZ")) + listOf(JobObjectReport(emptyList(), 0, TrackerImporterObjectType.EVENT, "UavzrupW3lZ")) ) ) assertThat(bundleReport.typeReportMap.relationship).isEqualTo( diff --git a/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationShould.java b/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationShould.java new file mode 100644 index 0000000000..59a5f4f3fe --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/visualization/VisualizationShould.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2021, 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.common.BaseObjectShould; +import org.hisp.dhis.android.core.common.ObjectShould; +import org.hisp.dhis.android.core.data.visualization.VisualizationSamples; +import org.junit.Test; + +import java.io.IOException; +import java.text.ParseException; + +import static com.google.common.truth.Truth.assertThat; + +public class VisualizationShould extends BaseObjectShould implements ObjectShould { + + public VisualizationShould() { + super("visualization/visualization_simplified.json"); + } + + @Override + @Test + public void map_from_json_string() throws IOException, ParseException { + Visualization jsonVisualization = objectMapper.readValue(jsonStream, Visualization.class) + .toBuilder().id(null).build(); + Visualization expectedVisualization = VisualizationSamples.INSTANCE.visualization() + .toBuilder().id(null).build(); + assertThat(jsonVisualization).isEqualTo(expectedVisualization); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.java b/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.java new file mode 100644 index 0000000000..5751cf18f0 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/visualization/internal/VisualizationHandlerShould.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2004-2021, 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.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.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.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.common.collect.Lists; + +@RunWith(JUnit4.class) +public class VisualizationHandlerShould { + + @Mock + private IdentifiableObjectStore visualizationStore; + + @Mock + private CollectionCleaner visualizationCollectionCleaner; + + @Mock + private LinkStore dataDimensionItemStore; + + @Mock + private LinkStore visualizationCategoryDimensionLinkStore; + + @Mock + private LinkHandler visualizationCategoryDimensionLinkHandler; + + @Mock + private LinkHandler dataDimensionItemHandler; + + @Mock + private DataDimensionItem dataDimensionItem; + + @Mock + private CategoryDimension categoryDimension; + + @Mock + private ObjectWithUid category; + + @Mock + private Visualization visualization; + + @Mock + private List categories; + + // object to test + private VisualizationHandler visualizationHandler; + + public VisualizationHandlerShould() { + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.openMocks(this); + visualizationHandler = new VisualizationHandler( + visualizationStore, + visualizationCollectionCleaner, + visualizationCategoryDimensionLinkStore, + dataDimensionItemStore, + visualizationCategoryDimensionLinkHandler, + dataDimensionItemHandler); + + List dataDimensionItems = new ArrayList<>(); + dataDimensionItems.add(dataDimensionItem); + List categoryDimensions = new ArrayList<>(); + categories = new ArrayList<>(); + categories.add(category); + categoryDimensions.add(categoryDimension); + + when(visualization.dataDimensionItems()).thenReturn(dataDimensionItems); + when(visualizationStore.updateOrInsert(any())).thenReturn(HandleAction.Insert); + when(visualization.uid()).thenReturn("visualization_uid"); + when(category.uid()).thenReturn("category_uid"); + when(categoryDimension.category()).thenReturn(category); + when(visualization.categoryDimensions()).thenReturn(categoryDimensions); + } + + @Test + public void extend_identifiable_sync_handler_impl() { + IdentifiableHandlerImpl genericHandler = new VisualizationHandler + (visualizationStore, visualizationCollectionCleaner, visualizationCategoryDimensionLinkStore, dataDimensionItemStore, + visualizationCategoryDimensionLinkHandler, dataDimensionItemHandler); + } + + @Test + public void call_stores_to_delete_before_collection_handled() { + visualizationHandler.handleMany(Collections.singletonList(visualization)); + verify(visualizationCategoryDimensionLinkStore).delete(); + verify(dataDimensionItemStore).delete(); + } + + @Test + public void call_data_dimension_items_handler() { + visualizationHandler.handleMany(Collections.singletonList(visualization)); + verify(dataDimensionItemHandler).handleMany(any(), any(), any()); + } + + @Test + public void call_category_dimensions_link_handler() { + visualizationHandler.handleMany(Collections.singletonList(visualization)); + verify(visualizationCategoryDimensionLinkHandler).handleMany(any(), any(), any()); + } + + @Test + public void call_collection_cleaner() { + visualizationHandler.handleMany(Collections.singletonList(visualization)); + verify(visualizationCollectionCleaner).deleteNotPresent(any()); + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/testapp/datastore/KeyValuePairPublicAccessShould.java b/core/src/test/java/org/hisp/dhis/android/testapp/datastore/KeyValuePairPublicAccessShould.java new file mode 100644 index 0000000000..bf75937ae8 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/testapp/datastore/KeyValuePairPublicAccessShould.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2021, 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.datastore; + +import org.hisp.dhis.android.core.datastore.KeyValuePair; +import org.hisp.dhis.android.testapp.arch.BasePublicAccessShould; +import org.mockito.Mock; + +public class KeyValuePairPublicAccessShould extends BasePublicAccessShould { + + @Mock + private KeyValuePair object; + + @Override + public KeyValuePair object() { + return object; + } + + @Override + public void has_public_create_method() { + KeyValuePair.create(null); + } + + @Override + public void has_public_builder_method() { + KeyValuePair.builder(); + } + + @Override + public void has_public_to_builder_method() { + object().toBuilder(); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/testapp/visualization/CategoryDimensionPublicAccessShould.java b/core/src/test/java/org/hisp/dhis/android/testapp/visualization/CategoryDimensionPublicAccessShould.java new file mode 100644 index 0000000000..5934641cb8 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/testapp/visualization/CategoryDimensionPublicAccessShould.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2021, 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.CategoryDimension; +import org.hisp.dhis.android.testapp.arch.ObjectWithBuilderShould; +import org.mockito.Mock; + +public class CategoryDimensionPublicAccessShould extends ObjectWithBuilderShould { + + @Mock + private CategoryDimension object; + + @Override + public void has_public_builder_method() { + CategoryDimension.builder(); + } + + @Override + public void has_public_to_builder_method() { + object.toBuilder(); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..b94911c6da --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/testapp/visualization/DataDimensionItemPublicAccessShould.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2021, 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 diff --git a/core/src/test/java/org/hisp/dhis/android/testapp/visualization/VisualizationPublicAccessShould.java b/core/src/test/java/org/hisp/dhis/android/testapp/visualization/VisualizationPublicAccessShould.java new file mode 100644 index 0000000000..95ef136e7c --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/testapp/visualization/VisualizationPublicAccessShould.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2021, 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.Visualization; +import org.hisp.dhis.android.testapp.arch.BasePublicAccessShould; +import org.mockito.Mock; + +public class VisualizationPublicAccessShould extends BasePublicAccessShould { + + @Mock + private Visualization object; + + @Override + public Visualization object() { + return object; + } + + @Override + public void has_public_create_method() { + Visualization.create(null); + } + + @Override + public void has_public_builder_method() { + Visualization.builder(); + } + + @Override + public void has_public_to_builder_method() { + object().toBuilder(); + } +} \ No newline at end of file diff --git a/docs/content/developer/getting-started.md b/docs/content/developer/getting-started.md index d78a8accfc..c8423724db 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.4.2" + implementation "org.hisp.dhis:android-core:1.4.3" ... } ``` diff --git a/docs/dhis2_android_sdk_developer_guide_INDEX.md b/docs/dhis2_android_sdk_developer_guide_INDEX.md index 326d8ea284..5979c97a52 100644 --- a/docs/dhis2_android_sdk_developer_guide_INDEX.md +++ b/docs/dhis2_android_sdk_developer_guide_INDEX.md @@ -3,11 +3,11 @@ title: 'DHIS 2 Android SDK Developer Guide' author: 'DHIS 2' date: year: 2021 -month: June +month: September keywords: [DHIS2, Android] commit: version: master -applicable_txt: 'Applicable to version 1.4.2' +applicable_txt: 'Applicable to version 1.4.3' ---