From 2d5bb15806ef32c82b2dcfca74245f17ad7e6ba1 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 12 Dec 2022 16:19:33 +1100 Subject: [PATCH 001/201] [ANDROSDK-1531] Enable login to version 2.40 --- .../internal/EventPostPayloadGenerator.kt | 2 +- .../domain/converter/internal/Converter.java | 2 +- .../core/systeminfo/DHISPatchVersion.kt | 4 +- .../android/core/systeminfo/DHISVersion.kt | 5 +- ...sionManager.java => DHISVersionManager.kt} | 55 +++--- ...temInfoModule.java => SystemInfoModule.kt} | 10 +- .../internal/DHISVersionManagerImpl.java | 159 ---------------- .../internal/DHISVersionManagerImpl.kt | 129 +++++++++++++ .../systeminfo/internal/SystemInfoCall.java | 105 ----------- .../systeminfo/internal/SystemInfoCall.kt | 88 +++++++++ ...oduleImpl.java => SystemInfoModuleImpl.kt} | 42 ++--- .../OldTrackerImporterPayloadGenerator.kt | 2 +- ...hould.java => DHISVersionManagerShould.kt} | 64 +++---- .../internal/SystemInfoCallShould.java | 178 ------------------ .../internal/SystemInfoCallShould.kt | 141 ++++++++++++++ 15 files changed, 435 insertions(+), 551 deletions(-) rename core/src/main/java/org/hisp/dhis/android/core/systeminfo/{DHISVersionManager.java => DHISVersionManager.kt} (76%) rename core/src/main/java/org/hisp/dhis/android/core/systeminfo/{SystemInfoModule.java => SystemInfoModule.kt} (87%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.kt rename core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/{SystemInfoModuleImpl.java => SystemInfoModuleImpl.kt} (62%) rename core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/{DHISVersionManagerShould.java => DHISVersionManagerShould.kt} (63%) delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.kt diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGenerator.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGenerator.kt index dcce6e0f31..a4661ced5d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGenerator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGenerator.kt @@ -48,7 +48,7 @@ internal class EventPostPayloadGenerator @Inject internal constructor( val eventBuilder = event.toBuilder() .trackedEntityDataValues(dataValueMap[event.uid()]) .notes(notes.filter { it.event() == event.uid() }) - if (versionManager.is2_30) { + if (versionManager.is2_30()) { eventBuilder.geometry(null) } eventBuilder.build() diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/Converter.java b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/Converter.java index 1c8de22625..1b4b9e51e0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/Converter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/Converter.java @@ -83,7 +83,7 @@ private Single convert(@NonNull P dataItem, SMSMetadata metadata, String if (smsVersion == null) { throw D2Error.builder() .errorCode(D2ErrorCode.SMS_NOT_SUPPORTED) - .errorDescription("SMS is not supported in version " + dhisVersionManager.getPatchVersion()) + .errorDescription("SMS is not supported in version " + dhisVersionManager. getPatchVersion()) .errorComponent(D2ErrorComponent.SDK) .build(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISPatchVersion.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISPatchVersion.kt index 95a5b39e15..7e3626a16f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISPatchVersion.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISPatchVersion.kt @@ -64,7 +64,9 @@ enum class DHISPatchVersion(val majorVersion: DHISVersion, val strValue: String, V2_38_0(DHISVersion.V2_38, "2.38.0", SMSVersion.V2), - V2_39_0(DHISVersion.V2_39, "2.39.0", SMSVersion.V2); + V2_39_0(DHISVersion.V2_39, "2.39.0", SMSVersion.V2), + + V2_40_0(DHISVersion.V2_40, "2.40.0", SMSVersion.V2); companion object { @JvmStatic diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersion.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersion.kt index 5865b6aff7..816f3979e3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersion.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersion.kt @@ -38,7 +38,8 @@ enum class DHISVersion(internal val prefix: String) { V2_36("2.36"), V2_37("2.37"), V2_38("2.38"), - V2_39("2.39"); + V2_39("2.39"), + V2_40("2.40"); companion object { @JvmStatic @@ -53,7 +54,7 @@ enum class DHISVersion(internal val prefix: String) { @JvmStatic fun allowedVersionsAsStr(): Array { - return listOf(V2_30, V2_31, V2_32, V2_33, V2_34, V2_35, V2_36, V2_37, V2_38, V2_39) + return listOf(V2_30, V2_31, V2_32, V2_33, V2_34, V2_35, V2_36, V2_37, V2_38, V2_39, V2_40) .map { it.prefix } .toTypedArray() } 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.kt similarity index 76% rename from core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.java rename to core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt index bd18c11300..60f7b1fcff 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.kt @@ -25,37 +25,26 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.systeminfo; - -public interface DHISVersionManager { - DHISVersion getVersion(); - - DHISPatchVersion getPatchVersion(); - - SMSVersion getSmsVersion(); - - @Deprecated - boolean is2_29(); - - boolean is2_30(); - - boolean is2_31(); - - boolean is2_32(); - - boolean is2_33(); - - boolean is2_34(); - - boolean is2_35(); - - boolean is2_36(); - - boolean is2_37(); - - boolean is2_38(); - - boolean is2_39(); +package org.hisp.dhis.android.core.systeminfo + +interface DHISVersionManager { + fun getVersion(): DHISVersion + fun getPatchVersion(): DHISPatchVersion? + fun getSmsVersion(): SMSVersion? + + @Deprecated("") + fun is2_29(): Boolean + fun is2_30(): Boolean + fun is2_31(): Boolean + fun is2_32(): Boolean + fun is2_33(): Boolean + fun is2_34(): Boolean + fun is2_35(): Boolean + fun is2_36(): Boolean + fun is2_37(): Boolean + fun is2_38(): Boolean + fun is2_39(): Boolean + fun is2_40(): Boolean /** * Check if the current version is strictly greater than the parameter. @@ -63,7 +52,7 @@ public interface DHISVersionManager { * @param version Version to compare to * @return True if current version is strictly greater than the parameter. */ - boolean isGreaterThan(DHISVersion version); + fun isGreaterThan(version: DHISVersion): Boolean /** * Check if the current version is greater or equal than the parameter. @@ -71,5 +60,5 @@ public interface DHISVersionManager { * @param version Version to compare to * @return True if current version is greater or equal than the parameter. */ - boolean isGreaterOrEqualThan(DHISVersion version); + fun isGreaterOrEqualThan(version: DHISVersion): Boolean } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModule.java b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModule.kt similarity index 87% rename from core/src/main/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModule.java rename to core/src/main/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModule.kt index 4dd778d362..1da5412f82 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModule.kt @@ -25,11 +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.systeminfo; +package org.hisp.dhis.android.core.systeminfo -import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository; +import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository -public interface SystemInfoModule { - DHISVersionManager versionManager(); - ReadOnlyWithDownloadObjectRepository systemInfo(); +interface SystemInfoModule { + fun versionManager(): DHISVersionManager + fun systemInfo(): ReadOnlyWithDownloadObjectRepository } 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 deleted file mode 100644 index 7fdf2898b0..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.android.core.systeminfo.internal; - -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore; -import org.hisp.dhis.android.core.systeminfo.DHISPatchVersion; -import org.hisp.dhis.android.core.systeminfo.DHISVersion; -import org.hisp.dhis.android.core.systeminfo.DHISVersionManager; -import org.hisp.dhis.android.core.systeminfo.SMSVersion; -import org.hisp.dhis.android.core.systeminfo.SystemInfo; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class DHISVersionManagerImpl implements DHISVersionManager { - - private DHISVersion version; - private DHISPatchVersion patchVersion; - private SMSVersion smsVersion; - private final ObjectWithoutUidStore systemInfoStore; - - @Inject - DHISVersionManagerImpl(ObjectWithoutUidStore systemInfoStore) { - this.systemInfoStore = systemInfoStore; - } - - @Override - public DHISVersion getVersion() { - if (version == null) { - SystemInfo systemInfo = systemInfoStore.selectFirst(); - - if (systemInfo != null && systemInfo.version() != null) { - version = DHISVersion.getValue(systemInfo.version()); - } - } - return version; - } - - @Override - public DHISPatchVersion getPatchVersion() { - if (patchVersion == null) { - SystemInfo systemInfo = systemInfoStore.selectFirst(); - - if (systemInfo != null && systemInfo.version() != null) { - patchVersion = DHISPatchVersion.getValue(systemInfo.version()); - } - } - return patchVersion; - } - - @Override - public SMSVersion getSmsVersion() { - if (smsVersion == null) { - SystemInfo systemInfo = systemInfoStore.selectFirst(); - - if (systemInfo != null && systemInfo.version() != null) { - smsVersion = SMSVersion.getValue(systemInfo.version()); - } - } - return smsVersion; - } - - @Override - public boolean is2_29() { - return getVersion() == DHISVersion.V2_29; - } - - @Override - public boolean is2_30() { - return getVersion() == DHISVersion.V2_30; - } - - @Override - public boolean is2_31() { - return getVersion() == DHISVersion.V2_31; - } - - @Override - public boolean is2_32() { - return getVersion() == DHISVersion.V2_32; - } - - @Override - public boolean is2_33() { - return getVersion() == DHISVersion.V2_33; - } - - @Override - public boolean is2_34() { - return getVersion() == DHISVersion.V2_34; - } - - @Override - public boolean is2_35() { - return getVersion() == DHISVersion.V2_35; - } - - @Override - public boolean is2_36() { - return getVersion() == DHISVersion.V2_36; - } - - @Override - public boolean is2_37() { - return getVersion() == DHISVersion.V2_37; - } - - @Override - public boolean is2_38() { - return getVersion() == DHISVersion.V2_38; - } - - @Override - public boolean is2_39() { - return getVersion() == DHISVersion.V2_39; - } - - @Override - public boolean isGreaterThan(DHISVersion version) { - return version.compareTo(getVersion()) < 0; - } - - @Override - public boolean isGreaterOrEqualThan(DHISVersion version) { - return version.compareTo(getVersion()) <= 0; - } - - void setVersion(String versionStr) { - this.version = DHISVersion.getValue(versionStr); - this.patchVersion = DHISPatchVersion.getValue(versionStr); - this.smsVersion = SMSVersion.getValue(versionStr); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt new file mode 100644 index 0000000000..34fd0326ba --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.systeminfo.internal + +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.systeminfo.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DHISVersionManagerImpl @Inject internal constructor( + private val systemInfoStore: ObjectWithoutUidStore +) : DHISVersionManager { + private var version: DHISVersion? = null + private var patchVersion: DHISPatchVersion? = null + private var smsVersion: SMSVersion? = null + + override fun getVersion(): DHISVersion { + return version + ?: systemInfoStore.selectFirst()?.let { systemInfo -> + systemInfo.version()?.let { DHISVersion.getValue(it) } + .also { dhisVersion -> version = dhisVersion } + } + ?: throw RuntimeException("Cannot get DHIS2 version") + } + + override fun getPatchVersion(): DHISPatchVersion? { + return patchVersion + ?: systemInfoStore.selectFirst()?.let { systemInfo -> + systemInfo.version()?.let { DHISPatchVersion.getValue(it) } + .also { patch -> patchVersion = patch} + } + } + + override fun getSmsVersion(): SMSVersion? { + return smsVersion + ?: systemInfoStore.selectFirst()?.let { systemInfo -> + systemInfo.version()?.let { SMSVersion.getValue(it) } + .also { sms -> smsVersion = sms } + } + } + + override fun is2_29(): Boolean { + return getVersion() === DHISVersion.V2_29 + } + + override fun is2_30(): Boolean { + return getVersion() === DHISVersion.V2_30 + } + + override fun is2_31(): Boolean { + return getVersion() === DHISVersion.V2_31 + } + + override fun is2_32(): Boolean { + return getVersion() === DHISVersion.V2_32 + } + + override fun is2_33(): Boolean { + return getVersion() === DHISVersion.V2_33 + } + + override fun is2_34(): Boolean { + return getVersion() === DHISVersion.V2_34 + } + + override fun is2_35(): Boolean { + return getVersion() === DHISVersion.V2_35 + } + + override fun is2_36(): Boolean { + return getVersion() === DHISVersion.V2_36 + } + + override fun is2_37(): Boolean { + return getVersion() === DHISVersion.V2_37 + } + + override fun is2_38(): Boolean { + return getVersion() === DHISVersion.V2_38 + } + + override fun is2_39(): Boolean { + return getVersion() === DHISVersion.V2_39 + } + + override fun is2_40(): Boolean { + return getVersion() === DHISVersion.V2_40 + } + + override fun isGreaterThan(version: DHISVersion): Boolean { + return version < getVersion() + } + + override fun isGreaterOrEqualThan(version: DHISVersion): Boolean { + return version <= getVersion() + } + + internal fun setVersion(versionStr: String) { + version = DHISVersion.getValue(versionStr) + patchVersion = DHISPatchVersion.getValue(versionStr) + smsVersion = SMSVersion.getValue(versionStr) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.java b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.java deleted file mode 100644 index b69503acf6..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.android.core.systeminfo.internal; - -import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor; -import org.hisp.dhis.android.core.arch.call.internal.CompletableProvider; -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.db.access.Transaction; -import org.hisp.dhis.android.core.arch.handlers.internal.Handler; -import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper; -import org.hisp.dhis.android.core.maintenance.D2Error; -import org.hisp.dhis.android.core.maintenance.D2ErrorCode; -import org.hisp.dhis.android.core.maintenance.D2ErrorComponent; -import org.hisp.dhis.android.core.resource.internal.Resource; -import org.hisp.dhis.android.core.resource.internal.ResourceHandler; -import org.hisp.dhis.android.core.systeminfo.DHISVersion; -import org.hisp.dhis.android.core.systeminfo.SystemInfo; - -import javax.inject.Inject; - -import dagger.Reusable; -import io.reactivex.Completable; - -@Reusable -public class SystemInfoCall implements CompletableProvider { - private final DatabaseAdapter databaseAdapter; - private final Handler systemInfoHandler; - private final SystemInfoService systemInfoService; - private final ResourceHandler resourceHandler; - private final DHISVersionManagerImpl versionManager; - private final RxAPICallExecutor apiCallExecutor; - - @Inject - SystemInfoCall(DatabaseAdapter databaseAdapter, - Handler systemInfoHandler, - SystemInfoService systemInfoService, - ResourceHandler resourceHandler, - DHISVersionManagerImpl versionManager, - RxAPICallExecutor apiCallExecutor) { - this.databaseAdapter = databaseAdapter; - this.systemInfoHandler = systemInfoHandler; - this.systemInfoService = systemInfoService; - this.resourceHandler = resourceHandler; - this.versionManager = versionManager; - this.apiCallExecutor = apiCallExecutor; - } - - @Override - public Completable getCompletable(boolean storeError) { - return apiCallExecutor.wrapSingle(systemInfoService.getSystemInfo(SystemInfoFields.allFields), storeError) - .doOnSuccess(systemInfo -> { - if (DHISVersion.isAllowedVersion(systemInfo.version())) { - versionManager.setVersion(systemInfo.version()); - } else { - throw D2Error.builder() - .errorComponent(D2ErrorComponent.SDK) - .errorCode(D2ErrorCode.INVALID_DHIS_VERSION) - .errorDescription("Server DHIS version (" + systemInfo.version() + ") not valid. " - + "Allowed versions: " - + CollectionsHelper.commaAndSpaceSeparatedArrayValues( - DHISVersion.allowedVersionsAsStr())) - .build(); - } - - insertOrUpdateSystemInfo(systemInfo); - }).ignoreElement(); - } - - private void insertOrUpdateSystemInfo(SystemInfo systemInfo) { - Transaction transaction = databaseAdapter.beginNewTransaction(); - try { - systemInfoHandler.handle(systemInfo); - resourceHandler.setServerDate(systemInfo.serverDate()); - resourceHandler.handleResource(Resource.Type.SYSTEM_INFO); - transaction.setSuccessful(); - } finally { - transaction.end(); - } - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.kt new file mode 100644 index 0000000000..d74a5cefbd --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.kt @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.systeminfo.internal + +import dagger.Reusable +import io.reactivex.Completable +import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor +import org.hisp.dhis.android.core.arch.call.internal.CompletableProvider +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.arch.helpers.CollectionsHelper +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import org.hisp.dhis.android.core.maintenance.D2ErrorComponent +import org.hisp.dhis.android.core.resource.internal.Resource +import org.hisp.dhis.android.core.resource.internal.ResourceHandler +import org.hisp.dhis.android.core.systeminfo.DHISVersion.Companion.allowedVersionsAsStr +import org.hisp.dhis.android.core.systeminfo.DHISVersion.Companion.isAllowedVersion +import org.hisp.dhis.android.core.systeminfo.SystemInfo +import javax.inject.Inject + +@Reusable +class SystemInfoCall @Inject internal constructor( + private val databaseAdapter: DatabaseAdapter, + private val systemInfoHandler: Handler, + private val systemInfoService: SystemInfoService, + private val resourceHandler: ResourceHandler, + private val versionManager: DHISVersionManagerImpl, + private val apiCallExecutor: RxAPICallExecutor +) : CompletableProvider { + override fun getCompletable(storeError: Boolean): Completable { + return apiCallExecutor.wrapSingle(systemInfoService.getSystemInfo(SystemInfoFields.allFields), storeError) + .doOnSuccess { systemInfo: SystemInfo -> + val version = systemInfo.version() + if (version != null && isAllowedVersion(version)) { + versionManager.setVersion(version) + } else { + throw D2Error.builder() + .errorComponent(D2ErrorComponent.SDK) + .errorCode(D2ErrorCode.INVALID_DHIS_VERSION) + .errorDescription( + "Server DHIS version (" + version + ") not valid. " + + "Allowed versions: " + + CollectionsHelper.commaAndSpaceSeparatedArrayValues(allowedVersionsAsStr()) + ) + .build() + } + insertOrUpdateSystemInfo(systemInfo) + }.ignoreElement() + } + + private fun insertOrUpdateSystemInfo(systemInfo: SystemInfo) { + val transaction = databaseAdapter.beginNewTransaction() + try { + systemInfoHandler.handle(systemInfo) + resourceHandler.serverDate = systemInfo.serverDate() + resourceHandler.handleResource(Resource.Type.SYSTEM_INFO) + transaction.setSuccessful() + } finally { + transaction.end() + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.java b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.kt similarity index 62% rename from core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.java rename to core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.kt index c9b43603a4..1f3575027b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.kt @@ -25,37 +25,25 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.systeminfo.internal; +package org.hisp.dhis.android.core.systeminfo.internal -import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository; -import org.hisp.dhis.android.core.systeminfo.DHISVersionManager; -import org.hisp.dhis.android.core.systeminfo.SystemInfo; -import org.hisp.dhis.android.core.systeminfo.SystemInfoModule; - -import javax.inject.Inject; - -import dagger.Reusable; +import dagger.Reusable +import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager +import org.hisp.dhis.android.core.systeminfo.SystemInfo +import org.hisp.dhis.android.core.systeminfo.SystemInfoModule +import javax.inject.Inject @Reusable -public final class SystemInfoModuleImpl implements SystemInfoModule { - - private final DHISVersionManager versionManager; - private final ReadOnlyWithDownloadObjectRepository systemInfo; - - @Inject - SystemInfoModuleImpl(DHISVersionManager versionManager, - ReadOnlyWithDownloadObjectRepository systemInfoRepository) { - this.versionManager = versionManager; - this.systemInfo = systemInfoRepository; - } - - @Override - public DHISVersionManager versionManager() { - return versionManager; +class SystemInfoModuleImpl @Inject internal constructor( + private val versionManager: DHISVersionManager, + private val systemInfo: ReadOnlyWithDownloadObjectRepository +) : SystemInfoModule { + override fun versionManager(): DHISVersionManager { + return versionManager } - @Override - public ReadOnlyWithDownloadObjectRepository systemInfo() { - return systemInfo; + override fun systemInfo(): ReadOnlyWithDownloadObjectRepository { + return systemInfo } } 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 index 209f26ce0b..6f44d9eb69 100644 --- 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 @@ -328,7 +328,7 @@ internal class OldTrackerImporterPayloadGenerator @Inject internal constructor( .trackedEntityDataValues(extraData.dataValueMap[event.uid()]) .notes(getEventNotes(extraData.notes, event)) - if (versionManager.is2_30) { + if (versionManager.is2_30()) { eventBuilder.geometry(null) } diff --git a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.java b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.kt similarity index 63% rename from core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.java rename to core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.kt index fbb91394fa..8dae6f1792 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.kt @@ -25,49 +25,37 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package org.hisp.dhis.android.core.systeminfo.internal; - -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore; -import org.hisp.dhis.android.core.systeminfo.DHISVersion; -import org.hisp.dhis.android.core.systeminfo.DHISVersionManager; -import org.hisp.dhis.android.core.systeminfo.SystemInfo; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.IOException; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -public class DHISVersionManagerShould { - - @Mock - private ObjectWithoutUidStore systemInfoStore; - - @Mock - private SystemInfo systemInfo; +package org.hisp.dhis.android.core.systeminfo.internal + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.systeminfo.DHISVersion +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager +import org.hisp.dhis.android.core.systeminfo.SystemInfo +import org.junit.Before +import org.junit.Test + +class DHISVersionManagerShould { + private val systemInfoStore: ObjectWithoutUidStore = mock() + private val systemInfo: SystemInfo = mock() // Object to test - private DHISVersionManager dhisVersionManager; + private lateinit var dhisVersionManager: DHISVersionManager @Before - public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - when(systemInfoStore.selectFirst()).thenReturn(systemInfo); - - this.dhisVersionManager = new DHISVersionManagerImpl(systemInfoStore); + fun setUp() { + whenever(systemInfoStore.selectFirst()).thenReturn(systemInfo) + dhisVersionManager = DHISVersionManagerImpl(systemInfoStore) } @Test - public void compare_version_when_not_null() { - when(systemInfo.version()).thenReturn("2.31.2"); - - assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_30)).isTrue(); - assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_31)).isFalse(); - assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_32)).isFalse(); - assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_33)).isFalse(); + fun compare_version_when_not_null() { + whenever(systemInfo.version()).thenReturn("2.31.2") + assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_30)).isTrue() + assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_31)).isFalse() + assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_32)).isFalse() + assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_33)).isFalse() } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.java b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.java deleted file mode 100644 index e245f29968..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.android.core.systeminfo.internal; - -import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor; -import org.hisp.dhis.android.core.arch.api.fields.internal.Fields; -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.db.access.Transaction; -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.resource.internal.Resource; -import org.hisp.dhis.android.core.resource.internal.ResourceHandler; -import org.hisp.dhis.android.core.systeminfo.SystemInfo; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Date; - -import io.reactivex.Single; - -import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -@RunWith(JUnit4.class) -public class SystemInfoCallShould { - - @Mock - private SystemInfoService systemInfoService; - - @Mock - private DatabaseAdapter databaseAdapter; - - @Mock - private RxAPICallExecutor apiCallExecutor; - - @Mock - private D2Error d2Error; - - @Mock - private Handler systemInfoHandler; - - @Mock - private ResourceHandler resourceHandler; - - @Mock - private Single systemInfoSingle; - - @Mock - private Transaction transaction; - - @Captor - private ArgumentCaptor> filterCaptor; - - @Mock - private SystemInfo systemInfo; - - @Mock - private DHISVersionManagerImpl versionManager; - - @Mock - private Date serverDate; - - private SystemInfoCall systemInfoSyncCall; - - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - systemInfoSyncCall = new SystemInfoCall( - databaseAdapter, systemInfoHandler, systemInfoService, resourceHandler, versionManager, - apiCallExecutor); - - when(systemInfo.version()).thenReturn("2.29"); - when(systemInfo.serverDate()).thenReturn(serverDate); - - when(databaseAdapter.beginNewTransaction()).thenReturn(transaction); - when(systemInfoService.getSystemInfo(filterCaptor.capture())).thenReturn(systemInfoSingle); - } - - @Test - public void pass_correct_fields_to_service() { - when(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.just(systemInfo)); - systemInfoSyncCall.getCompletable(true).subscribe(); - assertThat(filterCaptor.getValue()).isEqualTo(SystemInfoFields.allFields); - } - - @Test - public void emit_d2_error_when_api_call_executor_returns_error() { - when(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.error(d2Error)); - systemInfoSyncCall.getCompletable(true).test().assertError(d2Error); - } - - @Test - public void never_invoke_handlers_on_call_exception() { - when(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.error(d2Error)); - - systemInfoSyncCall.getCompletable(true).onErrorComplete().subscribe(); - - verify(databaseAdapter, never()).beginNewTransaction(); - verify(transaction, never()).setSuccessful(); - verify(transaction, never()).end(); - - verifyNoMoreInteractions(systemInfoHandler); - verifyNoMoreInteractions(resourceHandler); - } - - @Test - public void invoke_handler_after_successful_call() { - when(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.just(systemInfo)); - - systemInfoSyncCall.getCompletable(true).subscribe(); - - verify(systemInfoHandler).handle(systemInfo); - verify(resourceHandler).handleResource(eq(Resource.Type.SYSTEM_INFO)); - } - - @Test - public void throw_d2_call_exception_when_system_version_not_supported() { - when(systemInfo.version()).thenReturn("2.28"); - when(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.just(systemInfo)); - - systemInfoSyncCall.getCompletable(true).test().assertError(D2Error.class); - } - - @Test - public void not_call_handler_when_system_version_not_supported() { - when(systemInfo.version()).thenReturn("2.28"); - when(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.just(systemInfo)); - - try { - systemInfoSyncCall.getCompletable(true).blockingAwait(); - fail("It should not get here"); - } catch (RuntimeException e) { - assertThat(((D2Error) e.getCause()).errorCode()) - .isEquivalentAccordingToCompareTo(D2ErrorCode.INVALID_DHIS_VERSION); - } - - verify(systemInfoHandler, never()).handle(systemInfo); - verify(resourceHandler, never()).handleResource(eq(Resource.Type.SYSTEM_INFO)); - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.kt b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.kt new file mode 100644 index 0000000000..1e76b3f979 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.kt @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.systeminfo.internal + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.* +import io.reactivex.Single +import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor +import org.hisp.dhis.android.core.arch.api.fields.internal.Fields +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.db.access.Transaction +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.resource.internal.Resource +import org.hisp.dhis.android.core.resource.internal.ResourceHandler +import org.hisp.dhis.android.core.systeminfo.SystemInfo +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import java.util.* + +@RunWith(JUnit4::class) +class SystemInfoCallShould { + private val systemInfoService: SystemInfoService = mock() + private val databaseAdapter: DatabaseAdapter = mock() + private val apiCallExecutor: RxAPICallExecutor = mock() + private val d2Error: D2Error = mock() + private val systemInfoHandler: Handler = mock() + private val resourceHandler: ResourceHandler = mock() + private val systemInfoSingle: Single = mock() + private val transaction: Transaction = mock() + + private val filterCaptor: KArgumentCaptor> = argumentCaptor() + + private val systemInfo: SystemInfo = mock() + private val versionManager: DHISVersionManagerImpl = mock() + private val serverDate: Date = mock() + + private lateinit var systemInfoSyncCall: SystemInfoCall + + @Before + fun setUp() { + systemInfoSyncCall = SystemInfoCall( + databaseAdapter, systemInfoHandler, systemInfoService, resourceHandler, versionManager, + apiCallExecutor + ) + + whenever(systemInfo.version()).thenReturn("2.29") + whenever(systemInfo.serverDate()).thenReturn(serverDate) + whenever(databaseAdapter.beginNewTransaction()).thenReturn(transaction) + whenever(systemInfoService.getSystemInfo(filterCaptor.capture())).thenReturn(systemInfoSingle) + } + + @Test + fun pass_correct_fields_to_service() { + whenever(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.just(systemInfo)) + systemInfoSyncCall.getCompletable(true).subscribe() + + assertThat(filterCaptor.firstValue).isEqualTo(SystemInfoFields.allFields) + } + + @Test + fun emit_d2_error_when_api_call_executor_returns_error() { + whenever(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.error(d2Error)) + + systemInfoSyncCall.getCompletable(true).test().assertError(d2Error) + } + + @Test + fun never_invoke_handlers_on_call_exception() { + whenever(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.error(d2Error)) + systemInfoSyncCall.getCompletable(true).onErrorComplete().subscribe() + + verify(databaseAdapter, never()).beginNewTransaction() + verify(transaction, never()).setSuccessful() + verify(transaction, never()).end() + verifyNoMoreInteractions(systemInfoHandler) + verifyNoMoreInteractions(resourceHandler) + } + + @Test + fun invoke_handler_after_successful_call() { + whenever(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.just(systemInfo)) + systemInfoSyncCall.getCompletable(true).subscribe() + + verify(systemInfoHandler).handle(systemInfo) + verify(resourceHandler).handleResource(eq(Resource.Type.SYSTEM_INFO)) + } + + @Test + fun throw_d2_call_exception_when_system_version_not_supported() { + whenever(systemInfo.version()).thenReturn("2.28") + whenever(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.just(systemInfo)) + + systemInfoSyncCall.getCompletable(true).test().assertError(D2Error::class.java) + } + + @Test + fun not_call_handler_when_system_version_not_supported() { + whenever(systemInfo.version()).thenReturn("2.28") + whenever(apiCallExecutor.wrapSingle(systemInfoSingle, true)).thenReturn(Single.just(systemInfo)) + try { + systemInfoSyncCall.getCompletable(true).blockingAwait() + Assert.fail("It should not get here") + } catch (e: RuntimeException) { + assertThat((e.cause as D2Error).errorCode()) + .isEquivalentAccordingToCompareTo(D2ErrorCode.INVALID_DHIS_VERSION) + } + + verify(systemInfoHandler, never()).handle(systemInfo) + verify(resourceHandler, never()).handleResource(eq(Resource.Type.SYSTEM_INFO)) + } +} From 088655c519405e72718741ff0dc4cf51be0e1c5c Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 12 Dec 2022 16:41:03 +1100 Subject: [PATCH 002/201] [ANDROSDK-1531] Download file resources for tracker attributes --- .../internal/FileResourceDownloadCall.kt | 40 +++++++++++-------- .../FileResourceDownloadCallHelper.kt | 20 +++++----- .../internal/FileResourceService.kt | 8 +++- .../internal/MissingTrackerAttributeValue.kt | 37 +++++++++++++++++ 4 files changed, 77 insertions(+), 28 deletions(-) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/MissingTrackerAttributeValue.kt diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCall.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCall.kt index 7fe318a0ce..c456dc528c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCall.kt @@ -32,7 +32,6 @@ import android.util.Log import dagger.Reusable import io.reactivex.Observable import io.reactivex.ObservableEmitter -import javax.inject.Inject import okhttp3.ResponseBody import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor @@ -42,13 +41,11 @@ import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableDataObject import org.hisp.dhis.android.core.arch.handlers.internal.HandlerWithTransformer import org.hisp.dhis.android.core.arch.helpers.FileResizerHelper import org.hisp.dhis.android.core.common.State -import org.hisp.dhis.android.core.fileresource.FileResource -import org.hisp.dhis.android.core.fileresource.FileResourceDomainType -import org.hisp.dhis.android.core.fileresource.FileResourceElementType -import org.hisp.dhis.android.core.fileresource.FileResourceInternalAccessor -import org.hisp.dhis.android.core.fileresource.FileResourceRoutine +import org.hisp.dhis.android.core.common.ValueType +import org.hisp.dhis.android.core.fileresource.* import org.hisp.dhis.android.core.maintenance.D2Error import retrofit2.Call +import javax.inject.Inject @Reusable internal class FileResourceDownloadCall @Inject constructor( @@ -110,13 +107,22 @@ internal class FileResourceDownloadCall @Inject constructor( values = attributeDataValues, maxContentLength = params.maxContentLength, download = { v -> - fileResourceService.getFileFromTrackedEntityAttribute( - v.trackedEntityInstance()!!, - v.trackedEntityAttribute()!!, - FileResizerHelper.Dimension.MEDIUM.name - ) + when (v.valueType) { + ValueType.IMAGE -> + fileResourceService.getImageFromTrackedEntityAttribute( + v.value.trackedEntityInstance()!!, + v.value.trackedEntityAttribute()!!, + FileResizerHelper.Dimension.MEDIUM.name + ) + ValueType.FILE_RESOURCE -> + fileResourceService.getFileFromTrackedEntityAttribute( + v.value.trackedEntityInstance()!!, + v.value.trackedEntityAttribute()!! + ) + else -> null + } }, - getUid = { v -> v.value() } + getUid = { v -> v.value.value() } ) } @@ -142,7 +148,7 @@ internal class FileResourceDownloadCall @Inject constructor( private fun downloadAndPersistFiles( values: List, maxContentLength: Int?, - download: (V) -> Call, + download: (V) -> Call?, getUid: (V) -> String? ) { val fileResources = values.mapNotNull { downloadFile(it, maxContentLength, download, getUid) } @@ -157,16 +163,16 @@ internal class FileResourceDownloadCall @Inject constructor( private fun downloadFile( value: V, maxContentLength: Int?, - download: (V) -> Call, + download: (V) -> Call?, getUid: (V) -> String? ): FileResource? { return getUid(value)?.let { uid -> try { val fileResource = apiCallExecutor.executeObjectCall(fileResourceService.getFileResource(uid)) - val acceptedContentLength = maxContentLength == null || - fileResource.contentLength() == null || - fileResource.contentLength()!! <= maxContentLength + val acceptedContentLength = (maxContentLength == null) || + (fileResource.contentLength() == null) || + (fileResource.contentLength()!! <= maxContentLength) if (acceptedContentLength && FileResourceInternalAccessor.isStored(fileResource)) { val responseBody = apiCallExecutor.executeObjectCall(download(value)) diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt index 137886a055..49fa7398d0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt @@ -28,7 +28,6 @@ package org.hisp.dhis.android.core.fileresource.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.dataelement.DataElement @@ -36,10 +35,10 @@ import org.hisp.dhis.android.core.dataelement.DataElementTableInfo import org.hisp.dhis.android.core.datavalue.DataValue import org.hisp.dhis.android.core.datavalue.DataValueTableInfo import org.hisp.dhis.android.core.datavalue.internal.DataValueStore -import org.hisp.dhis.android.core.fileresource.FileResourceValueType import org.hisp.dhis.android.core.trackedentity.* import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityAttributeValueStore import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityDataValueStore +import javax.inject.Inject @Reusable internal class FileResourceDownloadCallHelper @Inject constructor( @@ -53,18 +52,15 @@ internal class FileResourceDownloadCallHelper @Inject constructor( fun getMissingTrackerAttributeValues( params: FileResourceDownloadParams, existingFileResources: List - ): List { - // TODO Download files for TrackedEntityAttributes - val valueTypes = params.valueTypes.filter { it == FileResourceValueType.IMAGE } - - val attributeUidsWhereClause = WhereClauseBuilder() - .appendInKeyEnumValues(TrackedEntityAttributeTableInfo.Columns.VALUE_TYPE, valueTypes) + ): List { + val attributesWhereClause = WhereClauseBuilder() + .appendInKeyEnumValues(TrackedEntityAttributeTableInfo.Columns.VALUE_TYPE, params.valueTypes) .build() - val trackedEntityAttributeUids = trackedEntityAttributeStore.selectUidsWhere(attributeUidsWhereClause) + val trackedEntityAttributes = trackedEntityAttributeStore.selectWhere(attributesWhereClause) val attributeValuesWhereClause = WhereClauseBuilder() .appendInKeyStringValues( TrackedEntityAttributeValueTableInfo.Columns.TRACKED_ENTITY_ATTRIBUTE, - trackedEntityAttributeUids + trackedEntityAttributes.map { it.uid() } ) .appendNotInKeyStringValues( TrackedEntityAttributeValueTableInfo.Columns.VALUE, @@ -72,6 +68,10 @@ internal class FileResourceDownloadCallHelper @Inject constructor( ) .build() return trackedEntityAttributeValueStore.selectWhere(attributeValuesWhereClause) + .map { av -> + val type = trackedEntityAttributes.find { it.uid() == av.trackedEntityAttribute() }!!.valueType()!! + MissingTrackerAttributeValue(av, type) + } } fun getMissingTrackerDataValues( diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceService.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceService.kt index 1579822878..999e01245b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceService.kt @@ -43,12 +43,18 @@ internal interface FileResourceService { fun getFileResource(@Path(FILE_RESOURCE) fileResource: String): Call @GET("$TRACKED_ENTITY_INSTANCES/{$TRACKED_ENTITY_INSTANCE}/{$TRACKED_ENTITY_ATTRIBUTE}/image") - fun getFileFromTrackedEntityAttribute( + fun getImageFromTrackedEntityAttribute( @Path(TRACKED_ENTITY_INSTANCE) trackedEntityInstanceUid: String, @Path(TRACKED_ENTITY_ATTRIBUTE) trackedEntityAttributeUid: String, @Query("dimension") dimension: String ): Call + @GET("$TRACKED_ENTITY_INSTANCES/{$TRACKED_ENTITY_INSTANCE}/{$TRACKED_ENTITY_ATTRIBUTE}/file") + fun getFileFromTrackedEntityAttribute( + @Path(TRACKED_ENTITY_INSTANCE) trackedEntityInstanceUid: String, + @Path(TRACKED_ENTITY_ATTRIBUTE) trackedEntityAttributeUid: String + ): Call + @GET("$EVENTS/files") fun getFileFromEventValue( @Query("eventUid") eventUid: String, diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/MissingTrackerAttributeValue.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/MissingTrackerAttributeValue.kt new file mode 100644 index 0000000000..20b1e064bf --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/MissingTrackerAttributeValue.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.hisp.dhis.android.core.fileresource.internal + +import org.hisp.dhis.android.core.common.ValueType +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue + +internal data class MissingTrackerAttributeValue( + val value: TrackedEntityAttributeValue, + val valueType: ValueType +) \ No newline at end of file From 403386c44c69834e20028cceae7dd0e872bf9fde Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 12 Dec 2022 16:49:09 +1100 Subject: [PATCH 003/201] [ANDROSDK-1531] Update version to 1.8.0-SNAPSHOT --- core/build.gradle | 4 ++-- core/gradle.properties | 4 ++-- .../fileresource/internal/FileResourceDownloadCall.kt | 6 +++--- .../internal/FileResourceDownloadCallHelper.kt | 2 +- .../fileresource/internal/MissingTrackerAttributeValue.kt | 2 +- .../core/sms/domain/converter/internal/Converter.java | 2 +- .../dhis/android/core/systeminfo/DHISVersionManager.kt | 3 ++- .../core/systeminfo/internal/DHISVersionManagerImpl.kt | 7 ++++--- .../android/core/systeminfo/internal/SystemInfoCall.kt | 8 ++++---- .../core/systeminfo/internal/SystemInfoModuleImpl.kt | 2 +- .../core/systeminfo/internal/SystemInfoCallShould.kt | 2 +- 11 files changed, 22 insertions(+), 20 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 1f0abd6344..96218b62aa 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -43,8 +43,8 @@ ext { buildToolsVersion: "30.0.2", minSdkVersion : 21, targetSdkVersion : 31, - versionCode : 271, - versionName : "1.7.1-SNAPSHOT" + versionCode : 280, + versionName : "1.8.0-SNAPSHOT" ] libraries = [ diff --git a/core/gradle.properties b/core/gradle.properties index 78f863ad94..1ec3049866 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.7.1-SNAPSHOT -VERSION_CODE=271 +VERSION_NAME=1.8.0-SNAPSHOT +VERSION_CODE=280 GROUP=org.hisp.dhis diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCall.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCall.kt index c456dc528c..1945ca3155 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCall.kt @@ -32,6 +32,7 @@ import android.util.Log import dagger.Reusable import io.reactivex.Observable import io.reactivex.ObservableEmitter +import javax.inject.Inject import okhttp3.ResponseBody import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor @@ -45,7 +46,6 @@ import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.android.core.fileresource.* import org.hisp.dhis.android.core.maintenance.D2Error import retrofit2.Call -import javax.inject.Inject @Reusable internal class FileResourceDownloadCall @Inject constructor( @@ -171,8 +171,8 @@ internal class FileResourceDownloadCall @Inject constructor( val fileResource = apiCallExecutor.executeObjectCall(fileResourceService.getFileResource(uid)) val acceptedContentLength = (maxContentLength == null) || - (fileResource.contentLength() == null) || - (fileResource.contentLength()!! <= maxContentLength) + (fileResource.contentLength() == null) || + (fileResource.contentLength()!! <= maxContentLength) if (acceptedContentLength && FileResourceInternalAccessor.isStored(fileResource)) { val responseBody = apiCallExecutor.executeObjectCall(download(value)) diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt index 49fa7398d0..1e5be5cd58 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt @@ -28,6 +28,7 @@ package org.hisp.dhis.android.core.fileresource.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.dataelement.DataElement @@ -38,7 +39,6 @@ import org.hisp.dhis.android.core.datavalue.internal.DataValueStore import org.hisp.dhis.android.core.trackedentity.* import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityAttributeValueStore import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityDataValueStore -import javax.inject.Inject @Reusable internal class FileResourceDownloadCallHelper @Inject constructor( diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/MissingTrackerAttributeValue.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/MissingTrackerAttributeValue.kt index 20b1e064bf..3f7ee8c641 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/MissingTrackerAttributeValue.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/MissingTrackerAttributeValue.kt @@ -34,4 +34,4 @@ import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue internal data class MissingTrackerAttributeValue( val value: TrackedEntityAttributeValue, val valueType: ValueType -) \ No newline at end of file +) diff --git a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/Converter.java b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/Converter.java index 1b4b9e51e0..1c8de22625 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/Converter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/sms/domain/converter/internal/Converter.java @@ -83,7 +83,7 @@ private Single convert(@NonNull P dataItem, SMSMetadata metadata, String if (smsVersion == null) { throw D2Error.builder() .errorCode(D2ErrorCode.SMS_NOT_SUPPORTED) - .errorDescription("SMS is not supported in version " + dhisVersionManager. getPatchVersion()) + .errorDescription("SMS is not supported in version " + dhisVersionManager.getPatchVersion()) .errorComponent(D2ErrorComponent.SDK) .build(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt index 60f7b1fcff..f4e704b448 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.systeminfo +@Suppress("FunctionNaming", "TooManyFunctions") interface DHISVersionManager { fun getVersion(): DHISVersion fun getPatchVersion(): DHISPatchVersion? @@ -61,4 +62,4 @@ interface DHISVersionManager { * @return True if current version is greater or equal than the parameter. */ fun isGreaterOrEqualThan(version: DHISVersion): Boolean -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt index 34fd0326ba..0fa08f078e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt @@ -27,12 +27,13 @@ */ package org.hisp.dhis.android.core.systeminfo.internal -import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore -import org.hisp.dhis.android.core.systeminfo.* import javax.inject.Inject import javax.inject.Singleton +import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.systeminfo.* @Singleton +@Suppress("TooManyFunctions", "TooGenericExceptionThrown") class DHISVersionManagerImpl @Inject internal constructor( private val systemInfoStore: ObjectWithoutUidStore ) : DHISVersionManager { @@ -53,7 +54,7 @@ class DHISVersionManagerImpl @Inject internal constructor( return patchVersion ?: systemInfoStore.selectFirst()?.let { systemInfo -> systemInfo.version()?.let { DHISPatchVersion.getValue(it) } - .also { patch -> patchVersion = patch} + .also { patch -> patchVersion = patch } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.kt index d74a5cefbd..0801e60e87 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCall.kt @@ -29,6 +29,7 @@ package org.hisp.dhis.android.core.systeminfo.internal import dagger.Reusable import io.reactivex.Completable +import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor import org.hisp.dhis.android.core.arch.call.internal.CompletableProvider import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter @@ -42,7 +43,6 @@ import org.hisp.dhis.android.core.resource.internal.ResourceHandler import org.hisp.dhis.android.core.systeminfo.DHISVersion.Companion.allowedVersionsAsStr import org.hisp.dhis.android.core.systeminfo.DHISVersion.Companion.isAllowedVersion import org.hisp.dhis.android.core.systeminfo.SystemInfo -import javax.inject.Inject @Reusable class SystemInfoCall @Inject internal constructor( @@ -64,9 +64,9 @@ class SystemInfoCall @Inject internal constructor( .errorComponent(D2ErrorComponent.SDK) .errorCode(D2ErrorCode.INVALID_DHIS_VERSION) .errorDescription( - "Server DHIS version (" + version + ") not valid. " - + "Allowed versions: " - + CollectionsHelper.commaAndSpaceSeparatedArrayValues(allowedVersionsAsStr()) + "Server DHIS version (" + version + ") not valid. " + + "Allowed versions: " + + CollectionsHelper.commaAndSpaceSeparatedArrayValues(allowedVersionsAsStr()) ) .build() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.kt index 1f3575027b..34c1596da2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoModuleImpl.kt @@ -28,11 +28,11 @@ package org.hisp.dhis.android.core.systeminfo.internal import dagger.Reusable +import javax.inject.Inject import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.systeminfo.SystemInfo import org.hisp.dhis.android.core.systeminfo.SystemInfoModule -import javax.inject.Inject @Reusable class SystemInfoModuleImpl @Inject internal constructor( diff --git a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.kt b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.kt index 1e76b3f979..b8feb03d16 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/SystemInfoCallShould.kt @@ -30,6 +30,7 @@ package org.hisp.dhis.android.core.systeminfo.internal import com.google.common.truth.Truth.assertThat import com.nhaarman.mockitokotlin2.* import io.reactivex.Single +import java.util.* import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor import org.hisp.dhis.android.core.arch.api.fields.internal.Fields import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter @@ -45,7 +46,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import java.util.* @RunWith(JUnit4::class) class SystemInfoCallShould { From 4f5b7f83ae1dba8bbd5b0f3f214a85455f21c523 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 12 Dec 2022 17:01:24 +1100 Subject: [PATCH 004/201] [ANDROSDK-1531] Restrict tracker attribute images to >= 2.40 --- .../internal/FileResourceDownloadCallHelper.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt index 1e5be5cd58..c18d62975f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt @@ -36,6 +36,9 @@ import org.hisp.dhis.android.core.dataelement.DataElementTableInfo import org.hisp.dhis.android.core.datavalue.DataValue import org.hisp.dhis.android.core.datavalue.DataValueTableInfo import org.hisp.dhis.android.core.datavalue.internal.DataValueStore +import org.hisp.dhis.android.core.fileresource.FileResourceValueType +import org.hisp.dhis.android.core.systeminfo.DHISVersion +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.trackedentity.* import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityAttributeValueStore import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityDataValueStore @@ -46,15 +49,22 @@ internal class FileResourceDownloadCallHelper @Inject constructor( private val trackedEntityAttributeValueStore: TrackedEntityAttributeValueStore, private val trackedEntityAttributeStore: IdentifiableObjectStore, private val trackedEntityDataValueStore: TrackedEntityDataValueStore, - private val dataValueStore: DataValueStore + private val dataValueStore: DataValueStore, + private val dhisVersionManager: DHISVersionManager ) { fun getMissingTrackerAttributeValues( params: FileResourceDownloadParams, existingFileResources: List ): List { + val valueTypes = + if (dhisVersionManager.isGreaterOrEqualThan(DHISVersion.V2_40)) { + params.valueTypes + } else { + params.valueTypes.filter { it == FileResourceValueType.IMAGE } + } val attributesWhereClause = WhereClauseBuilder() - .appendInKeyEnumValues(TrackedEntityAttributeTableInfo.Columns.VALUE_TYPE, params.valueTypes) + .appendInKeyEnumValues(TrackedEntityAttributeTableInfo.Columns.VALUE_TYPE, valueTypes) .build() val trackedEntityAttributes = trackedEntityAttributeStore.selectWhere(attributesWhereClause) val attributeValuesWhereClause = WhereClauseBuilder() From e16d51c65d62969a551d98cc5cdb0ccab54a29ad Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 12 Dec 2022 17:06:22 +1100 Subject: [PATCH 005/201] [ANDROSDK-1531] Filter by actual ValueType --- .../internal/FileResourceDownloadCallHelper.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt index c18d62975f..6fbbbcee3d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/fileresource/internal/FileResourceDownloadCallHelper.kt @@ -57,14 +57,14 @@ internal class FileResourceDownloadCallHelper @Inject constructor( params: FileResourceDownloadParams, existingFileResources: List ): List { - val valueTypes = + val fileTypes = if (dhisVersionManager.isGreaterOrEqualThan(DHISVersion.V2_40)) { params.valueTypes } else { params.valueTypes.filter { it == FileResourceValueType.IMAGE } } val attributesWhereClause = WhereClauseBuilder() - .appendInKeyEnumValues(TrackedEntityAttributeTableInfo.Columns.VALUE_TYPE, valueTypes) + .appendInKeyEnumValues(TrackedEntityAttributeTableInfo.Columns.VALUE_TYPE, fileTypes.map { it.valueType }) .build() val trackedEntityAttributes = trackedEntityAttributeStore.selectWhere(attributesWhereClause) val attributeValuesWhereClause = WhereClauseBuilder() @@ -89,7 +89,7 @@ internal class FileResourceDownloadCallHelper @Inject constructor( existingFileResources: List ): List { val dataElementUidsWhereClause = WhereClauseBuilder() - .appendInKeyEnumValues(DataElementTableInfo.Columns.VALUE_TYPE, params.valueTypes) + .appendInKeyEnumValues(DataElementTableInfo.Columns.VALUE_TYPE, params.valueTypes.map { it.valueType }) .appendKeyStringValue(DataElementTableInfo.Columns.DOMAIN_TYPE, "TRACKER") .build() val dataElementUids = dataElementStore.selectUidsWhere(dataElementUidsWhereClause) @@ -105,7 +105,7 @@ internal class FileResourceDownloadCallHelper @Inject constructor( existingFileResources: List ): List { val dataElementUidsWhereClause = WhereClauseBuilder() - .appendInKeyEnumValues(DataElementTableInfo.Columns.VALUE_TYPE, params.valueTypes) + .appendInKeyEnumValues(DataElementTableInfo.Columns.VALUE_TYPE, params.valueTypes.map { it.valueType }) .appendKeyStringValue(DataElementTableInfo.Columns.DOMAIN_TYPE, "AGGREGATE") .build() val dataElementUids = dataElementStore.selectUidsWhere(dataElementUidsWhereClause) From a09644df22934638df667c4450f2e10f5990d299 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 13 Dec 2022 11:12:02 +1100 Subject: [PATCH 006/201] [ANDROSDK-1531] Add test for FileResource call --- ...ISVersionsManagerRealIntegrationShould.kt} | 45 ++-- ... SystemInfoModuleMockIntegrationShould.kt} | 33 +-- .../BaseMockIntegrationTestFullDispatcher.kt | 5 + .../FileResourceAddMockIntegrationShould.kt | 78 +++++++ ...ectionRepositoryMockIntegrationShould.java | 192 ------------------ ...llectionRepositoryMockIntegrationShould.kt | 110 ++++++++++ ...ectionRepositoryMockIntegrationShould.java | 4 + ...ectionRepositoryMockIntegrationShould.java | 2 +- ...ectionRepositoryMockIntegrationShould.java | 6 +- .../internal/EventPostPayloadGenerator.kt | 3 +- .../core/mockwebserver/Dhis2MockServer.java | 8 +- .../core/systeminfo/DHISVersionManager.kt | 15 -- .../internal/DHISVersionManagerImpl.kt | 58 +----- .../OldTrackerImporterPayloadGenerator.kt | 3 +- .../tracked_entity_attribute_value_image.png | Bin 0 -> 1141 bytes ...entity_attribute_value_image_resource.json | 39 ++++ .../tracked_entity_attributes.json | 8 +- .../tracked_entity_instances.json | 12 +- .../internal/DHISVersionManagerShould.kt | 40 +++- 19 files changed, 344 insertions(+), 317 deletions(-) rename core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/{DHISVersionsManagerRealIntegrationShould.java => DHISVersionsManagerRealIntegrationShould.kt} (63%) rename core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/{SystemInfoModuleMockIntegrationShould.java => SystemInfoModuleMockIntegrationShould.kt} (69%) create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceAddMockIntegrationShould.kt delete mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.java create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.kt create mode 100644 core/src/sharedTest/resources/trackedentity/tracked_entity_attribute_value_image.png create mode 100644 core/src/sharedTest/resources/trackedentity/tracked_entity_attribute_value_image_resource.json diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/DHISVersionsManagerRealIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/DHISVersionsManagerRealIntegrationShould.kt similarity index 63% rename from core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/DHISVersionsManagerRealIntegrationShould.java rename to core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/DHISVersionsManagerRealIntegrationShould.kt index 4da56d5ec7..1209fc0833 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/DHISVersionsManagerRealIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/DHISVersionsManagerRealIntegrationShould.kt @@ -25,36 +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.systeminfo -package org.hisp.dhis.android.core.systeminfo; +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.BaseRealIntegrationTest +import org.hisp.dhis.android.core.data.server.RealServerMother -import static com.google.common.truth.Truth.assertThat; +class DHISVersionsManagerRealIntegrationShould : BaseRealIntegrationTest() { + // @Test + fun return_2_30_version_when_connecting_to_2_30_server() { + d2.wipeModule().wipeEverything() + val versionManager = d2.systemInfoModule().versionManager() + d2.userModule().logIn(username, password, RealServerMother.url2_30).blockingGet() -import org.hisp.dhis.android.core.BaseRealIntegrationTest; -import org.hisp.dhis.android.core.data.server.RealServerMother; - -public class DHISVersionsManagerRealIntegrationShould extends BaseRealIntegrationTest { - //@Test - public void return_2_30_version_when_connecting_to_2_30_server() throws Exception { - d2.wipeModule().wipeEverything(); - - DHISVersionManager versionManager = d2.systemInfoModule().versionManager(); - - d2.userModule().logIn(username, password, RealServerMother.url2_30).blockingGet(); - assertThat(versionManager.getVersion()).isEqualTo(DHISVersion.V2_30); - assertThat(versionManager.is2_30()).isTrue(); - assertThat(versionManager.is2_31()).isFalse(); + assertThat(versionManager.getVersion()).isEqualTo(DHISVersion.V2_30) + assertThat(versionManager.getVersion()).isNotEqualTo(DHISVersion.V2_31) } - //@Test - public void return_2_31_version_when_connecting_to_2_31_server() throws Exception { - d2.wipeModule().wipeEverything(); - - DHISVersionManager versionManager = d2.systemInfoModule().versionManager(); + // @Test + @Throws(Exception::class) + fun return_2_31_version_when_connecting_to_2_31_server() { + d2.wipeModule().wipeEverything() + val versionManager = d2.systemInfoModule().versionManager() + d2.userModule().logIn(username, password, RealServerMother.url2_31).blockingGet() - d2.userModule().logIn(username, password, RealServerMother.url2_31).blockingGet(); - assertThat(versionManager.getVersion()).isEqualTo(DHISVersion.V2_31); - assertThat(versionManager.is2_30()).isFalse(); - assertThat(versionManager.is2_31()).isTrue(); + assertThat(versionManager.getVersion()).isNotEqualTo(DHISVersion.V2_30) + assertThat(versionManager.getVersion()).isEqualTo(DHISVersion.V2_31) } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModuleMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModuleMockIntegrationShould.kt similarity index 69% rename from core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModuleMockIntegrationShould.java rename to core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModuleMockIntegrationShould.kt index 2ad06b2023..a9d82157ce 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModuleMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/systeminfo/SystemInfoModuleMockIntegrationShould.kt @@ -25,23 +25,26 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.systeminfo -package org.hisp.dhis.android.core.systeminfo; +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Test +import org.junit.runner.RunWith -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; - -@RunWith(D2JunitRunner.class) -public class SystemInfoModuleMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { +@RunWith(D2JunitRunner::class) +class SystemInfoModuleMockIntegrationShould : BaseMockIntegrationTestFullDispatcher() { + @Test + fun allow_access_to_system_info_user() { + val systemInfo = d2.systemInfoModule().systemInfo().blockingGet() + assertThat(systemInfo.version()).isEqualTo("2.38") + assertThat(systemInfo.systemName()).isEqualTo("DHIS 2 Demo - Sierra Leone") + } @Test - public void allow_access_to_system_info_user() { - SystemInfo systemInfo = d2.systemInfoModule().systemInfo().blockingGet(); - assertThat(systemInfo.version()).isEqualTo("2.38"); - assertThat(systemInfo.systemName()).isEqualTo("DHIS 2 Demo - Sierra Leone"); + fun allow_access_to_version_manager() { + val version = d2.systemInfoModule().versionManager().getVersion() + assertThat(version).isEqualTo(DHISVersion.V2_38) } -} \ No newline at end of file +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt index ef2b8ea63b..995c5d4aff 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt @@ -56,6 +56,7 @@ abstract class BaseMockIntegrationTestFullDispatcher : BaseMockIntegrationTest() downloadTrackedEntityInstances() downloadEvents() downloadAggregatedData() + downloadFileResources() storeSomeD2Errors() storeSomeConflicts() storeSomeKeyValuesInLocalDataStore() @@ -94,6 +95,10 @@ abstract class BaseMockIntegrationTestFullDispatcher : BaseMockIntegrationTest() d2.aggregatedModule().data().blockingDownload() } + private fun downloadFileResources() { + d2.fileResourceModule().fileResourceDownloader().blockingDownload() + } + private fun storeSomeD2Errors() { val d2ErrorStore = D2ErrorStore.create(databaseAdapter) d2ErrorStore.insert(D2ErrorSamples.get()) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceAddMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceAddMockIntegrationShould.kt new file mode 100644 index 0000000000..78575721c2 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceAddMockIntegrationShould.kt @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.testapp.fileresource + +import androidx.test.platform.app.InstrumentationRegistry +import com.google.common.truth.Truth.assertThat +import java.io.File +import java.io.InputStream +import org.hisp.dhis.android.core.arch.helpers.FileResourceDirectoryHelper.getFileResourceDirectory +import org.hisp.dhis.android.core.data.fileresource.RandomGeneratedInputStream +import org.hisp.dhis.android.core.fileresource.internal.FileResourceStoreImpl +import org.hisp.dhis.android.core.fileresource.internal.FileResourceUtil.writeInputStream +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyDispatcher +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class FileResourceAddMockIntegrationShould : BaseMockIntegrationTestEmptyDispatcher() { + + @Test + fun add_fileResources_to_the_repository() { + val file = storeFile() + assertThat(file.exists()).isTrue() + + val fileResources1 = d2.fileResourceModule().fileResources().blockingGet() + + assertThat(fileResources1.size).isEqualTo(0) + + val fileResourceUid = d2.fileResourceModule().fileResources().blockingAdd(file) + val fileResources2 = d2.fileResourceModule().fileResources().blockingGet() + assertThat(fileResources2.size).isEqualTo(1) + + val fileResource = d2.fileResourceModule().fileResources() + .uid(fileResourceUid) + .blockingGet() + + assertThat(fileResource.uid()).isEqualTo(fileResourceUid) + + val savedFile = File(fileResource.path()) + assertThat(savedFile.exists()).isTrue() + + savedFile.delete() + FileResourceStoreImpl.create(databaseAdapter).delete(fileResource.uid()!!) + } + + private fun storeFile(): File { + val inputStream: InputStream = RandomGeneratedInputStream(1024) + val context = InstrumentationRegistry.getInstrumentation().context + val destinationFile = File(getFileResourceDirectory(context), "file1.png") + return writeInputStream(inputStream, destinationFile, 1024) + } +} 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 deleted file mode 100644 index 0e501dca7b..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.testapp.fileresource; - -import android.content.Context; - -import androidx.test.platform.app.InstrumentationRegistry; - -import org.apache.commons.io.FileUtils; -import org.hisp.dhis.android.core.arch.helpers.FileResourceDirectoryHelper; -import org.hisp.dhis.android.core.common.BaseIdentifiableObject; -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.data.fileresource.RandomGeneratedInputStream; -import org.hisp.dhis.android.core.fileresource.FileResource; -import org.hisp.dhis.android.core.fileresource.FileResourceTableInfo; -import org.hisp.dhis.android.core.fileresource.internal.FileResourceUtil; -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.hisp.dhis.android.core.wipe.internal.TableWiper; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.text.ParseException; -import java.util.Date; -import java.util.List; - -import static com.google.common.truth.Truth.assertThat; - -@RunWith(D2JunitRunner.class) -public class FileResourceCollectionRepositoryMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { - - @Test - public void find_all() throws D2Error, IOException { - cleanFileResources(); - d2.fileResourceModule().fileResources().blockingAdd(getFile()); - List fileResources = - d2.fileResourceModule().fileResources() - .blockingGet(); - - assertThat(fileResources.size()).isEqualTo(1); - } - - @Test - public void filter_by_uid() throws D2Error, IOException { - cleanFileResources(); - String fileUid = d2.fileResourceModule().fileResources().blockingAdd(getFile()); - List fileResources = - d2.fileResourceModule().fileResources() - .byUid().eq(fileUid) - .blockingGet(); - - assertThat(fileResources.size()).isEqualTo(1); - } - - @Test - public void filter_by_name() throws D2Error, IOException { - cleanFileResources(); - String fileUid = d2.fileResourceModule().fileResources().blockingAdd(getFile()); - List fileResources = - d2.fileResourceModule().fileResources() - .byName().eq(fileUid+ ".png") - .blockingGet(); - - assertThat(fileResources.size()).isEqualTo(1); - } - @Test - public void filter_by_last_updated() throws D2Error, IOException, ParseException { - cleanFileResources(); - String BEFORE_DATE = "2007-12-24T12:24:25.203"; - Date created = BaseIdentifiableObject.DATE_FORMAT.parse(BEFORE_DATE); - - d2.fileResourceModule().fileResources().blockingAdd(getFile()); - List fileResources = - d2.fileResourceModule().fileResources() - .byLastUpdated().after(created) - .blockingGet(); - - assertThat(fileResources.size()).isEqualTo(1); - } - - @Test - public void filter_by_content_type() throws D2Error, IOException { - cleanFileResources(); - d2.fileResourceModule().fileResources().blockingAdd(getFile()); - List fileResources = - d2.fileResourceModule().fileResources() - .byContentType().eq("image/png") - .blockingGet(); - - assertThat(fileResources.size()).isEqualTo(1); - } - - @Test - public void filter_by_path() throws D2Error, IOException { - cleanFileResources(); - d2.fileResourceModule().fileResources().blockingAdd(getFile()); - List fileResources = - d2.fileResourceModule().fileResources() - .byPath().like("files/sdk_resources") - .blockingGet(); - - assertThat(fileResources.size()).isEqualTo(1); - } - - @Test - public void filter_by_state() throws D2Error, IOException { - cleanFileResources(); - d2.fileResourceModule().fileResources().blockingAdd(getFile()); - List fileResources = - d2.fileResourceModule().fileResources() - .bySyncState().eq(State.TO_POST) - .blockingGet(); - - assertThat(fileResources.size()).isEqualTo(1); - } - - @Test - public void filter_by_content_length() throws D2Error, IOException { - cleanFileResources(); - d2.fileResourceModule().fileResources().blockingAdd(getFile()); - List fileResources = - d2.fileResourceModule().fileResources() - .byContentLength().eq(1024L) - .blockingGet(); - - assertThat(fileResources.size()).isEqualTo(1); - } - - @Test - public void add_fileResources_to_the_repository() throws D2Error, IOException { - cleanFileResources(); - List fileResources1 = d2.fileResourceModule().fileResources().blockingGet(); - assertThat(fileResources1.size()).isEqualTo(0); - - File file = getFile(); - assertThat(file.exists()).isTrue(); - - String fileResourceUid = d2.fileResourceModule().fileResources().blockingAdd(file); - - List fileResources2 = d2.fileResourceModule().fileResources().blockingGet(); - assertThat(fileResources2.size()).isEqualTo(1); - - FileResource fileResource = d2.fileResourceModule().fileResources().uid(fileResourceUid).blockingGet(); - assertThat(fileResource.uid()).isEqualTo(fileResourceUid); - - File savedFile = new File(fileResource.path()); - assertThat(savedFile.exists()).isTrue(); - } - - private File getFile() { - InputStream inputStream = new RandomGeneratedInputStream(1024); - Context context = InstrumentationRegistry.getInstrumentation().getContext(); - File destinationFile = new File(FileResourceDirectoryHelper.getFileResourceDirectory(context), "file1.png"); - return FileResourceUtil.writeInputStream(inputStream, destinationFile, 1024); - } - - private void cleanFileResources() throws IOException { - Context context = InstrumentationRegistry.getInstrumentation().getContext(); - FileUtils.cleanDirectory(FileResourceDirectoryHelper.getFileResourceDirectory(context)); - new TableWiper(databaseAdapter).wipeTable(FileResourceTableInfo.TABLE_INFO); - } -} \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.kt new file mode 100644 index 0000000000..459581b052 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.kt @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.testapp.fileresource + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.arch.helpers.DateUtils +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 +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class FileResourceCollectionRepositoryMockIntegrationShould : BaseMockIntegrationTestFullDispatcher() { + @Test + fun find_all() { + val fileResources = d2.fileResourceModule().fileResources() + .blockingGet() + + assertThat(fileResources.size).isEqualTo(1) + } + + @Test + fun filter_by_uid() { + val fileResources = d2.fileResourceModule().fileResources() + .byUid().eq("befryEfXge5") + .blockingGet() + + assertThat(fileResources.size).isEqualTo(1) + } + + @Test + fun filter_by_name() { + val fileResources = d2.fileResourceModule().fileResources() + .byName().eq("profile.png") + .blockingGet() + + assertThat(fileResources.size).isEqualTo(1) + } + + @Test + fun filter_by_last_updated() { + val fileResources = d2.fileResourceModule().fileResources() + .byLastUpdated().after(DateUtils.DATE_FORMAT.parse("2007-12-24T12:24:25.203")) + .blockingGet() + + assertThat(fileResources.size).isEqualTo(1) + } + + @Test + fun filter_by_content_type() { + val fileResources = d2.fileResourceModule().fileResources() + .byContentType().eq("image/png") + .blockingGet() + + assertThat(fileResources.size).isEqualTo(1) + } + + @Test + fun filter_by_path() { + val fileResources = d2.fileResourceModule().fileResources() + .byPath().like("files/sdk_resources") + .blockingGet() + + assertThat(fileResources.size).isEqualTo(1) + } + + @Test + fun filter_by_state() { + val fileResources = d2.fileResourceModule().fileResources() + .bySyncState().eq(State.SYNCED) + .blockingGet() + + assertThat(fileResources.size).isEqualTo(1) + } + + @Test + fun filter_by_content_length() { + val fileResources = d2.fileResourceModule().fileResources() + .byContentLength().eq(9270L) + .blockingGet() + + assertThat(fileResources.size).isEqualTo(1) + } +} 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 173c6ccb53..5499a1070b 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 @@ -104,6 +104,10 @@ public void filter_d2_error_by_created() { .build(); List d2Errors = d2.maintenanceModule().d2Errors() .byCreated().inPeriods(Lists.newArrayList(todayPeriod)).blockingGet(); + + for (D2Error error : d2Errors) { + System.out.println("AAAAA " + error.errorDescription()); + } assertThat(d2Errors.size()).isEqualTo(2); } } \ No newline at end of file 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 9e0626c38e..848dde12f0 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 @@ -98,7 +98,7 @@ public void filter_by_tracked_entity_instance() { d2.trackedEntityModule().trackedEntityAttributeValues() .byTrackedEntityInstance().eq("nWrB0TfWlvh") .blockingGet(); - assertThat(trackedEntityAttributeValues.size()).isEqualTo(1); + assertThat(trackedEntityAttributeValues.size()).isEqualTo(2); } @Test 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 b6bf1420f8..c9c42f86c3 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 @@ -69,11 +69,11 @@ public void include_tracked_entity_attribute_values_as_children() { TrackedEntityInstance tei = d2.trackedEntityModule().trackedEntityInstances() .withTrackedEntityAttributeValues().uid("nWrB0TfWlvD").blockingGet(); - assertThat(tei.trackedEntityAttributeValues().size()).isEqualTo(2); + assertThat(tei.trackedEntityAttributeValues().size()).isEqualTo(1); - assertThat(tei.trackedEntityAttributeValues().get(0).trackedEntityAttribute()).isEqualTo("aejWyOfXge6"); - assertThat(tei.trackedEntityAttributeValues().get(0).value()).isEqualTo("123456"); + assertThat(tei.trackedEntityAttributeValues().get(0).trackedEntityAttribute()).isEqualTo("cejWyOfXge6"); + assertThat(tei.trackedEntityAttributeValues().get(0).value()).isEqualTo("654321"); } @Test diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGenerator.kt b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGenerator.kt index a4661ced5d..cb2b48af53 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGenerator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/event/internal/EventPostPayloadGenerator.kt @@ -30,6 +30,7 @@ package org.hisp.dhis.android.core.event.internal import dagger.Reusable import javax.inject.Inject import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.systeminfo.DHISVersion import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityDataValueStore @@ -48,7 +49,7 @@ internal class EventPostPayloadGenerator @Inject internal constructor( val eventBuilder = event.toBuilder() .trackedEntityDataValues(dataValueMap[event.uid()]) .notes(notes.filter { it.event() == event.uid() }) - if (versionManager.is2_30()) { + if (versionManager.getVersion() == DHISVersion.V2_30) { eventBuilder.geometry(null) } eventBuilder.build() 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 ceb67b50e3..0afaf49e20 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 @@ -97,6 +97,8 @@ public class Dhis2MockServer { private static final String LEGEND_SETS_JSON = "legendset/legend_sets.json"; private static final String TRACKED_ENTITY_INSTANCES_JSON = "trackedentity/tracked_entity_instances.json"; private static final String DATA_VALUES_JSON = "datavalue/data_values.json"; + private static final String TRACKED_ENTITY_IMAGE = "trackedentity/tracked_entity_attribute_value_image.png"; + private static final String FILE_RESOURCE = "trackedentity/tracked_entity_attribute_value_image_resource.json"; private static final String DATA_SET_COMPLETE_REGISTRATIONS_JSON = "dataset/data_set_complete_registrations.json"; private static final String DATA_APPROVALS_MULTIPLE_JSON = "dataapproval/data_approvals_multiple.json"; private static final String ORGANISATION_UNITS_JSON = "organisationunit/organisation_units.json"; @@ -270,10 +272,14 @@ public MockResponse dispatch(RecordedRequest request) { return createMockResponse(RESERVE_VALUES_JSON); } else if (path.startsWith("/api/metadata")) { return createMockResponse(SMS_METADATA); + } else if (path.startsWith("/api/fileResources")) { + return createMockResponse(FILE_RESOURCE); + } else if (path.startsWith("/api/trackedEntityInstances/nWrB0TfWlvh/aejWyOfXge6/image")) { + return createMockResponse(TRACKED_ENTITY_IMAGE); } else { return new MockResponse() .setResponseCode(404) - .setBody("Path not present in Dhis2MockServer dispatcher"); + .setBody("Path not present in Dhis2MockServer dispatcher: " + path); } } }; diff --git a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt index f4e704b448..713eb17d88 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/DHISVersionManager.kt @@ -27,26 +27,11 @@ */ package org.hisp.dhis.android.core.systeminfo -@Suppress("FunctionNaming", "TooManyFunctions") interface DHISVersionManager { fun getVersion(): DHISVersion fun getPatchVersion(): DHISPatchVersion? fun getSmsVersion(): SMSVersion? - @Deprecated("") - fun is2_29(): Boolean - fun is2_30(): Boolean - fun is2_31(): Boolean - fun is2_32(): Boolean - fun is2_33(): Boolean - fun is2_34(): Boolean - fun is2_35(): Boolean - fun is2_36(): Boolean - fun is2_37(): Boolean - fun is2_38(): Boolean - fun is2_39(): Boolean - fun is2_40(): Boolean - /** * 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.kt b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt index 0fa08f078e..c34b617d61 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerImpl.kt @@ -30,10 +30,12 @@ package org.hisp.dhis.android.core.systeminfo.internal import javax.inject.Inject import javax.inject.Singleton import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import org.hisp.dhis.android.core.maintenance.D2ErrorComponent import org.hisp.dhis.android.core.systeminfo.* @Singleton -@Suppress("TooManyFunctions", "TooGenericExceptionThrown") class DHISVersionManagerImpl @Inject internal constructor( private val systemInfoStore: ObjectWithoutUidStore ) : DHISVersionManager { @@ -47,7 +49,11 @@ class DHISVersionManagerImpl @Inject internal constructor( systemInfo.version()?.let { DHISVersion.getValue(it) } .also { dhisVersion -> version = dhisVersion } } - ?: throw RuntimeException("Cannot get DHIS2 version") + ?: throw D2Error.builder() + .errorComponent(D2ErrorComponent.SDK) + .errorCode(D2ErrorCode.INVALID_DHIS_VERSION) + .errorDescription("Invalid DHIS version") + .build() } override fun getPatchVersion(): DHISPatchVersion? { @@ -66,54 +72,6 @@ class DHISVersionManagerImpl @Inject internal constructor( } } - override fun is2_29(): Boolean { - return getVersion() === DHISVersion.V2_29 - } - - override fun is2_30(): Boolean { - return getVersion() === DHISVersion.V2_30 - } - - override fun is2_31(): Boolean { - return getVersion() === DHISVersion.V2_31 - } - - override fun is2_32(): Boolean { - return getVersion() === DHISVersion.V2_32 - } - - override fun is2_33(): Boolean { - return getVersion() === DHISVersion.V2_33 - } - - override fun is2_34(): Boolean { - return getVersion() === DHISVersion.V2_34 - } - - override fun is2_35(): Boolean { - return getVersion() === DHISVersion.V2_35 - } - - override fun is2_36(): Boolean { - return getVersion() === DHISVersion.V2_36 - } - - override fun is2_37(): Boolean { - return getVersion() === DHISVersion.V2_37 - } - - override fun is2_38(): Boolean { - return getVersion() === DHISVersion.V2_38 - } - - override fun is2_39(): Boolean { - return getVersion() === DHISVersion.V2_39 - } - - override fun is2_40(): Boolean { - return getVersion() === DHISVersion.V2_40 - } - override fun isGreaterThan(version: DHISVersion): Boolean { return version < getVersion() } 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 index 6f44d9eb69..dc2801e8e7 100644 --- 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 @@ -46,6 +46,7 @@ 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.RelationshipType +import org.hisp.dhis.android.core.systeminfo.DHISVersion import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.trackedentity.* import org.hisp.dhis.android.core.trackedentity.ownership.ProgramOwner @@ -328,7 +329,7 @@ internal class OldTrackerImporterPayloadGenerator @Inject internal constructor( .trackedEntityDataValues(extraData.dataValueMap[event.uid()]) .notes(getEventNotes(extraData.notes, event)) - if (versionManager.is2_30()) { + if (versionManager.getVersion() == DHISVersion.V2_30) { eventBuilder.geometry(null) } diff --git a/core/src/sharedTest/resources/trackedentity/tracked_entity_attribute_value_image.png b/core/src/sharedTest/resources/trackedentity/tracked_entity_attribute_value_image.png new file mode 100644 index 0000000000000000000000000000000000000000..e120b99b0542a241e67fa7f4042af0c9f2522a44 GIT binary patch literal 1141 zcmV-*1d98KP)JZw}|ok%R23J4$VGu&V1HDgJgNotBh|*ug7bDB{_!$VP1Qx1HeF(mL9kso?Q-=` zTkm2^n>nN75J?qW2H96w3z?e${Qj^N_QIs(G&t7H!wZl-h3U!me0>*Nz8N$!9$~Zq z3F{lkB)F;&9{!*0>LWV@T@w8|aJ)|qzwIDp=g^){>0-+#dxpj$i2c9C@rhEG&?X@~ z{NZpDWNKdqGvG1E-oV_@UnR#AA+*25uS~Y5@m*|bvSx4$Vz>>mlh8&XQnrFEpu0`k zIp`I_$8QbW;Rnc0LytuJ5RSvXZ*izkTe{fNX3p3+L@@@QgX|HE1?}JTjrJB6KuNZ+ zsUHmzt3Yb_f3XhJZd2+d2q07?<$-!BA*c7gKk! z)kPK!oC86u2iZr^H&jtlk@b#+d1IVgjKnNdSHxRni>XIuv13eS%gEnZRui5n>l0S z5XC$Qm%wxQ-9w1@z2G9qv=3Vi)8Qe=bOSLj^jFF81kjC!>=pQbg|WSMh%`%0)i(X)8%n8 z^oFw_(xF{5kw(H~BlF7@@k@ijieQpMkKZ!Fi~LRgm>T zZy`ca00lC=XU0ZWAbFrCQl6V7h zvBkT#=4R$%Gq)u12IOLkcWuqh%*AGIN#YI2#TM_{nw!~AG&Xc%NHsZ900000NkvXX Hu0mjf8B`VL literal 0 HcmV?d00001 diff --git a/core/src/sharedTest/resources/trackedentity/tracked_entity_attribute_value_image_resource.json b/core/src/sharedTest/resources/trackedentity/tracked_entity_attribute_value_image_resource.json new file mode 100644 index 0000000000..2ca40046aa --- /dev/null +++ b/core/src/sharedTest/resources/trackedentity/tracked_entity_attribute_value_image_resource.json @@ -0,0 +1,39 @@ +{ + "name": "profile.png", + "created": "2022-12-12T04:22:49.229", + "lastUpdated": "2022-12-12T04:22:49.454", + "translations": [], + "externalAccess": false, + "createdBy": { + "id": "xE7jOejl9FI", + "code": null, + "name": "John Traore", + "displayName": "John Traore", + "username": "admin" + }, + "userGroupAccesses": [], + "userAccesses": [], + "favorites": [], + "sharing": { + "external": false, + "users": {}, + "userGroups": {} + }, + "contentType": "image/png", + "contentLength": 9270, + "contentMd5": "c20a12625f68dedcd1172157e3a8734c", + "domain": "DATA_VALUE", + "hasMultipleStorageFiles": true, + "storageStatus": "STORED", + "displayName": "profile.png", + "user": { + "id": "xE7jOejl9FI", + "code": null, + "name": "John Traore", + "displayName": "John Traore", + "username": "admin" + }, + "favorite": false, + "id": "befryEfXge5", + "attributeValues": [] +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/trackedentity/tracked_entity_attributes.json b/core/src/sharedTest/resources/trackedentity/tracked_entity_attributes.json index 4a1059e0f6..d786510fe9 100644 --- a/core/src/sharedTest/resources/trackedentity/tracked_entity_attributes.json +++ b/core/src/sharedTest/resources/trackedentity/tracked_entity_attributes.json @@ -36,17 +36,17 @@ "id": "aejWyOfXge6", "lastUpdated": "2016-08-04T11:48:56.928", "created": "2014-01-09T19:12:46.551", - "name": "Age", - "shortName": "Age", + "name": "Profile", + "shortName": "Profile", "formName": "formname", "displayFormName": "displayformname", - "valueType": "TEXT", + "valueType": "IMAGE", "programScope": true, "orgunitScope": true, "displayInListNoProgram": true, "displayOnVisitSchedule": true, "sortOrderInListNoProgram": 0, - "displayName": "Age", + "displayName": "Profile", "expression": "expression", "generated": true, "pattern": "RANDOM(##)", diff --git a/core/src/sharedTest/resources/trackedentity/tracked_entity_instances.json b/core/src/sharedTest/resources/trackedentity/tracked_entity_instances.json index 6b8fb99d40..65aa0c2b2f 100644 --- a/core/src/sharedTest/resources/trackedentity/tracked_entity_instances.json +++ b/core/src/sharedTest/resources/trackedentity/tracked_entity_instances.json @@ -51,6 +51,12 @@ "created": "2019-01-10T13:40:28.000", "attribute": "cejWyOfXge6", "value": "4081507" + }, + { + "lastUpdated": "2019-01-11T13:40:28.000", + "created": "2019-01-11T13:40:28.000", + "attribute": "aejWyOfXge6", + "value": "befryEfXge5" } ], "enrollments": [ @@ -220,12 +226,6 @@ "created": "2018-01-10T13:40:28.000", "attribute": "cejWyOfXge6", "value": "654321" - }, - { - "lastUpdated": "2017-01-10T13:40:28.000", - "created": "2017-01-10T13:40:28.000", - "attribute": "aejWyOfXge6", - "value": "123456" } ], "enrollments": [ diff --git a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.kt b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.kt index 8dae6f1792..0758a61dd1 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/systeminfo/internal/DHISVersionManagerShould.kt @@ -31,9 +31,8 @@ import com.google.common.truth.Truth.assertThat import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore -import org.hisp.dhis.android.core.systeminfo.DHISVersion -import org.hisp.dhis.android.core.systeminfo.DHISVersionManager -import org.hisp.dhis.android.core.systeminfo.SystemInfo +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.systeminfo.* import org.junit.Before import org.junit.Test @@ -57,5 +56,40 @@ class DHISVersionManagerShould { assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_31)).isFalse() assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_32)).isFalse() assertThat(dhisVersionManager.isGreaterThan(DHISVersion.V2_33)).isFalse() + + assertThat(dhisVersionManager.isGreaterOrEqualThan(DHISVersion.V2_30)).isTrue() + assertThat(dhisVersionManager.isGreaterOrEqualThan(DHISVersion.V2_31)).isTrue() + assertThat(dhisVersionManager.isGreaterOrEqualThan(DHISVersion.V2_32)).isFalse() + assertThat(dhisVersionManager.isGreaterOrEqualThan(DHISVersion.V2_33)).isFalse() + } + + @Test(expected = D2Error::class) + fun throw_invalid_version() { + whenever(systemInfo.version()).thenReturn("Invalid_version") + dhisVersionManager.getVersion() + } + + @Test + fun should_get_patch_version() { + whenever(systemInfo.version()).thenReturn("2.40.0") + assertThat(dhisVersionManager.getPatchVersion()).isEqualTo(DHISPatchVersion.V2_40_0) + } + + @Test + fun return_null_if_unknown_patch_version() { + whenever(systemInfo.version()).thenReturn("2.39.5.1") + assertThat(dhisVersionManager.getPatchVersion()).isNull() + } + + @Test + fun should_return_sms_version() { + whenever(systemInfo.version()).thenReturn("2.39.5.1") + assertThat(dhisVersionManager.getSmsVersion()).isEqualTo(SMSVersion.V2) + } + + @Test + fun return_null_if_none_sms_version() { + whenever(systemInfo.version()).thenReturn("2.31.7") + assertThat(dhisVersionManager.getSmsVersion()).isNull() } } From 7f376ad29a249c71dc5e60ae0c635072b1c229b7 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Tue, 13 Dec 2022 17:55:28 +0100 Subject: [PATCH 007/201] Rename programThemes to UseCases --- .../StockUseCaseStoreIntegrationShould.kt} | 18 ++--- ...eTransactionLinkStoreIntegrationShould.kt} | 26 ++++---- ...ctionRepositoryMockIntegrationShould.java} | 23 ++++--- core/src/main/assets/migrations/136.sql | 4 ++ .../assets/snapshots/{135.sql => 136.sql} | 4 +- .../java/org/hisp/dhis/android/core/D2.kt | 6 +- .../core/arch/d2/internal/D2DIComponent.kt | 6 +- .../core/arch/d2/internal/D2Modules.kt | 60 ++++++++--------- .../internal/BaseDatabaseOpenHelper.java | 2 +- ...kUseCaseTransactionListColumnAdapter.java} | 6 +- .../core/domain/metadata/MetadataCall.kt | 46 ++++++------- .../android/core/map/MapModuleDownloader.kt | 6 +- .../core/mockwebserver/Dhis2MockServer.java | 8 +-- .../UseCaseModule.kt} | 8 +-- .../UseCaseModuleDownloader.kt} | 10 +-- .../internal/UseCaseModuleImpl.kt} | 16 ++--- .../internal/UseCaseModuleWiper.kt} | 12 ++-- .../internal/UseCasePackageDIModule.kt} | 22 +++---- .../stock/InternalStockUseCase.java} | 22 +++---- .../InternalStockUseCaseTransaction.java} | 14 ++-- .../stock/StockUseCase.kt} | 6 +- .../StockUseCaseCollectionRepository.kt} | 28 ++++---- .../stock/StockUseCaseTableInfo.kt} | 6 +- .../stock/StockUseCaseTransaction.kt} | 16 ++--- .../StockUseCaseTransactionTableInfo.kt} | 6 +- .../stock/internal/StockUseCaseCall.kt} | 24 +++---- .../internal/StockUseCaseEntityDIModule.kt} | 36 +++++----- .../stock/internal/StockUseCaseHandler.kt} | 18 ++--- .../stock/internal/StockUseCaseService.kt} | 10 +-- .../stock/internal/StockUseCaseStore.kt} | 16 ++--- ...tockUseCaseTransactionChildrenAppender.kt} | 18 ++--- .../StockUseCaseTransactionEntityDIModule.kt} | 14 ++-- .../StockUseCaseTransactionLinkStore.kt} | 22 +++---- .../internal/StockUseCaseTransformer.kt} | 22 +++---- .../core/wipe/internal/D2ModuleWipers.kt | 66 +++++++++---------- .../stock/InternalStockUseCaseSamples.kt} | 12 ++-- ...InternalStockUseCaseTransactionSamples.kt} | 10 +-- .../stock_use_case.json} | 0 .../stock_use_cases.json} | 0 .../domain/metadata/MetadataCallShould.kt | 8 +-- .../stock/InternalStockUseCaseShould.kt} | 26 ++++---- .../stock/StockUseCaseCallShould.kt} | 24 +++---- ...ternalStockUseCasePublicAccessShould.java} | 16 ++--- ...UseCaseTransactionPublicAccessShould.java} | 16 ++--- 44 files changed, 371 insertions(+), 368 deletions(-) rename core/src/androidTest/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeStoreIntegrationShould.kt => usecase/stock/internal/StockUseCaseStoreIntegrationShould.kt} (75%) rename core/src/androidTest/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeTransactionLinkStoreIntegrationShould.kt => usecase/stock/internal/StockUseCaseTransactionLinkStoreIntegrationShould.kt} (71%) rename core/src/androidTest/java/org/hisp/dhis/android/testapp/{programtheme/stock/StockThemeCollectionRepositoryMockIntegrationShould.java => usecase/stock/StockUseCaseCollectionRepositoryMockIntegrationShould.java} (73%) create mode 100644 core/src/main/assets/migrations/136.sql rename core/src/main/assets/snapshots/{135.sql => 136.sql} (98%) rename core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/{IgnoreInternalStockThemeTransactionListColumnAdapter.java => IgnoreInternalStockUseCaseTransactionListColumnAdapter.java} (87%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/ProgramThemeModule.kt => usecase/UseCaseModule.kt} (87%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/ProgramThemeModuleDownloader.kt => usecase/UseCaseModuleDownloader.kt} (86%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/internal/ProgramThemeModuleImpl.kt => usecase/internal/UseCaseModuleImpl.kt} (78%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/internal/ProgramThemeModuleWiper.kt => usecase/internal/UseCaseModuleWiper.kt} (84%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/internal/ProgramThemePackageDIModule.kt => usecase/internal/UseCasePackageDIModule.kt} (71%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/InternalStockTheme.java => usecase/stock/InternalStockUseCase.java} (83%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/InternalStockThemeTransaction.java => usecase/stock/InternalStockUseCaseTransaction.java} (86%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/StockTheme.kt => usecase/stock/StockUseCase.kt} (93%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/StockThemeCollectionRepository.kt => usecase/stock/StockUseCaseCollectionRepository.kt} (74%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/StockThemeTableInfo.kt => usecase/stock/StockUseCaseTableInfo.kt} (95%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/StockThemeTransaction.kt => usecase/stock/StockUseCaseTransaction.kt} (87%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/StockThemeTransactionTableInfo.kt => usecase/stock/StockUseCaseTransactionTableInfo.kt} (95%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeCall.kt => usecase/stock/internal/StockUseCaseCall.kt} (73%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeEntityDIModule.kt => usecase/stock/internal/StockUseCaseEntityDIModule.kt} (69%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeHandler.kt => usecase/stock/internal/StockUseCaseHandler.kt} (77%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeService.kt => usecase/stock/internal/StockUseCaseService.kt} (85%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeStore.kt => usecase/stock/internal/StockUseCaseStore.kt} (82%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeTransactionChildrenAppender.kt => usecase/stock/internal/StockUseCaseTransactionChildrenAppender.kt} (76%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeTransactionEntityDIModule.kt => usecase/stock/internal/StockUseCaseTransactionEntityDIModule.kt} (82%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeTransactionLinkStore.kt => usecase/stock/internal/StockUseCaseTransactionLinkStore.kt} (76%) rename core/src/main/java/org/hisp/dhis/android/core/{programtheme/stock/internal/StockThemeTransformer.kt => usecase/stock/internal/StockUseCaseTransformer.kt} (73%) rename core/src/sharedTest/java/org/hisp/dhis/android/core/data/{programtheme/stock/InternalStockThemeSamples.kt => usecase/stock/InternalStockUseCaseSamples.kt} (85%) rename core/src/sharedTest/java/org/hisp/dhis/android/core/data/{programtheme/stock/InternalStockThemeTransactionSamples.kt => usecase/stock/InternalStockUseCaseTransactionSamples.kt} (86%) rename core/src/sharedTest/resources/{programtheme.stock/stock_theme.json => usecase.stock/stock_use_case.json} (100%) rename core/src/sharedTest/resources/{programtheme.stock/stock_themes.json => usecase.stock/stock_use_cases.json} (100%) rename core/src/test/java/org/hisp/dhis/android/core/{programtheme/stock/InternalStockThemeShould.kt => usecase/stock/InternalStockUseCaseShould.kt} (71%) rename core/src/test/java/org/hisp/dhis/android/core/{programtheme/stock/StockThemeCallShould.kt => usecase/stock/StockUseCaseCallShould.kt} (74%) rename core/src/test/java/org/hisp/dhis/android/testapp/{programtheme/stock/InternalStockThemePublicAccessShould.java => usecase/stock/InternalStockUseCasePublicAccessShould.java} (82%) rename core/src/test/java/org/hisp/dhis/android/testapp/{programtheme/stock/InternalStockThemeTransactionPublicAccessShould.java => usecase/stock/InternalStockUseCaseTransactionPublicAccessShould.java} (80%) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseStoreIntegrationShould.kt similarity index 75% rename from core/src/androidTest/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeStoreIntegrationShould.kt rename to core/src/androidTest/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseStoreIntegrationShould.kt index d313eda6ef..62db131dad 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeStoreIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseStoreIntegrationShould.kt @@ -25,27 +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.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import org.hisp.dhis.android.core.data.database.IdentifiableObjectStoreAbstractIntegrationShould -import org.hisp.dhis.android.core.data.programtheme.stock.InternalStockThemeSamples.get -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme -import org.hisp.dhis.android.core.programtheme.stock.StockThemeTableInfo -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeStore.create +import org.hisp.dhis.android.core.data.usecase.stock.InternalStockUseCaseSamples.get +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseTableInfo +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseStore.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 StockThemeStoreIntegrationShould : IdentifiableObjectStoreAbstractIntegrationShould( - create(TestDatabaseAdapterFactory.get()), StockThemeTableInfo.TABLE_INFO, +class StockUseCaseStoreIntegrationShould : IdentifiableObjectStoreAbstractIntegrationShould( + create(TestDatabaseAdapterFactory.get()), StockUseCaseTableInfo.TABLE_INFO, TestDatabaseAdapterFactory.get() ) { - override fun buildObject(): InternalStockTheme { + override fun buildObject(): InternalStockUseCase { return get() } - override fun buildObjectToUpdate(): InternalStockTheme { + override fun buildObjectToUpdate(): InternalStockUseCase { return get().toBuilder() .stockOnHand("new_stock_on_hand") .build() diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionLinkStoreIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionLinkStoreIntegrationShould.kt similarity index 71% rename from core/src/androidTest/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionLinkStoreIntegrationShould.kt rename to core/src/androidTest/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionLinkStoreIntegrationShould.kt index 657e754f8c..236ecc5ebd 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionLinkStoreIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionLinkStoreIntegrationShould.kt @@ -25,34 +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.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import com.google.common.truth.Truth.assertThat import org.hisp.dhis.android.core.data.database.LinkStoreAbstractIntegrationShould -import org.hisp.dhis.android.core.data.programtheme.stock.InternalStockThemeTransactionSamples -import org.hisp.dhis.android.core.programtheme.stock.InternalStockThemeTransaction -import org.hisp.dhis.android.core.programtheme.stock.StockThemeTransactionTableInfo -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeTransactionLinkStore.create +import org.hisp.dhis.android.core.data.usecase.stock.InternalStockUseCaseTransactionSamples +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseTransactionTableInfo +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseTransactionLinkStore.create import org.hisp.dhis.android.core.utils.integration.mock.TestDatabaseAdapterFactory import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.Test import org.junit.runner.RunWith @RunWith(D2JunitRunner::class) -class StockThemeTransactionLinkStoreIntegrationShould : - LinkStoreAbstractIntegrationShould( +class StockUseCaseTransactionLinkStoreIntegrationShould : + LinkStoreAbstractIntegrationShould( create(TestDatabaseAdapterFactory.get()), - StockThemeTransactionTableInfo.TABLE_INFO, TestDatabaseAdapterFactory.get() + StockUseCaseTransactionTableInfo.TABLE_INFO, TestDatabaseAdapterFactory.get() ) { override fun addMasterUid(): String { - return InternalStockThemeTransactionSamples.get().programUid()!! + return InternalStockUseCaseTransactionSamples.get().programUid()!! } - override fun buildObject(): InternalStockThemeTransaction { - return InternalStockThemeTransactionSamples.get() + override fun buildObject(): InternalStockUseCaseTransaction { + return InternalStockUseCaseTransactionSamples.get() } - override fun buildObjectWithOtherMasterUid(): InternalStockThemeTransaction { + override fun buildObjectWithOtherMasterUid(): InternalStockUseCaseTransaction { return buildObject().toBuilder() .programUid("new_program_uid") .build() @@ -64,7 +64,7 @@ class StockThemeTransactionLinkStoreIntegrationShould : store.insert(buildObject()) val count: Map = - store.groupAndGetCountBy(StockThemeTransactionTableInfo.Columns.PROGRAM_UID) + store.groupAndGetCountBy(StockUseCaseTransactionTableInfo.Columns.PROGRAM_UID) assertThat(count.keys.size).isEqualTo(2) assertThat(count[buildObjectWithOtherMasterUid().programUid()]).isEqualTo(1) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/programtheme/stock/StockThemeCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/usecase/stock/StockUseCaseCollectionRepositoryMockIntegrationShould.java similarity index 73% rename from core/src/androidTest/java/org/hisp/dhis/android/testapp/programtheme/stock/StockThemeCollectionRepositoryMockIntegrationShould.java rename to core/src/androidTest/java/org/hisp/dhis/android/testapp/usecase/stock/StockUseCaseCollectionRepositoryMockIntegrationShould.java index b6731b8099..7fa50e4123 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/programtheme/stock/StockThemeCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/usecase/stock/StockUseCaseCollectionRepositoryMockIntegrationShould.java @@ -26,12 +26,11 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.testapp.programtheme.stock; +package org.hisp.dhis.android.testapp.usecase.stock; import static com.google.common.truth.Truth.assertThat; -import org.hisp.dhis.android.core.indicator.IndicatorType; -import org.hisp.dhis.android.core.programtheme.stock.StockTheme; +import org.hisp.dhis.android.core.usecase.stock.StockUseCase; import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; import org.hisp.dhis.android.core.utils.runner.D2JunitRunner; import org.junit.Test; @@ -40,31 +39,31 @@ import java.util.List; @RunWith(D2JunitRunner.class) -public class StockThemeCollectionRepositoryMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { +public class StockUseCaseCollectionRepositoryMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { @Test public void find_all() { - List stockThemes = d2.programThemeModule().stockThemes() + List stockUseCases = d2.useCaseModule().stockUseCases() .blockingGet(); - assertThat(stockThemes.size()).isEqualTo(1); + assertThat(stockUseCases.size()).isEqualTo(1); } @Test public void filter_by_uid() { - StockTheme stockTheme = d2.programThemeModule().stockThemes() + StockUseCase stockUseCase = d2.useCaseModule().stockUseCases() .uid("IpHINAT79UW") .blockingGet(); - assertThat(stockTheme).isNotNull(); - assertThat(stockTheme.getStockOnHand()).isEqualTo("ypCQAFr1a5l"); + assertThat(stockUseCase).isNotNull(); + assertThat(stockUseCase.getStockOnHand()).isEqualTo("ypCQAFr1a5l"); } @Test public void filter_by_number() { - List stockThemes = d2.programThemeModule().stockThemes() + List stockUseCases = d2.useCaseModule().stockUseCases() .withTransactions() .blockingGet(); - assertThat(stockThemes.size()).isEqualTo(1); - assertThat(stockThemes.get(0).getTransactions().size()).isEqualTo(3); + assertThat(stockUseCases.size()).isEqualTo(1); + assertThat(stockUseCases.get(0).getTransactions().size()).isEqualTo(3); } } \ No newline at end of file diff --git a/core/src/main/assets/migrations/136.sql b/core/src/main/assets/migrations/136.sql new file mode 100644 index 0000000000..b0fda06957 --- /dev/null +++ b/core/src/main/assets/migrations/136.sql @@ -0,0 +1,4 @@ +# Rename to StockUseCases (ANDROSDK-1602); + +CREATE TABLE StockUseCase (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, itemCode TEXT, itemDescription TEXT, programType TEXT, description TEXT, stockOnHand TEXT, FOREIGN KEY (uid) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE StockUseCaseTransaction (_id INTEGER PRIMARY KEY AUTOINCREMENT, programUid TEXT NOT NULL, sortOrder INTEGER, transactionType TEXT, distributedTo TEXT, stockDistributed TEXT, stockDiscarded TEXT, stockCorrected TEXT, FOREIGN KEY (programUid) REFERENCES StockUseCase (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); diff --git a/core/src/main/assets/snapshots/135.sql b/core/src/main/assets/snapshots/136.sql similarity index 98% rename from core/src/main/assets/snapshots/135.sql rename to core/src/main/assets/snapshots/136.sql index 294042176b..eb8255b6a8 100644 --- a/core/src/main/assets/snapshots/135.sql +++ b/core/src/main/assets/snapshots/136.sql @@ -124,7 +124,7 @@ CREATE TABLE SmsMetadataId (_id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT, ui CREATE TABLE SMSOngoingSubmission (_id INTEGER PRIMARY KEY AUTOINCREMENT, submissionId INTEGER, type TEXT); CREATE TABLE TrackedEntityAttributeLegendSetLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackedEntityAttribute TEXT NOT NULL, legendSet TEXT NOT NULL, sortOrder INTEGER, FOREIGN KEY (trackedEntityAttribute) REFERENCES TrackedEntityAttribute (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (legendSet) REFERENCES LegendSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (trackedEntityAttribute, legendSet)); CREATE TABLE ItemFilter (_id INTEGER PRIMARY KEY AUTOINCREMENT, eventFilter TEXT, dataItem TEXT, trackedEntityInstanceFilter TEXT, attribute TEXT, sw TEXT, ew 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, FOREIGN KEY (trackedEntityInstanceFilter) REFERENCES TrackedEntityInstanceFilter (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE StockTheme (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, itemCode TEXT, itemDescription TEXT, programType TEXT, description TEXT, stockOnHand TEXT, FOREIGN KEY (uid) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE StockThemeTransaction (_id INTEGER PRIMARY KEY AUTOINCREMENT, programUid TEXT NOT NULL, sortOrder INTEGER, transactionType TEXT, distributedTo TEXT, stockDistributed TEXT, stockDiscarded TEXT, stockCorrected TEXT, FOREIGN KEY (programUid) REFERENCES StockTheme (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE StockUseCase (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, itemCode TEXT, itemDescription TEXT, programType TEXT, description TEXT, stockOnHand TEXT, FOREIGN KEY (uid) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE StockUseCaseTransaction (_id INTEGER PRIMARY KEY AUTOINCREMENT, programUid TEXT NOT NULL, sortOrder INTEGER, transactionType TEXT, distributedTo TEXT, stockDistributed TEXT, stockDiscarded TEXT, stockCorrected TEXT, FOREIGN KEY (programUid) REFERENCES StockUseCase (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE MapLayer (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, name TEXT NOT NULL, displayName TEXT NOT NULL, external INTEGER, mapLayerPosition TEXT NOT NULL, style TEXT, imageUrl TEXT NOT NULL, subdomains TEXT, subdomainPlaceholder TEXT); CREATE TABLE MapLayerImageryProvider (_id INTEGER PRIMARY KEY AUTOINCREMENT, mapLayer TEXT NOT NULL, attribution TEXT NOT NULL, coverageAreas TEXT, FOREIGN KEY (mapLayer) REFERENCES MapLayer (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); diff --git a/core/src/main/java/org/hisp/dhis/android/core/D2.kt b/core/src/main/java/org/hisp/dhis/android/core/D2.kt index e2b7ec34a4..dd42c82218 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/D2.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/D2.kt @@ -54,7 +54,7 @@ import org.hisp.dhis.android.core.option.OptionModule import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModule import org.hisp.dhis.android.core.period.PeriodModule import org.hisp.dhis.android.core.program.ProgramModule -import org.hisp.dhis.android.core.programtheme.ProgramThemeModule +import org.hisp.dhis.android.core.usecase.UseCaseModule import org.hisp.dhis.android.core.relationship.RelationshipModule import org.hisp.dhis.android.core.settings.SettingModule import org.hisp.dhis.android.core.sms.SmsModule @@ -185,8 +185,8 @@ class D2 internal constructor(internal val d2DIComponent: D2DIComponent) { return modules.program } - fun programThemeModule(): ProgramThemeModule { - return modules.programTheme + fun useCaseModule(): UseCaseModule { + return modules.useCase } fun organisationUnitModule(): OrganisationUnitModule { diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt index 193593362b..5c06a36c8d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt @@ -79,7 +79,7 @@ import org.hisp.dhis.android.core.period.internal.PeriodHandler import org.hisp.dhis.android.core.period.internal.PeriodPackageDIModule import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.program.internal.ProgramPackageDIModule -import org.hisp.dhis.android.core.programtheme.internal.ProgramThemePackageDIModule +import org.hisp.dhis.android.core.usecase.internal.UseCasePackageDIModule import org.hisp.dhis.android.core.relationship.RelationshipType import org.hisp.dhis.android.core.relationship.internal.RelationshipPackageDIModule import org.hisp.dhis.android.core.resource.internal.ResourcePackageDIModule @@ -139,7 +139,7 @@ import retrofit2.Retrofit ResourcePackageDIModule::class, SystemInfoPackageDIModule::class, SettingPackageDIModule::class, - ProgramThemePackageDIModule::class, + UseCasePackageDIModule::class, TrackedEntityPackageDIModule::class, TrackerImporterPackageDIModule::class, SmsDIModule::class, @@ -235,7 +235,7 @@ internal interface D2DIComponent { fun resourcePackageDIModule(resourcePackageDIModule: ResourcePackageDIModule): Builder fun systemInfoPackageDIModule(systemInfoPackageDIModule: SystemInfoPackageDIModule): Builder fun systemSettingPackageDIModule(settingPackageDIModule: SettingPackageDIModule): Builder - fun programThemePackageDIModule(programThemePackageDIModule: ProgramThemePackageDIModule): Builder + fun useCasePackageDIModule(useCasePackageDIModule: UseCasePackageDIModule): Builder fun trackedEntityPackageDIModule(trackedEntityPackageDIModule: TrackedEntityPackageDIModule): Builder fun trackerImporterPackageDIModule(trackerImporterPackageDIModule: TrackerImporterPackageDIModule): Builder fun userPackageDIModule(userPackageDIModule: UserPackageDIModule): Builder diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt index 3d19c08964..a6f343d5fb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt @@ -49,7 +49,7 @@ import org.hisp.dhis.android.core.option.OptionModule import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModule import org.hisp.dhis.android.core.period.PeriodModule import org.hisp.dhis.android.core.program.ProgramModule -import org.hisp.dhis.android.core.programtheme.ProgramThemeModule +import org.hisp.dhis.android.core.usecase.UseCaseModule import org.hisp.dhis.android.core.relationship.RelationshipModule import org.hisp.dhis.android.core.settings.SettingModule import org.hisp.dhis.android.core.sms.SmsModule @@ -62,33 +62,33 @@ import org.hisp.dhis.android.core.visualization.VisualizationModule @Reusable @Suppress("LongParameterList") internal class D2Modules @Inject constructor( - val analytics: AnalyticsModule, - val category: CategoryModule, - val constant: ConstantModule, - val dataElement: DataElementModule, - val dataSet: DataSetModule, - val option: OptionModule, - val dataValue: DataValueModule, - val enrollment: EnrollmentModule, - val event: EventModule, - val fileResource: FileResourceModule, - val importModule: ImportModule, - val indicator: IndicatorModule, - val legendSet: LegendSetModule, - val dataStore: DataStoreModule, - val maintenance: MaintenanceModule, - val maps: MapModule, - val note: NoteModule, - val program: ProgramModule, - val programTheme: ProgramThemeModule, - val organisationUnit: OrganisationUnitModule, - val systemInfo: SystemInfoModule, - val settingModule: SettingModule, - val periodModule: PeriodModule, - val relationship: RelationshipModule, - val trackedEntity: TrackedEntityModule, - val user: UserModule, - val validation: ValidationModule, - val visualization: VisualizationModule, - val sms: SmsModule + val analytics: AnalyticsModule, + val category: CategoryModule, + val constant: ConstantModule, + val dataElement: DataElementModule, + val dataSet: DataSetModule, + val option: OptionModule, + val dataValue: DataValueModule, + val enrollment: EnrollmentModule, + val event: EventModule, + val fileResource: FileResourceModule, + val importModule: ImportModule, + val indicator: IndicatorModule, + val legendSet: LegendSetModule, + val dataStore: DataStoreModule, + val maintenance: MaintenanceModule, + val maps: MapModule, + val note: NoteModule, + val program: ProgramModule, + val useCase: UseCaseModule, + val organisationUnit: OrganisationUnitModule, + val systemInfo: SystemInfoModule, + val settingModule: SettingModule, + val periodModule: PeriodModule, + val relationship: RelationshipModule, + val trackedEntity: TrackedEntityModule, + val user: UserModule, + val validation: ValidationModule, + val visualization: VisualizationModule, + val sms: SmsModule ) 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 b56d735f31..250756be02 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 = 135; + static final int VERSION = 136; private final AssetManager assetManager; private final int targetVersion; diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreInternalStockThemeTransactionListColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreInternalStockUseCaseTransactionListColumnAdapter.java similarity index 87% rename from core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreInternalStockThemeTransactionListColumnAdapter.java rename to core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreInternalStockUseCaseTransactionListColumnAdapter.java index 11b890ceff..63e3b7ec0e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreInternalStockThemeTransactionListColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/adapters/ignore/internal/IgnoreInternalStockUseCaseTransactionListColumnAdapter.java @@ -27,10 +27,10 @@ */ package org.hisp.dhis.android.core.arch.db.adapters.ignore.internal; -import org.hisp.dhis.android.core.programtheme.stock.InternalStockThemeTransaction; +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction; import java.util.List; -public final class IgnoreInternalStockThemeTransactionListColumnAdapter extends - IgnoreColumnAdapter> { +public final class IgnoreInternalStockUseCaseTransactionListColumnAdapter extends + IgnoreColumnAdapter> { } \ No newline at end of file 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 dc7da7be0d..ad560d98bf 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 @@ -55,8 +55,8 @@ import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.program.ProgramIndicator import org.hisp.dhis.android.core.program.internal.ProgramIndicatorModuleDownloader import org.hisp.dhis.android.core.program.internal.ProgramModuleDownloader -import org.hisp.dhis.android.core.programtheme.ProgramThemeModuleDownloader -import org.hisp.dhis.android.core.programtheme.stock.StockTheme +import org.hisp.dhis.android.core.usecase.UseCaseModuleDownloader +import org.hisp.dhis.android.core.usecase.stock.StockUseCase import org.hisp.dhis.android.core.settings.SystemSetting import org.hisp.dhis.android.core.settings.internal.GeneralSettingCall import org.hisp.dhis.android.core.settings.internal.SettingModuleDownloader @@ -71,25 +71,25 @@ import org.hisp.dhis.android.core.visualization.internal.VisualizationModuleDown @Suppress("LongParameterList") @Reusable internal class MetadataCall @Inject constructor( - private val rxCallExecutor: RxAPICallExecutor, - private val systemInfoDownloader: SystemInfoModuleDownloader, - private val systemSettingDownloader: SettingModuleDownloader, - private val programThemeDownloader: ProgramThemeModuleDownloader, - private val userModuleDownloader: UserModuleDownloader, - private val categoryDownloader: CategoryModuleDownloader, - private val programDownloader: ProgramModuleDownloader, - private val organisationUnitModuleDownloader: OrganisationUnitModuleDownloader, - private val dataSetDownloader: DataSetModuleDownloader, - private val visualizationDownloader: VisualizationModuleDownloader, - private val constantModuleDownloader: ConstantModuleDownloader, - private val indicatorModuleDownloader: IndicatorModuleDownloader, - private val programIndicatorModuleDownloader: ProgramIndicatorModuleDownloader, - private val smsModule: SmsModule, - private val databaseAdapter: DatabaseAdapter, - private val generalSettingCall: GeneralSettingCall, - private val multiUserDatabaseManager: MultiUserDatabaseManager, - private val credentialsSecureStore: CredentialsSecureStore, - private val legendSetModuleDownloader: LegendSetModuleDownloader, + private val rxCallExecutor: RxAPICallExecutor, + private val systemInfoDownloader: SystemInfoModuleDownloader, + private val systemSettingDownloader: SettingModuleDownloader, + private val useCaseDownloader: UseCaseModuleDownloader, + private val userModuleDownloader: UserModuleDownloader, + private val categoryDownloader: CategoryModuleDownloader, + private val programDownloader: ProgramModuleDownloader, + private val organisationUnitModuleDownloader: OrganisationUnitModuleDownloader, + private val dataSetDownloader: DataSetModuleDownloader, + private val visualizationDownloader: VisualizationModuleDownloader, + private val constantModuleDownloader: ConstantModuleDownloader, + private val indicatorModuleDownloader: IndicatorModuleDownloader, + private val programIndicatorModuleDownloader: ProgramIndicatorModuleDownloader, + private val smsModule: SmsModule, + private val databaseAdapter: DatabaseAdapter, + private val generalSettingCall: GeneralSettingCall, + private val multiUserDatabaseManager: MultiUserDatabaseManager, + private val credentialsSecureStore: CredentialsSecureStore, + private val legendSetModuleDownloader: LegendSetModuleDownloader, ) { companion object { @@ -120,8 +120,8 @@ internal class MetadataCall @Inject constructor( systemSettingDownloader.downloadMetadata().toSingle { progressManager.increaseProgress(SystemSetting::class.java, false) }, - programThemeDownloader.downloadMetadata().toSingle { - progressManager.increaseProgress(StockTheme::class.java, false) + useCaseDownloader.downloadMetadata().toSingle { + progressManager.increaseProgress(StockUseCase::class.java, false) }, constantModuleDownloader.downloadMetadata().map { progressManager.increaseProgress(Constant::class.java, false) diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/MapModuleDownloader.kt b/core/src/main/java/org/hisp/dhis/android/core/map/MapModuleDownloader.kt index c272639779..0287ffdecd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/map/MapModuleDownloader.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/map/MapModuleDownloader.kt @@ -31,16 +31,16 @@ import dagger.Reusable import io.reactivex.Completable import javax.inject.Inject import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeCall +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseCall @Reusable internal class MapModuleDownloader @Inject constructor( - private val stockThemeCall: StockThemeCall + private val stockUseCaseCall: StockUseCaseCall ) : UntypedModuleDownloader { override fun downloadMetadata(): Completable { return Completable.fromAction { - stockThemeCall.getCompletable(false).blockingAwait() + stockUseCaseCall.getCompletable(false).blockingAwait() } } } 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 0afaf49e20..a7a7bf7729 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 @@ -55,7 +55,7 @@ public class Dhis2MockServer { private static final String AUTHORITIES_JSON = "user/authorities.json"; private static final String SYSTEM_INFO_JSON = "systeminfo/system_info.json"; private static final String SYSTEM_SETTINGS_JSON = "settings/system_settings.json"; - private static final String STOCK_THEMES_JSON = "programtheme.stock/stock_themes.json"; + private static final String STOCK_USE_CASES_JSON = "usecase.stock/stock_use_cases.json"; private static final String ANDROID_SETTINGS_METADATA_JSON = "settings/app_metadata_list.json"; private static final String ANDROID_SETTINGS_INFO_JSON = "settings/app_info.json"; private static final String GENERAL_SETTINGS_V1_JSON = "settings/general_settings_v1.json"; @@ -182,8 +182,8 @@ public MockResponse dispatch(RecordedRequest request) { return createMockResponse(SYSTEM_INFO_JSON); } else if (path.startsWith("/api/systemSettings?")) { return createMockResponse(SYSTEM_SETTINGS_JSON); - } else if (path.startsWith("/api/dataStore/PROGRAM_THEMES/stockThemes")) { - return createMockResponse(STOCK_THEMES_JSON); + } else if (path.startsWith("/api/dataStore/USE_CASES/stockUseCases")) { + return createMockResponse(STOCK_USE_CASES_JSON); } else if (path.startsWith("/api/apps?filter")) { return createMockResponse(ANDROID_SETTINGS_METADATA_JSON); } else if (path.startsWith("/api/dataStore/ANDROID_SETTINGS_APP/info")) { @@ -309,7 +309,7 @@ public void enqueueMetadataResponses() { enqueueMockResponse(ANALYTICS_SETTINGS_JSON); enqueueMockResponse(USER_SETTINGS_JSON); enqueueMockResponse(SYSTEM_SETTINGS_JSON); - enqueueMockResponse(STOCK_THEMES_JSON); + enqueueMockResponse(STOCK_USE_CASES_JSON); enqueueMockResponse(CONSTANTS_JSON); enqueueMockResponse(USER_JSON); enqueueMockResponse(AUTHORITIES_JSON); diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/ProgramThemeModule.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/UseCaseModule.kt similarity index 87% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/ProgramThemeModule.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/UseCaseModule.kt index da448f6c6a..2da9a8a2e8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/ProgramThemeModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/UseCaseModule.kt @@ -25,11 +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.programtheme +package org.hisp.dhis.android.core.usecase -import org.hisp.dhis.android.core.programtheme.stock.StockThemeCollectionRepository +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseCollectionRepository -interface ProgramThemeModule { +interface UseCaseModule { - fun stockThemes(): StockThemeCollectionRepository + fun stockUseCases(): StockUseCaseCollectionRepository } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/ProgramThemeModuleDownloader.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/UseCaseModuleDownloader.kt similarity index 86% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/ProgramThemeModuleDownloader.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/UseCaseModuleDownloader.kt index f0ddca9a4e..03e0b83364 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/ProgramThemeModuleDownloader.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/UseCaseModuleDownloader.kt @@ -25,22 +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.programtheme +package org.hisp.dhis.android.core.usecase import dagger.Reusable import io.reactivex.Completable import javax.inject.Inject import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeCall +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseCall @Reusable -internal class ProgramThemeModuleDownloader @Inject constructor( - private val stockThemeCall: StockThemeCall +internal class UseCaseModuleDownloader @Inject constructor( + private val stockUseCaseCall: StockUseCaseCall ) : UntypedModuleDownloader { override fun downloadMetadata(): Completable { return Completable.fromAction { - stockThemeCall.getCompletable(false).blockingAwait() + stockUseCaseCall.getCompletable(false).blockingAwait() } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemeModuleImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCaseModuleImpl.kt similarity index 78% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemeModuleImpl.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCaseModuleImpl.kt index d348edbf4e..466521a85f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemeModuleImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCaseModuleImpl.kt @@ -25,18 +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.programtheme.internal +package org.hisp.dhis.android.core.usecase.internal import dagger.Reusable import javax.inject.Inject -import org.hisp.dhis.android.core.programtheme.ProgramThemeModule -import org.hisp.dhis.android.core.programtheme.stock.StockThemeCollectionRepository +import org.hisp.dhis.android.core.usecase.UseCaseModule +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseCollectionRepository @Reusable -internal class ProgramThemeModuleImpl @Inject internal constructor( - private val stockThemes: StockThemeCollectionRepository -) : ProgramThemeModule { - override fun stockThemes(): StockThemeCollectionRepository { - return stockThemes +internal class UseCaseModuleImpl @Inject internal constructor( + private val stockUseCases: StockUseCaseCollectionRepository +) : UseCaseModule { + override fun stockUseCases(): StockUseCaseCollectionRepository { + return stockUseCases } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemeModuleWiper.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCaseModuleWiper.kt similarity index 84% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemeModuleWiper.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCaseModuleWiper.kt index 39b5a768e4..7d5c52499a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemeModuleWiper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCaseModuleWiper.kt @@ -25,23 +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.programtheme.internal +package org.hisp.dhis.android.core.usecase.internal import dagger.Reusable import javax.inject.Inject -import org.hisp.dhis.android.core.programtheme.stock.StockThemeTableInfo -import org.hisp.dhis.android.core.programtheme.stock.StockThemeTransactionTableInfo +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseTableInfo +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseTransactionTableInfo import org.hisp.dhis.android.core.wipe.internal.ModuleWiper import org.hisp.dhis.android.core.wipe.internal.TableWiper @Reusable -class ProgramThemeModuleWiper @Inject internal constructor( +class UseCaseModuleWiper @Inject internal constructor( private val tableWiper: TableWiper ) : ModuleWiper { override fun wipeMetadata() { tableWiper.wipeTables( - StockThemeTableInfo.TABLE_INFO, - StockThemeTransactionTableInfo.TABLE_INFO + StockUseCaseTableInfo.TABLE_INFO, + StockUseCaseTransactionTableInfo.TABLE_INFO ) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemePackageDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCasePackageDIModule.kt similarity index 71% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemePackageDIModule.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCasePackageDIModule.kt index 8be59e5552..894e67963a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/internal/ProgramThemePackageDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/internal/UseCasePackageDIModule.kt @@ -25,34 +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.programtheme.internal +package org.hisp.dhis.android.core.usecase.internal import dagger.Module import dagger.Provides import dagger.Reusable -import org.hisp.dhis.android.core.programtheme.ProgramThemeModule -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeEntityDIModule -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeService -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeTransactionEntityDIModule +import org.hisp.dhis.android.core.usecase.UseCaseModule +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseEntityDIModule +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseService +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseTransactionEntityDIModule import retrofit2.Retrofit @Module( includes = [ - StockThemeEntityDIModule::class, - StockThemeTransactionEntityDIModule::class, + StockUseCaseEntityDIModule::class, + StockUseCaseTransactionEntityDIModule::class, ] ) -internal class ProgramThemePackageDIModule { +internal class UseCasePackageDIModule { @Provides @Reusable - fun stockThemeService(retrofit: Retrofit): StockThemeService { - return retrofit.create(StockThemeService::class.java) + fun stockUseCaseService(retrofit: Retrofit): StockUseCaseService { + return retrofit.create(StockUseCaseService::class.java) } @Provides @Reusable - fun module(impl: ProgramThemeModuleImpl): ProgramThemeModule { + fun module(impl: UseCaseModuleImpl): UseCaseModule { return impl } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockTheme.java b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCase.java similarity index 83% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockTheme.java rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCase.java index de9c0c4862..77de826402 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockTheme.java +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCase.java @@ -26,7 +26,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock; +package org.hisp.dhis.android.core.usecase.stock; import android.database.Cursor; @@ -41,7 +41,7 @@ import com.google.auto.value.AutoValue; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreBooleanColumnAdapter; -import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreInternalStockThemeTransactionListColumnAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreInternalStockUseCaseTransactionListColumnAdapter; import org.hisp.dhis.android.core.common.BaseObject; import org.hisp.dhis.android.core.common.ObjectWithDeleteInterface; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; @@ -49,8 +49,8 @@ import java.util.List; @AutoValue -@JsonDeserialize(builder = $$AutoValue_InternalStockTheme.Builder.class) -public abstract class InternalStockTheme extends BaseObject +@JsonDeserialize(builder = $$AutoValue_InternalStockUseCase.Builder.class) +public abstract class InternalStockUseCase extends BaseObject implements ObjectWithUidInterface, ObjectWithDeleteInterface { public static final String TRANSACTIONS = "transactions"; @@ -87,17 +87,17 @@ public abstract class InternalStockTheme extends BaseObject @Nullable @JsonProperty() - @ColumnAdapter(IgnoreInternalStockThemeTransactionListColumnAdapter.class) - public abstract List transactions(); + @ColumnAdapter(IgnoreInternalStockUseCaseTransactionListColumnAdapter.class) + public abstract List transactions(); - public static InternalStockTheme create(Cursor cursor) { - return AutoValue_InternalStockTheme.createFromCursor(cursor); + public static InternalStockUseCase create(Cursor cursor) { + return AutoValue_InternalStockUseCase.createFromCursor(cursor); } public abstract Builder toBuilder(); public static Builder builder() { - return new $$AutoValue_InternalStockTheme.Builder(); + return new $$AutoValue_InternalStockUseCase.Builder(); } @AutoValue.Builder @@ -119,8 +119,8 @@ public static abstract class Builder extends BaseObject.Builder { public abstract Builder deleted(Boolean deleted); - public abstract Builder transactions(List transactions); + public abstract Builder transactions(List transactions); - public abstract InternalStockTheme build(); + public abstract InternalStockUseCase build(); } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockThemeTransaction.java b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseTransaction.java similarity index 86% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockThemeTransaction.java rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseTransaction.java index 8b67f4de20..38ed410932 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockThemeTransaction.java +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseTransaction.java @@ -26,7 +26,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock; +package org.hisp.dhis.android.core.usecase.stock; import static org.hisp.dhis.android.core.common.BaseIdentifiableObject.UID; @@ -43,8 +43,8 @@ import org.hisp.dhis.android.core.common.BaseObject; @AutoValue -@JsonDeserialize(builder = $$AutoValue_InternalStockThemeTransaction.Builder.class) -public abstract class InternalStockThemeTransaction extends BaseObject { +@JsonDeserialize(builder = $$AutoValue_InternalStockUseCaseTransaction.Builder.class) +public abstract class InternalStockUseCaseTransaction extends BaseObject { @Nullable public abstract String programUid(); @@ -73,14 +73,14 @@ public abstract class InternalStockThemeTransaction extends BaseObject { @JsonProperty() public abstract String stockCorrected(); - public static InternalStockThemeTransaction create(Cursor cursor) { - return AutoValue_InternalStockThemeTransaction.createFromCursor(cursor); + public static InternalStockUseCaseTransaction create(Cursor cursor) { + return AutoValue_InternalStockUseCaseTransaction.createFromCursor(cursor); } public abstract Builder toBuilder(); public static Builder builder() { - return new $$AutoValue_InternalStockThemeTransaction.Builder(); + return new $$AutoValue_InternalStockUseCaseTransaction.Builder(); } @AutoValue.Builder @@ -102,6 +102,6 @@ public static abstract class Builder extends BaseObject.Builder { public abstract Builder stockCorrected(String stockCorrected); - public abstract InternalStockThemeTransaction build(); + public abstract InternalStockUseCaseTransaction build(); } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockTheme.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCase.kt similarity index 93% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockTheme.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCase.kt index d0961eebea..04321e022e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockTheme.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCase.kt @@ -26,18 +26,18 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock +package org.hisp.dhis.android.core.usecase.stock import org.hisp.dhis.android.core.common.ObjectWithUidInterface -data class StockTheme( +data class StockUseCase( val programUid: String, val itemCode: String, val itemDescription: String, val programType: String, val description: String, val stockOnHand: String, - val transactions: List + val transactions: List ) : ObjectWithUidInterface { override fun uid(): String { return programUid diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeCollectionRepository.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt similarity index 74% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeCollectionRepository.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt index 01fee28167..914b1323e4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeCollectionRepository.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock +package org.hisp.dhis.android.core.usecase.stock import dagger.Reusable import javax.inject.Inject @@ -38,28 +38,28 @@ import org.hisp.dhis.android.core.arch.repositories.filters.internal.FilterConne import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope @Reusable -class StockThemeCollectionRepository @Inject internal constructor( - store: IdentifiableObjectStore, - childrenAppenders: MutableMap>, - scope: RepositoryScope, - transformer: TwoWayTransformer, -) : ReadOnlyWithUidCollectionRepository by - ReadOnlyWithUidAndTransformerCollectionRepositoryImpl( +class StockUseCaseCollectionRepository @Inject internal constructor( + store: IdentifiableObjectStore, + childrenAppenders: MutableMap>, + scope: RepositoryScope, + transformer: TwoWayTransformer, +) : ReadOnlyWithUidCollectionRepository by + ReadOnlyWithUidAndTransformerCollectionRepositoryImpl( store, childrenAppenders, scope, FilterConnectorFactory(scope) { s: RepositoryScope -> - StockThemeCollectionRepository(store, childrenAppenders, s, transformer) + StockUseCaseCollectionRepository(store, childrenAppenders, s, transformer) }, transformer ) { - private val cf: FilterConnectorFactory = + private val cf: FilterConnectorFactory = FilterConnectorFactory(scope) { s: RepositoryScope -> - StockThemeCollectionRepository(store, childrenAppenders, s, transformer) + StockUseCaseCollectionRepository(store, childrenAppenders, s, transformer) } - fun withTransactions(): StockThemeCollectionRepository { - return cf.withChild(InternalStockTheme.TRANSACTIONS) + fun withTransactions(): StockUseCaseCollectionRepository { + return cf.withChild(InternalStockUseCase.TRANSACTIONS) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTableInfo.kt similarity index 95% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTableInfo.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTableInfo.kt index 3645058765..3a39949b83 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTableInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTableInfo.kt @@ -25,19 +25,19 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock +package org.hisp.dhis.android.core.usecase.stock 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.DeletableDataColumns -object StockThemeTableInfo { +object StockUseCaseTableInfo { @JvmField val TABLE_INFO: TableInfo = object : TableInfo() { override fun name(): String { - return "StockTheme" + return "StockUseCase" } override fun columns(): CoreColumns { diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTransaction.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransaction.kt similarity index 87% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTransaction.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransaction.kt index 38ac4b2f27..adb9578c67 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTransaction.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransaction.kt @@ -25,9 +25,9 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock +package org.hisp.dhis.android.core.usecase.stock -sealed class StockThemeTransaction { +sealed class StockUseCaseTransaction { abstract val sortOrder: Int abstract val transactionType: TransactionType @@ -36,19 +36,19 @@ sealed class StockThemeTransaction { override val transactionType: TransactionType, val distributedTo: String, val stockDistributed: String - ) : StockThemeTransaction() + ) : StockUseCaseTransaction() data class Discarded( override val sortOrder: Int, override val transactionType: TransactionType, val stockDiscarded: String - ) : StockThemeTransaction() + ) : StockUseCaseTransaction() data class Correction( override val sortOrder: Int, override val transactionType: TransactionType, val stockCorrected: String - ) : StockThemeTransaction() + ) : StockUseCaseTransaction() companion object { enum class TransactionType { @@ -57,7 +57,7 @@ sealed class StockThemeTransaction { CORRECTED } - internal fun transformFrom(t: InternalStockThemeTransaction): StockThemeTransaction { + internal fun transformFrom(t: InternalStockUseCaseTransaction): StockUseCaseTransaction { return when (val type = TransactionType.valueOf(t.transactionType())) { TransactionType.DISTRIBUTED -> Distributed( @@ -69,8 +69,8 @@ sealed class StockThemeTransaction { } } - internal fun transformTo(programUid: String, t: StockThemeTransaction): InternalStockThemeTransaction { - val builder = InternalStockThemeTransaction.builder() + internal fun transformTo(programUid: String, t: StockUseCaseTransaction): InternalStockUseCaseTransaction { + val builder = InternalStockUseCaseTransaction.builder() .programUid(programUid) .transactionType(t.transactionType.name) .sortOrder(t.sortOrder) diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTransactionTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransactionTableInfo.kt similarity index 95% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTransactionTableInfo.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransactionTableInfo.kt index 0ee61e44ab..7a25af92a2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeTransactionTableInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransactionTableInfo.kt @@ -25,19 +25,19 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock +package org.hisp.dhis.android.core.usecase.stock 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.DeletableDataColumns -object StockThemeTransactionTableInfo { +object StockUseCaseTransactionTableInfo { @JvmField val TABLE_INFO: TableInfo = object : TableInfo() { override fun name(): String { - return "StockThemeTransaction" + return "StockUseCaseTransaction" } override fun columns(): CoreColumns { diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeCall.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseCall.kt similarity index 73% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeCall.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseCall.kt index c76a4b4005..2ff4ada6e8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseCall.kt @@ -25,29 +25,29 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import dagger.Reusable import io.reactivex.Single import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor import org.hisp.dhis.android.core.arch.handlers.internal.HandlerWithTransformer -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase import org.hisp.dhis.android.core.settings.internal.BaseSettingCall @Reusable -internal class StockThemeCall @Inject constructor( - private val stockThemeHandler: HandlerWithTransformer, - private val stockThemeService: StockThemeService, - private val apiCallExecutor: RxAPICallExecutor, -) : BaseSettingCall>() { +internal class StockUseCaseCall @Inject constructor( + private val stockUseCaseHandler: HandlerWithTransformer, + private val stockUseCaseService: StockUseCaseService, + private val apiCallExecutor: RxAPICallExecutor, +) : BaseSettingCall>() { - override fun fetch(storeError: Boolean): Single> { - return apiCallExecutor.wrapSingle(stockThemeService.stockThemes(), storeError = storeError) + override fun fetch(storeError: Boolean): Single> { + return apiCallExecutor.wrapSingle(stockUseCaseService.stockUseCases(), storeError = storeError) } - override fun process(item: List?) { - val stockThemes = item ?: emptyList() - stockThemeHandler.handleMany(stockThemes) + override fun process(item: List?) { + val stockUseCases = item ?: emptyList() + stockUseCaseHandler.handleMany(stockUseCases) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt similarity index 69% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeEntityDIModule.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt index 4b37d5cdf8..2107d8450f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import dagger.Module import dagger.Provides @@ -37,39 +37,39 @@ import org.hisp.dhis.android.core.arch.handlers.internal.HandlerWithTransformer import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandler import org.hisp.dhis.android.core.arch.handlers.internal.TwoWayTransformer import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme -import org.hisp.dhis.android.core.programtheme.stock.InternalStockThemeTransaction -import org.hisp.dhis.android.core.programtheme.stock.StockTheme +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction +import org.hisp.dhis.android.core.usecase.stock.StockUseCase @Module -internal class StockThemeEntityDIModule { +internal class StockUseCaseEntityDIModule { @Provides @Reusable - fun store(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { - return StockThemeStore.create(databaseAdapter) + fun store(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + return StockUseCaseStore.create(databaseAdapter) } @Provides @Reusable fun handler( - store: IdentifiableObjectStore, - linkHandler: LinkHandler - ): HandlerWithTransformer { - return StockThemeHandler(store, linkHandler) + store: IdentifiableObjectStore, + linkHandler: LinkHandler + ): HandlerWithTransformer { + return StockUseCaseHandler(store, linkHandler) } @Provides @Reusable - fun transformer(): TwoWayTransformer { - return StockThemeTransformer() + fun transformer(): TwoWayTransformer { + return StockUseCaseTransformer() } @Provides @Reusable - fun childrenAppenders(linkStore: LinkStore): - Map> { - val childrenAppender: ChildrenAppender = - StockThemeTransactionChildrenAppender(linkStore) - return mapOf(Pair(InternalStockTheme.TRANSACTIONS, childrenAppender)) + fun childrenAppenders(linkStore: LinkStore): + Map> { + val childrenAppender: ChildrenAppender = + StockUseCaseTransactionChildrenAppender(linkStore) + return mapOf(Pair(InternalStockUseCase.TRANSACTIONS, childrenAppender)) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt similarity index 77% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeHandler.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt index 41443d2e6b..469c660a11 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt @@ -25,28 +25,28 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandler -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme -import org.hisp.dhis.android.core.programtheme.stock.InternalStockThemeTransaction +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction -internal class StockThemeHandler @Inject constructor( - store: IdentifiableObjectStore, - private val transactionLinkHandler: LinkHandler, -) : IdentifiableHandlerImpl(store) { +internal class StockUseCaseHandler @Inject constructor( + store: IdentifiableObjectStore, + private val transactionLinkHandler: LinkHandler, +) : IdentifiableHandlerImpl(store) { - override fun beforeCollectionHandled(oCollection: Collection): Collection { + override fun beforeCollectionHandled(oCollection: Collection): Collection { store.delete() transactionLinkHandler.resetAllLinks() return oCollection } - override fun afterObjectHandled(o: InternalStockTheme, action: HandleAction) { + override fun afterObjectHandled(o: InternalStockUseCase, action: HandleAction) { transactionLinkHandler.handleMany(o.uid(), o.transactions()) { it.toBuilder().programUid(o.uid()).build() } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeService.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseService.kt similarity index 85% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeService.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseService.kt index 1851bfc0a5..b5eb87353e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseService.kt @@ -25,14 +25,14 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import io.reactivex.Single -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase import retrofit2.http.GET -internal interface StockThemeService { +internal interface StockUseCaseService { - @GET("dataStore/PROGRAM_THEMES/stockThemes") - fun stockThemes(): Single> + @GET("dataStore/USE_CASES/stockUseCases") + fun stockUseCases(): Single> } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeStore.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseStore.kt similarity index 82% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeStore.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseStore.kt index e46ad79d89..57e9795115 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseStore.kt @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import android.database.Cursor import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter @@ -33,12 +33,12 @@ import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinde import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapper import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.db.stores.internal.StoreFactory.objectWithUidStore -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme -import org.hisp.dhis.android.core.programtheme.stock.StockThemeTableInfo +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseTableInfo @Suppress("MagicNumber") -internal object StockThemeStore { - private val BINDER = StatementBinder { o: InternalStockTheme, w: StatementWrapper -> +internal object StockUseCaseStore { + private val BINDER = StatementBinder { o: InternalStockUseCase, w: StatementWrapper -> w.bind(1, o.uid()) w.bind(2, o.itemCode()) w.bind(3, o.itemDescription()) @@ -47,10 +47,10 @@ internal object StockThemeStore { w.bind(6, o.stockOnHand()) } - fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { + fun create(databaseAdapter: DatabaseAdapter): IdentifiableObjectStore { return objectWithUidStore( - databaseAdapter, StockThemeTableInfo.TABLE_INFO, + databaseAdapter, StockUseCaseTableInfo.TABLE_INFO, BINDER - ) { cursor: Cursor -> InternalStockTheme.create(cursor) } + ) { cursor: Cursor -> InternalStockUseCase.create(cursor) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionChildrenAppender.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionChildrenAppender.kt similarity index 76% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionChildrenAppender.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionChildrenAppender.kt index 8d2b5ddcb2..3149255c7d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionChildrenAppender.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionChildrenAppender.kt @@ -25,20 +25,20 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal 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.programtheme.stock.InternalStockTheme -import org.hisp.dhis.android.core.programtheme.stock.InternalStockThemeTransaction +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction -internal class StockThemeTransactionChildrenAppender( - private val transactionLinkStore: LinkStore -) : ChildrenAppender() { +internal class StockUseCaseTransactionChildrenAppender( + private val transactionLinkStore: LinkStore +) : ChildrenAppender() { - override fun appendChildren(internalStockTheme: InternalStockTheme): InternalStockTheme { - return internalStockTheme.toBuilder() - .transactions(transactionLinkStore.selectLinksForMasterUid(internalStockTheme.uid())) + override fun appendChildren(internalStockUseCase: InternalStockUseCase): InternalStockUseCase { + return internalStockUseCase.toBuilder() + .transactions(transactionLinkStore.selectLinksForMasterUid(internalStockUseCase.uid())) .build() } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionEntityDIModule.kt similarity index 82% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionEntityDIModule.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionEntityDIModule.kt index 2f8058a17e..1399d2929f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionEntityDIModule.kt @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import dagger.Module import dagger.Provides @@ -34,20 +34,20 @@ 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.programtheme.stock.InternalStockThemeTransaction +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction @Module -internal class StockThemeTransactionEntityDIModule { +internal class StockUseCaseTransactionEntityDIModule { @Provides @Reusable - fun store(databaseAdapter: DatabaseAdapter): LinkStore { - return StockThemeTransactionLinkStore.create(databaseAdapter) + fun store(databaseAdapter: DatabaseAdapter): LinkStore { + return StockUseCaseTransactionLinkStore.create(databaseAdapter) } @Provides @Reusable - fun handler(store: LinkStore): - LinkHandler { + fun handler(store: LinkStore): + LinkHandler { return LinkHandlerImpl(store) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionLinkStore.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionLinkStore.kt similarity index 76% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionLinkStore.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionLinkStore.kt index c9b42485fb..15fd1b12b0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransactionLinkStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionLinkStore.kt @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import android.database.Cursor import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter @@ -34,13 +34,13 @@ import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementWrapp 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.db.stores.projections.internal.SingleParentChildProjection -import org.hisp.dhis.android.core.programtheme.stock.InternalStockThemeTransaction -import org.hisp.dhis.android.core.programtheme.stock.StockThemeTransactionTableInfo +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseTransactionTableInfo @Suppress("MagicNumber") -internal object StockThemeTransactionLinkStore { - private val BINDER: StatementBinder = - StatementBinder { o: InternalStockThemeTransaction, w: StatementWrapper -> +internal object StockUseCaseTransactionLinkStore { + private val BINDER: StatementBinder = + StatementBinder { o: InternalStockUseCaseTransaction, w: StatementWrapper -> w.bind(1, o.programUid()) w.bind(2, o.sortOrder()) w.bind(3, o.transactionType()) @@ -51,15 +51,15 @@ internal object StockThemeTransactionLinkStore { } val CHILD_PROJECTION: SingleParentChildProjection = SingleParentChildProjection( - StockThemeTransactionTableInfo.TABLE_INFO, StockThemeTransactionTableInfo.Columns.PROGRAM_UID + StockUseCaseTransactionTableInfo.TABLE_INFO, StockUseCaseTransactionTableInfo.Columns.PROGRAM_UID ) - fun create(databaseAdapter: DatabaseAdapter): LinkStore { + fun create(databaseAdapter: DatabaseAdapter): LinkStore { return StoreFactory.linkStore( databaseAdapter, - StockThemeTransactionTableInfo.TABLE_INFO, - StockThemeTransactionTableInfo.Columns.PROGRAM_UID, + StockUseCaseTransactionTableInfo.TABLE_INFO, + StockUseCaseTransactionTableInfo.Columns.PROGRAM_UID, BINDER - ) { cursor: Cursor -> InternalStockThemeTransaction.create(cursor) } + ) { cursor: Cursor -> InternalStockUseCaseTransaction.create(cursor) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransformer.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransformer.kt similarity index 73% rename from core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransformer.kt rename to core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransformer.kt index 4398d3f319..6fee967efb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/programtheme/stock/internal/StockThemeTransformer.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransformer.kt @@ -25,37 +25,37 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock.internal +package org.hisp.dhis.android.core.usecase.stock.internal import dagger.Reusable import org.hisp.dhis.android.core.arch.handlers.internal.TwoWayTransformer -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme -import org.hisp.dhis.android.core.programtheme.stock.StockTheme -import org.hisp.dhis.android.core.programtheme.stock.StockThemeTransaction +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase +import org.hisp.dhis.android.core.usecase.stock.StockUseCase +import org.hisp.dhis.android.core.usecase.stock.StockUseCaseTransaction @Reusable -internal class StockThemeTransformer : TwoWayTransformer { - override fun transform(o: InternalStockTheme): StockTheme { - return StockTheme( +internal class StockUseCaseTransformer : TwoWayTransformer { + override fun transform(o: InternalStockUseCase): StockUseCase { + return StockUseCase( o.uid(), o.itemCode(), o.itemDescription(), o.programType(), o.description(), o.stockOnHand(), - o.transactions()?.map { StockThemeTransaction.transformFrom(it) } ?: emptyList() + o.transactions()?.map { StockUseCaseTransaction.transformFrom(it) } ?: emptyList() ) } - override fun deTransform(t: StockTheme): InternalStockTheme { - return InternalStockTheme.builder() + override fun deTransform(t: StockUseCase): InternalStockUseCase { + return InternalStockUseCase.builder() .uid(t.programUid) .itemCode(t.itemCode) .itemDescription(t.itemDescription) .programType(t.programType) .description(t.description) .stockOnHand(t.stockOnHand) - .transactions(t.transactions.map { StockThemeTransaction.transformTo(t.programUid, it) }) + .transactions(t.transactions.map { StockUseCaseTransaction.transformTo(t.programUid, it) }) .build() } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt b/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt index 29f470ffcf..1ba4653fec 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt @@ -49,7 +49,7 @@ import org.hisp.dhis.android.core.option.internal.OptionModuleWiper import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitModuleWiper import org.hisp.dhis.android.core.period.internal.PeriodModuleWiper import org.hisp.dhis.android.core.program.internal.ProgramModuleWiper -import org.hisp.dhis.android.core.programtheme.internal.ProgramThemeModuleWiper +import org.hisp.dhis.android.core.usecase.internal.UseCaseModuleWiper import org.hisp.dhis.android.core.relationship.internal.RelationshipModuleWiper import org.hisp.dhis.android.core.resource.internal.ResourceModuleWiper import org.hisp.dhis.android.core.settings.internal.SettingModuleWiper @@ -64,37 +64,37 @@ import org.hisp.dhis.android.core.visualization.internal.VisualizationModuleWipe @Reusable @Suppress("LongParameterList") internal class D2ModuleWipers @Inject constructor( - attribute: AttributeModuleWiper, - category: CategoryModuleWiper, - common: CommonModuleWiper, - constant: ConstantModuleWiper, - dataElement: DataElementModuleWiper, - dataSet: DataSetModuleWiper, - dataValue: DataValueModuleWiper, - enrollment: EnrollmentModuleWiper, - event: EventModuleWiper, - fileResource: FileResourceModuleWiper, - importModule: ImportModuleWiper, - indicator: IndicatorModuleWiper, - legendSet: LegendSetModuleWiper, - localDataStore: LocalDataStoreModuleWiper, - maintenance: MaintenanceModuleWiper, - map: MapModuleWiper, - option: OptionModuleWiper, - organisationUnit: OrganisationUnitModuleWiper, - period: PeriodModuleWiper, - program: ProgramModuleWiper, - programTheme: ProgramThemeModuleWiper, - relationship: RelationshipModuleWiper, - resource: ResourceModuleWiper, - smsModuleWiper: SMSModuleWiper, - systemInfo: SystemInfoModuleWiper, - systemSetting: SettingModuleWiper, - trackedEntity: TrackedEntityModuleWiper, - trackerJob: TrackerJobModuleWiper, - user: UserModuleWiper, - validation: ValidationModuleWiper, - visualization: VisualizationModuleWiper + attribute: AttributeModuleWiper, + category: CategoryModuleWiper, + common: CommonModuleWiper, + constant: ConstantModuleWiper, + dataElement: DataElementModuleWiper, + dataSet: DataSetModuleWiper, + dataValue: DataValueModuleWiper, + enrollment: EnrollmentModuleWiper, + event: EventModuleWiper, + fileResource: FileResourceModuleWiper, + importModule: ImportModuleWiper, + indicator: IndicatorModuleWiper, + legendSet: LegendSetModuleWiper, + localDataStore: LocalDataStoreModuleWiper, + maintenance: MaintenanceModuleWiper, + map: MapModuleWiper, + option: OptionModuleWiper, + organisationUnit: OrganisationUnitModuleWiper, + period: PeriodModuleWiper, + program: ProgramModuleWiper, + useCase: UseCaseModuleWiper, + relationship: RelationshipModuleWiper, + resource: ResourceModuleWiper, + smsModuleWiper: SMSModuleWiper, + systemInfo: SystemInfoModuleWiper, + systemSetting: SettingModuleWiper, + trackedEntity: TrackedEntityModuleWiper, + trackerJob: TrackerJobModuleWiper, + user: UserModuleWiper, + validation: ValidationModuleWiper, + visualization: VisualizationModuleWiper ) { val wipers: List @@ -120,7 +120,7 @@ internal class D2ModuleWipers @Inject constructor( organisationUnit, period, program, - programTheme, + useCase, relationship, resource, smsModuleWiper, diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/programtheme/stock/InternalStockThemeSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseSamples.kt similarity index 85% rename from core/src/sharedTest/java/org/hisp/dhis/android/core/data/programtheme/stock/InternalStockThemeSamples.kt rename to core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseSamples.kt index c58b1e3b05..c878007f46 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/programtheme/stock/InternalStockThemeSamples.kt +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseSamples.kt @@ -25,13 +25,13 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.data.programtheme.stock +package org.hisp.dhis.android.core.data.usecase.stock -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase -internal object InternalStockThemeSamples { - fun get(): InternalStockTheme { - return InternalStockTheme.builder() +internal object InternalStockUseCaseSamples { + fun get(): InternalStockUseCase { + return InternalStockUseCase.builder() .id(1L) .uid("program_uid") .stockOnHand("stock_on_hand") @@ -40,7 +40,7 @@ internal object InternalStockThemeSamples { .itemCode("item_code") .itemDescription("item_description") .deleted(false) - .transactions(listOf(InternalStockThemeTransactionSamples.get())) + .transactions(listOf(InternalStockUseCaseTransactionSamples.get())) .build() } } diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/programtheme/stock/InternalStockThemeTransactionSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseTransactionSamples.kt similarity index 86% rename from core/src/sharedTest/java/org/hisp/dhis/android/core/data/programtheme/stock/InternalStockThemeTransactionSamples.kt rename to core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseTransactionSamples.kt index 0658e91409..cbbec238b7 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/programtheme/stock/InternalStockThemeTransactionSamples.kt +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseTransactionSamples.kt @@ -25,13 +25,13 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.data.programtheme.stock +package org.hisp.dhis.android.core.data.usecase.stock -import org.hisp.dhis.android.core.programtheme.stock.InternalStockThemeTransaction +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction -internal object InternalStockThemeTransactionSamples { - fun get(): InternalStockThemeTransaction { - return InternalStockThemeTransaction.builder() +internal object InternalStockUseCaseTransactionSamples { + fun get(): InternalStockUseCaseTransaction { + return InternalStockUseCaseTransaction.builder() .id(1L) .programUid("program_uid") .sortOrder(2) diff --git a/core/src/sharedTest/resources/programtheme.stock/stock_theme.json b/core/src/sharedTest/resources/usecase.stock/stock_use_case.json similarity index 100% rename from core/src/sharedTest/resources/programtheme.stock/stock_theme.json rename to core/src/sharedTest/resources/usecase.stock/stock_use_case.json diff --git a/core/src/sharedTest/resources/programtheme.stock/stock_themes.json b/core/src/sharedTest/resources/usecase.stock/stock_use_cases.json similarity index 100% rename from core/src/sharedTest/resources/programtheme.stock/stock_themes.json rename to core/src/sharedTest/resources/usecase.stock/stock_use_cases.json 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 06347f89e3..264d936e47 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 @@ -47,7 +47,7 @@ import org.hisp.dhis.android.core.maintenance.ForeignKeyViolationTableInfo import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitModuleDownloader import org.hisp.dhis.android.core.program.internal.ProgramIndicatorModuleDownloader import org.hisp.dhis.android.core.program.internal.ProgramModuleDownloader -import org.hisp.dhis.android.core.programtheme.ProgramThemeModuleDownloader +import org.hisp.dhis.android.core.usecase.UseCaseModuleDownloader import org.hisp.dhis.android.core.settings.internal.GeneralSettingCall import org.hisp.dhis.android.core.settings.internal.SettingModuleDownloader import org.hisp.dhis.android.core.sms.SmsModule @@ -69,7 +69,7 @@ class MetadataCallShould : BaseCallShould() { private val rxAPICallExecutor: RxAPICallExecutor = mock() private val systemInfoDownloader: SystemInfoModuleDownloader = mock() private val systemSettingDownloader: SettingModuleDownloader = mock() - private val programThemeModuleDownloader: ProgramThemeModuleDownloader = mock() + private val useCaseModuleDownloader: UseCaseModuleDownloader = mock() private val userDownloader: UserModuleDownloader = mock() private val categoryDownloader: CategoryModuleDownloader = mock() private val programDownloader: ProgramModuleDownloader = mock() @@ -98,7 +98,7 @@ class MetadataCallShould : BaseCallShould() { whenever(systemInfoDownloader.downloadWithProgressManager(any())) .thenReturn(Observable.just(BaseD2Progress.empty(10))) whenever(systemSettingDownloader.downloadMetadata()).thenReturn(Completable.complete()) - whenever(programThemeModuleDownloader.downloadMetadata()).thenReturn(Completable.complete()) + whenever(useCaseModuleDownloader.downloadMetadata()).thenReturn(Completable.complete()) whenever(userDownloader.downloadMetadata()).thenReturn(Single.just(user)) whenever(programDownloader.downloadMetadata()).thenReturn( Completable.complete() @@ -132,7 +132,7 @@ class MetadataCallShould : BaseCallShould() { rxAPICallExecutor, systemInfoDownloader, systemSettingDownloader, - programThemeModuleDownloader, + useCaseModuleDownloader, userDownloader, categoryDownloader, programDownloader, diff --git a/core/src/test/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockThemeShould.kt b/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseShould.kt similarity index 71% rename from core/src/test/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockThemeShould.kt rename to core/src/test/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseShould.kt index d8950fcdd1..2b5ce2c958 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/programtheme/stock/InternalStockThemeShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseShould.kt @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock +package org.hisp.dhis.android.core.usecase.stock import com.fasterxml.jackson.databind.ObjectMapper import com.google.common.truth.Truth @@ -33,25 +33,25 @@ import java.io.InputStream import org.hisp.dhis.android.core.Inject import org.junit.Test -class InternalStockThemeShould { +class InternalStockUseCaseShould { private var objectMapper: ObjectMapper = Inject.objectMapper() private var jsonStream: InputStream = this.javaClass.classLoader!! - .getResourceAsStream("programtheme.stock/stock_theme.json") + .getResourceAsStream("usecase.stock/stock_use_case.json") @Test fun map_from_json_string() { - val internalStockTheme: InternalStockTheme = objectMapper.readValue(jsonStream, InternalStockTheme::class.java) + val internalStockUseCase: InternalStockUseCase = objectMapper.readValue(jsonStream, InternalStockUseCase::class.java) - Truth.assertThat(internalStockTheme).isNotNull() - Truth.assertThat(internalStockTheme.uid()).isEqualTo("IpHINAT79UW") - Truth.assertThat(internalStockTheme.itemCode()).isEqualTo("wBr4wccNBj1") - Truth.assertThat(internalStockTheme.itemDescription()).isEqualTo("MBczRWvfM46") - Truth.assertThat(internalStockTheme.programType()).isEqualTo("logistics") - Truth.assertThat(internalStockTheme.description()).isEqualTo("this is a logistics program, stock management") - Truth.assertThat(internalStockTheme.stockOnHand()).isEqualTo("ypCQAFr1a5l") - Truth.assertThat(internalStockTheme.uid()).isEqualTo("IpHINAT79UW") + Truth.assertThat(internalStockUseCase).isNotNull() + Truth.assertThat(internalStockUseCase.uid()).isEqualTo("IpHINAT79UW") + Truth.assertThat(internalStockUseCase.itemCode()).isEqualTo("wBr4wccNBj1") + Truth.assertThat(internalStockUseCase.itemDescription()).isEqualTo("MBczRWvfM46") + Truth.assertThat(internalStockUseCase.programType()).isEqualTo("logistics") + Truth.assertThat(internalStockUseCase.description()).isEqualTo("this is a logistics program, stock management") + Truth.assertThat(internalStockUseCase.stockOnHand()).isEqualTo("ypCQAFr1a5l") + Truth.assertThat(internalStockUseCase.uid()).isEqualTo("IpHINAT79UW") - val transactions = internalStockTheme.transactions() + val transactions = internalStockUseCase.transactions() Truth.assertThat(transactions?.size).isEqualTo(3) Truth.assertThat(transactions?.get(0)?.sortOrder()).isEqualTo(0) Truth.assertThat(transactions?.get(0)?.transactionType()).isEqualTo("DISTRIBUTED") diff --git a/core/src/test/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeCallShould.kt b/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCallShould.kt similarity index 74% rename from core/src/test/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeCallShould.kt rename to core/src/test/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCallShould.kt index 0c0c3f6767..0046f2343d 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/programtheme/stock/StockThemeCallShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCallShould.kt @@ -25,41 +25,41 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.programtheme.stock +package org.hisp.dhis.android.core.usecase.stock import com.nhaarman.mockitokotlin2.* import io.reactivex.Single import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl import org.hisp.dhis.android.core.maintenance.D2ErrorSamples -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeCall -import org.hisp.dhis.android.core.programtheme.stock.internal.StockThemeService +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseCall +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseService import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @RunWith(JUnit4::class) -class StockThemeCallShould { - private val handler: IdentifiableHandlerImpl = mock() - private val service: StockThemeService = mock() - private val stockThemeSingle: Single> = mock() +class StockUseCaseCallShould { + private val handler: IdentifiableHandlerImpl = mock() + private val service: StockUseCaseService = mock() + private val stockUseCaseSingle: Single> = mock() private val apiCallExecutor: RxAPICallExecutor = mock() - private lateinit var stockThemeCall: StockThemeCall + private lateinit var stockUseCaseCall: StockUseCaseCall @Before fun setUp() { - whenever(service.stockThemes()) doReturn stockThemeSingle - stockThemeCall = StockThemeCall(handler, service, apiCallExecutor) + whenever(service.stockUseCases()) doReturn stockUseCaseSingle + stockUseCaseCall = StockUseCaseCall(handler, service, apiCallExecutor) } @Test fun default_to_empty_collection_if_not_found() { - whenever(apiCallExecutor.wrapSingle(stockThemeSingle, false)) doReturn + whenever(apiCallExecutor.wrapSingle(stockUseCaseSingle, false)) doReturn Single.error(D2ErrorSamples.notFound()) - stockThemeCall.getCompletable(false).blockingAwait() + stockUseCaseCall.getCompletable(false).blockingAwait() verify(handler).handleMany(emptyList()) verifyNoMoreInteractions(handler) diff --git a/core/src/test/java/org/hisp/dhis/android/testapp/programtheme/stock/InternalStockThemePublicAccessShould.java b/core/src/test/java/org/hisp/dhis/android/testapp/usecase/stock/InternalStockUseCasePublicAccessShould.java similarity index 82% rename from core/src/test/java/org/hisp/dhis/android/testapp/programtheme/stock/InternalStockThemePublicAccessShould.java rename to core/src/test/java/org/hisp/dhis/android/testapp/usecase/stock/InternalStockUseCasePublicAccessShould.java index ddda74561e..d09145efae 100644 --- a/core/src/test/java/org/hisp/dhis/android/testapp/programtheme/stock/InternalStockThemePublicAccessShould.java +++ b/core/src/test/java/org/hisp/dhis/android/testapp/usecase/stock/InternalStockUseCasePublicAccessShould.java @@ -26,31 +26,31 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.testapp.programtheme.stock; +package org.hisp.dhis.android.testapp.usecase.stock; -import org.hisp.dhis.android.core.programtheme.stock.InternalStockTheme; +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase; import org.hisp.dhis.android.testapp.arch.BasePublicAccessShould; import org.mockito.Mock; -public class InternalStockThemePublicAccessShould - extends BasePublicAccessShould { +public class InternalStockUseCasePublicAccessShould + extends BasePublicAccessShould { @Mock - private InternalStockTheme object; + private InternalStockUseCase object; @Override - public InternalStockTheme object() { + public InternalStockUseCase object() { return object; } @Override public void has_public_create_method() { - InternalStockTheme.create(null); + InternalStockUseCase.create(null); } @Override public void has_public_builder_method() { - InternalStockTheme.builder(); + InternalStockUseCase.builder(); } @Override diff --git a/core/src/test/java/org/hisp/dhis/android/testapp/programtheme/stock/InternalStockThemeTransactionPublicAccessShould.java b/core/src/test/java/org/hisp/dhis/android/testapp/usecase/stock/InternalStockUseCaseTransactionPublicAccessShould.java similarity index 80% rename from core/src/test/java/org/hisp/dhis/android/testapp/programtheme/stock/InternalStockThemeTransactionPublicAccessShould.java rename to core/src/test/java/org/hisp/dhis/android/testapp/usecase/stock/InternalStockUseCaseTransactionPublicAccessShould.java index ab417ca210..bc084f39d3 100644 --- a/core/src/test/java/org/hisp/dhis/android/testapp/programtheme/stock/InternalStockThemeTransactionPublicAccessShould.java +++ b/core/src/test/java/org/hisp/dhis/android/testapp/usecase/stock/InternalStockUseCaseTransactionPublicAccessShould.java @@ -26,31 +26,31 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.testapp.programtheme.stock; +package org.hisp.dhis.android.testapp.usecase.stock; -import org.hisp.dhis.android.core.programtheme.stock.InternalStockThemeTransaction; +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction; import org.hisp.dhis.android.testapp.arch.BasePublicAccessShould; import org.mockito.Mock; -public class InternalStockThemeTransactionPublicAccessShould - extends BasePublicAccessShould { +public class InternalStockUseCaseTransactionPublicAccessShould + extends BasePublicAccessShould { @Mock - private InternalStockThemeTransaction object; + private InternalStockUseCaseTransaction object; @Override - public InternalStockThemeTransaction object() { + public InternalStockUseCaseTransaction object() { return object; } @Override public void has_public_create_method() { - InternalStockThemeTransaction.create(null); + InternalStockUseCaseTransaction.create(null); } @Override public void has_public_builder_method() { - InternalStockThemeTransaction.builder(); + InternalStockUseCaseTransaction.builder(); } @Override From 44143d34fcb77d0addc1934074637d82ed5244b9 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 14 Dec 2022 08:28:35 +0100 Subject: [PATCH 008/201] Drop ProgramThemes tables and ktlintFormat --- core/src/main/assets/migrations/136.sql | 2 + .../java/org/hisp/dhis/android/core/D2.kt | 2 +- .../core/arch/d2/internal/D2DIComponent.kt | 2 +- .../core/arch/d2/internal/D2Modules.kt | 60 ++++++++--------- .../core/domain/metadata/MetadataCall.kt | 42 ++++++------ .../stock/StockUseCaseCollectionRepository.kt | 8 +-- .../stock/internal/StockUseCaseCall.kt | 8 +-- .../internal/StockUseCaseEntityDIModule.kt | 4 +- .../stock/internal/StockUseCaseHandler.kt | 8 ++- .../core/wipe/internal/D2ModuleWipers.kt | 64 +++++++++---------- .../domain/metadata/MetadataCallShould.kt | 2 +- .../stock/InternalStockUseCaseShould.kt | 3 +- .../instrumentedTestApp/TestLabActivity.kt | 3 +- 13 files changed, 106 insertions(+), 102 deletions(-) diff --git a/core/src/main/assets/migrations/136.sql b/core/src/main/assets/migrations/136.sql index b0fda06957..7977c66f58 100644 --- a/core/src/main/assets/migrations/136.sql +++ b/core/src/main/assets/migrations/136.sql @@ -1,4 +1,6 @@ # Rename to StockUseCases (ANDROSDK-1602); +DROP TABLE IF EXISTS StockThemeTransaction; +DROP TABLE IF EXISTS StockTheme; CREATE TABLE StockUseCase (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, itemCode TEXT, itemDescription TEXT, programType TEXT, description TEXT, stockOnHand TEXT, FOREIGN KEY (uid) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE StockUseCaseTransaction (_id INTEGER PRIMARY KEY AUTOINCREMENT, programUid TEXT NOT NULL, sortOrder INTEGER, transactionType TEXT, distributedTo TEXT, stockDistributed TEXT, stockDiscarded TEXT, stockCorrected TEXT, FOREIGN KEY (programUid) REFERENCES StockUseCase (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); diff --git a/core/src/main/java/org/hisp/dhis/android/core/D2.kt b/core/src/main/java/org/hisp/dhis/android/core/D2.kt index dd42c82218..5621e7e2a0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/D2.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/D2.kt @@ -54,12 +54,12 @@ import org.hisp.dhis.android.core.option.OptionModule import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModule import org.hisp.dhis.android.core.period.PeriodModule import org.hisp.dhis.android.core.program.ProgramModule -import org.hisp.dhis.android.core.usecase.UseCaseModule import org.hisp.dhis.android.core.relationship.RelationshipModule import org.hisp.dhis.android.core.settings.SettingModule import org.hisp.dhis.android.core.sms.SmsModule import org.hisp.dhis.android.core.systeminfo.SystemInfoModule import org.hisp.dhis.android.core.trackedentity.TrackedEntityModule +import org.hisp.dhis.android.core.usecase.UseCaseModule import org.hisp.dhis.android.core.user.UserModule import org.hisp.dhis.android.core.validation.ValidationModule import org.hisp.dhis.android.core.visualization.VisualizationModule diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt index 5c06a36c8d..f24e951377 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2DIComponent.kt @@ -79,7 +79,6 @@ import org.hisp.dhis.android.core.period.internal.PeriodHandler import org.hisp.dhis.android.core.period.internal.PeriodPackageDIModule import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.program.internal.ProgramPackageDIModule -import org.hisp.dhis.android.core.usecase.internal.UseCasePackageDIModule import org.hisp.dhis.android.core.relationship.RelationshipType import org.hisp.dhis.android.core.relationship.internal.RelationshipPackageDIModule import org.hisp.dhis.android.core.resource.internal.ResourcePackageDIModule @@ -92,6 +91,7 @@ import org.hisp.dhis.android.core.trackedentity.internal.OldTrackerImporterPaylo import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityAttributeLegendSetDIModule import org.hisp.dhis.android.core.tracker.importer.internal.TrackerImporterPackageDIModule import org.hisp.dhis.android.core.tracker.importer.internal.interpreters.InterpreterSelector +import org.hisp.dhis.android.core.usecase.internal.UseCasePackageDIModule 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 diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt index a6f343d5fb..2ae042b367 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/d2/internal/D2Modules.kt @@ -49,12 +49,12 @@ import org.hisp.dhis.android.core.option.OptionModule import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModule import org.hisp.dhis.android.core.period.PeriodModule import org.hisp.dhis.android.core.program.ProgramModule -import org.hisp.dhis.android.core.usecase.UseCaseModule import org.hisp.dhis.android.core.relationship.RelationshipModule import org.hisp.dhis.android.core.settings.SettingModule import org.hisp.dhis.android.core.sms.SmsModule import org.hisp.dhis.android.core.systeminfo.SystemInfoModule import org.hisp.dhis.android.core.trackedentity.TrackedEntityModule +import org.hisp.dhis.android.core.usecase.UseCaseModule import org.hisp.dhis.android.core.user.UserModule import org.hisp.dhis.android.core.validation.ValidationModule import org.hisp.dhis.android.core.visualization.VisualizationModule @@ -62,33 +62,33 @@ import org.hisp.dhis.android.core.visualization.VisualizationModule @Reusable @Suppress("LongParameterList") internal class D2Modules @Inject constructor( - val analytics: AnalyticsModule, - val category: CategoryModule, - val constant: ConstantModule, - val dataElement: DataElementModule, - val dataSet: DataSetModule, - val option: OptionModule, - val dataValue: DataValueModule, - val enrollment: EnrollmentModule, - val event: EventModule, - val fileResource: FileResourceModule, - val importModule: ImportModule, - val indicator: IndicatorModule, - val legendSet: LegendSetModule, - val dataStore: DataStoreModule, - val maintenance: MaintenanceModule, - val maps: MapModule, - val note: NoteModule, - val program: ProgramModule, - val useCase: UseCaseModule, - val organisationUnit: OrganisationUnitModule, - val systemInfo: SystemInfoModule, - val settingModule: SettingModule, - val periodModule: PeriodModule, - val relationship: RelationshipModule, - val trackedEntity: TrackedEntityModule, - val user: UserModule, - val validation: ValidationModule, - val visualization: VisualizationModule, - val sms: SmsModule + val analytics: AnalyticsModule, + val category: CategoryModule, + val constant: ConstantModule, + val dataElement: DataElementModule, + val dataSet: DataSetModule, + val option: OptionModule, + val dataValue: DataValueModule, + val enrollment: EnrollmentModule, + val event: EventModule, + val fileResource: FileResourceModule, + val importModule: ImportModule, + val indicator: IndicatorModule, + val legendSet: LegendSetModule, + val dataStore: DataStoreModule, + val maintenance: MaintenanceModule, + val maps: MapModule, + val note: NoteModule, + val program: ProgramModule, + val useCase: UseCaseModule, + val organisationUnit: OrganisationUnitModule, + val systemInfo: SystemInfoModule, + val settingModule: SettingModule, + val periodModule: PeriodModule, + val relationship: RelationshipModule, + val trackedEntity: TrackedEntityModule, + val user: UserModule, + val validation: ValidationModule, + val visualization: VisualizationModule, + val sms: SmsModule ) 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 ad560d98bf..f8fd8b426e 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 @@ -55,14 +55,14 @@ import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.program.ProgramIndicator import org.hisp.dhis.android.core.program.internal.ProgramIndicatorModuleDownloader import org.hisp.dhis.android.core.program.internal.ProgramModuleDownloader -import org.hisp.dhis.android.core.usecase.UseCaseModuleDownloader -import org.hisp.dhis.android.core.usecase.stock.StockUseCase import org.hisp.dhis.android.core.settings.SystemSetting import org.hisp.dhis.android.core.settings.internal.GeneralSettingCall import org.hisp.dhis.android.core.settings.internal.SettingModuleDownloader import org.hisp.dhis.android.core.sms.SmsModule import org.hisp.dhis.android.core.systeminfo.SystemInfo import org.hisp.dhis.android.core.systeminfo.internal.SystemInfoModuleDownloader +import org.hisp.dhis.android.core.usecase.UseCaseModuleDownloader +import org.hisp.dhis.android.core.usecase.stock.StockUseCase 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 @@ -71,25 +71,25 @@ import org.hisp.dhis.android.core.visualization.internal.VisualizationModuleDown @Suppress("LongParameterList") @Reusable internal class MetadataCall @Inject constructor( - private val rxCallExecutor: RxAPICallExecutor, - private val systemInfoDownloader: SystemInfoModuleDownloader, - private val systemSettingDownloader: SettingModuleDownloader, - private val useCaseDownloader: UseCaseModuleDownloader, - private val userModuleDownloader: UserModuleDownloader, - private val categoryDownloader: CategoryModuleDownloader, - private val programDownloader: ProgramModuleDownloader, - private val organisationUnitModuleDownloader: OrganisationUnitModuleDownloader, - private val dataSetDownloader: DataSetModuleDownloader, - private val visualizationDownloader: VisualizationModuleDownloader, - private val constantModuleDownloader: ConstantModuleDownloader, - private val indicatorModuleDownloader: IndicatorModuleDownloader, - private val programIndicatorModuleDownloader: ProgramIndicatorModuleDownloader, - private val smsModule: SmsModule, - private val databaseAdapter: DatabaseAdapter, - private val generalSettingCall: GeneralSettingCall, - private val multiUserDatabaseManager: MultiUserDatabaseManager, - private val credentialsSecureStore: CredentialsSecureStore, - private val legendSetModuleDownloader: LegendSetModuleDownloader, + private val rxCallExecutor: RxAPICallExecutor, + private val systemInfoDownloader: SystemInfoModuleDownloader, + private val systemSettingDownloader: SettingModuleDownloader, + private val useCaseDownloader: UseCaseModuleDownloader, + private val userModuleDownloader: UserModuleDownloader, + private val categoryDownloader: CategoryModuleDownloader, + private val programDownloader: ProgramModuleDownloader, + private val organisationUnitModuleDownloader: OrganisationUnitModuleDownloader, + private val dataSetDownloader: DataSetModuleDownloader, + private val visualizationDownloader: VisualizationModuleDownloader, + private val constantModuleDownloader: ConstantModuleDownloader, + private val indicatorModuleDownloader: IndicatorModuleDownloader, + private val programIndicatorModuleDownloader: ProgramIndicatorModuleDownloader, + private val smsModule: SmsModule, + private val databaseAdapter: DatabaseAdapter, + private val generalSettingCall: GeneralSettingCall, + private val multiUserDatabaseManager: MultiUserDatabaseManager, + private val credentialsSecureStore: CredentialsSecureStore, + private val legendSetModuleDownloader: LegendSetModuleDownloader, ) { companion object { diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt index 914b1323e4..a940033c6b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt @@ -39,10 +39,10 @@ import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope @Reusable class StockUseCaseCollectionRepository @Inject internal constructor( - store: IdentifiableObjectStore, - childrenAppenders: MutableMap>, - scope: RepositoryScope, - transformer: TwoWayTransformer, + store: IdentifiableObjectStore, + childrenAppenders: MutableMap>, + scope: RepositoryScope, + transformer: TwoWayTransformer, ) : ReadOnlyWithUidCollectionRepository by ReadOnlyWithUidAndTransformerCollectionRepositoryImpl( diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseCall.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseCall.kt index 2ff4ada6e8..fdef4ae025 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseCall.kt @@ -32,14 +32,14 @@ import io.reactivex.Single import javax.inject.Inject import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor import org.hisp.dhis.android.core.arch.handlers.internal.HandlerWithTransformer -import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase import org.hisp.dhis.android.core.settings.internal.BaseSettingCall +import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase @Reusable internal class StockUseCaseCall @Inject constructor( - private val stockUseCaseHandler: HandlerWithTransformer, - private val stockUseCaseService: StockUseCaseService, - private val apiCallExecutor: RxAPICallExecutor, + private val stockUseCaseHandler: HandlerWithTransformer, + private val stockUseCaseService: StockUseCaseService, + private val apiCallExecutor: RxAPICallExecutor, ) : BaseSettingCall>() { override fun fetch(storeError: Boolean): Single> { diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt index 2107d8450f..23b3621f20 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt @@ -52,8 +52,8 @@ internal class StockUseCaseEntityDIModule { @Provides @Reusable fun handler( - store: IdentifiableObjectStore, - linkHandler: LinkHandler + store: IdentifiableObjectStore, + linkHandler: LinkHandler ): HandlerWithTransformer { return StockUseCaseHandler(store, linkHandler) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt index 469c660a11..b2ef770fe2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt @@ -36,11 +36,13 @@ import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction internal class StockUseCaseHandler @Inject constructor( - store: IdentifiableObjectStore, - private val transactionLinkHandler: LinkHandler, + store: IdentifiableObjectStore, + private val transactionLinkHandler: LinkHandler, ) : IdentifiableHandlerImpl(store) { - override fun beforeCollectionHandled(oCollection: Collection): Collection { + override fun beforeCollectionHandled( + oCollection: Collection + ): Collection { store.delete() transactionLinkHandler.resetAllLinks() return oCollection diff --git a/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt b/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt index 1ba4653fec..33041434cd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/wipe/internal/D2ModuleWipers.kt @@ -49,7 +49,6 @@ import org.hisp.dhis.android.core.option.internal.OptionModuleWiper import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitModuleWiper import org.hisp.dhis.android.core.period.internal.PeriodModuleWiper import org.hisp.dhis.android.core.program.internal.ProgramModuleWiper -import org.hisp.dhis.android.core.usecase.internal.UseCaseModuleWiper import org.hisp.dhis.android.core.relationship.internal.RelationshipModuleWiper import org.hisp.dhis.android.core.resource.internal.ResourceModuleWiper import org.hisp.dhis.android.core.settings.internal.SettingModuleWiper @@ -57,6 +56,7 @@ import org.hisp.dhis.android.core.sms.internal.SMSModuleWiper import org.hisp.dhis.android.core.systeminfo.internal.SystemInfoModuleWiper import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityModuleWiper import org.hisp.dhis.android.core.tracker.importer.internal.TrackerJobModuleWiper +import org.hisp.dhis.android.core.usecase.internal.UseCaseModuleWiper 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 @@ -64,37 +64,37 @@ import org.hisp.dhis.android.core.visualization.internal.VisualizationModuleWipe @Reusable @Suppress("LongParameterList") internal class D2ModuleWipers @Inject constructor( - attribute: AttributeModuleWiper, - category: CategoryModuleWiper, - common: CommonModuleWiper, - constant: ConstantModuleWiper, - dataElement: DataElementModuleWiper, - dataSet: DataSetModuleWiper, - dataValue: DataValueModuleWiper, - enrollment: EnrollmentModuleWiper, - event: EventModuleWiper, - fileResource: FileResourceModuleWiper, - importModule: ImportModuleWiper, - indicator: IndicatorModuleWiper, - legendSet: LegendSetModuleWiper, - localDataStore: LocalDataStoreModuleWiper, - maintenance: MaintenanceModuleWiper, - map: MapModuleWiper, - option: OptionModuleWiper, - organisationUnit: OrganisationUnitModuleWiper, - period: PeriodModuleWiper, - program: ProgramModuleWiper, - useCase: UseCaseModuleWiper, - relationship: RelationshipModuleWiper, - resource: ResourceModuleWiper, - smsModuleWiper: SMSModuleWiper, - systemInfo: SystemInfoModuleWiper, - systemSetting: SettingModuleWiper, - trackedEntity: TrackedEntityModuleWiper, - trackerJob: TrackerJobModuleWiper, - user: UserModuleWiper, - validation: ValidationModuleWiper, - visualization: VisualizationModuleWiper + attribute: AttributeModuleWiper, + category: CategoryModuleWiper, + common: CommonModuleWiper, + constant: ConstantModuleWiper, + dataElement: DataElementModuleWiper, + dataSet: DataSetModuleWiper, + dataValue: DataValueModuleWiper, + enrollment: EnrollmentModuleWiper, + event: EventModuleWiper, + fileResource: FileResourceModuleWiper, + importModule: ImportModuleWiper, + indicator: IndicatorModuleWiper, + legendSet: LegendSetModuleWiper, + localDataStore: LocalDataStoreModuleWiper, + maintenance: MaintenanceModuleWiper, + map: MapModuleWiper, + option: OptionModuleWiper, + organisationUnit: OrganisationUnitModuleWiper, + period: PeriodModuleWiper, + program: ProgramModuleWiper, + useCase: UseCaseModuleWiper, + relationship: RelationshipModuleWiper, + resource: ResourceModuleWiper, + smsModuleWiper: SMSModuleWiper, + systemInfo: SystemInfoModuleWiper, + systemSetting: SettingModuleWiper, + trackedEntity: TrackedEntityModuleWiper, + trackerJob: TrackerJobModuleWiper, + user: UserModuleWiper, + validation: ValidationModuleWiper, + visualization: VisualizationModuleWiper ) { val wipers: List 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 264d936e47..a7c6b0f639 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 @@ -47,12 +47,12 @@ import org.hisp.dhis.android.core.maintenance.ForeignKeyViolationTableInfo import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitModuleDownloader import org.hisp.dhis.android.core.program.internal.ProgramIndicatorModuleDownloader import org.hisp.dhis.android.core.program.internal.ProgramModuleDownloader -import org.hisp.dhis.android.core.usecase.UseCaseModuleDownloader import org.hisp.dhis.android.core.settings.internal.GeneralSettingCall import org.hisp.dhis.android.core.settings.internal.SettingModuleDownloader import org.hisp.dhis.android.core.sms.SmsModule 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.usecase.UseCaseModuleDownloader 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 diff --git a/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseShould.kt b/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseShould.kt index 2b5ce2c958..c06bf2ef14 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCaseShould.kt @@ -40,7 +40,8 @@ class InternalStockUseCaseShould { @Test fun map_from_json_string() { - val internalStockUseCase: InternalStockUseCase = objectMapper.readValue(jsonStream, InternalStockUseCase::class.java) + val internalStockUseCase: InternalStockUseCase = objectMapper + .readValue(jsonStream, InternalStockUseCase::class.java) Truth.assertThat(internalStockUseCase).isNotNull() Truth.assertThat(internalStockUseCase.uid()).isEqualTo("IpHINAT79UW") diff --git a/instrumented-test-app/src/main/java/org/hisp/dhis/android/instrumentedTestApp/TestLabActivity.kt b/instrumented-test-app/src/main/java/org/hisp/dhis/android/instrumentedTestApp/TestLabActivity.kt index e75cba67a8..0ea8f6f444 100644 --- a/instrumented-test-app/src/main/java/org/hisp/dhis/android/instrumentedTestApp/TestLabActivity.kt +++ b/instrumented-test-app/src/main/java/org/hisp/dhis/android/instrumentedTestApp/TestLabActivity.kt @@ -30,5 +30,4 @@ package org.hisp.dhis.android.instrumentedTestApp import android.app.Activity -class TestLabActivity : Activity() { -} \ No newline at end of file +class TestLabActivity : Activity() From e27fc625d391dd0d223911d229dde8ea20454149 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 14 Dec 2022 10:46:07 +0100 Subject: [PATCH 009/201] Fix the D2Modulus with a locale, fix tests, remove deleteable interface from InternalStockUseCase --- .../ObjectStoreAbstractIntegrationShould.kt | 3 +++ .../internal/function/D2Modulus.java | 4 +++- .../core/usecase/stock/InternalStockUseCase.java | 14 ++------------ .../core/usecase/stock/StockUseCaseTableInfo.kt | 3 +-- .../usecase/stock/internal/StockUseCaseHandler.kt | 4 ++-- .../usecase/stock/InternalStockUseCaseSamples.kt | 1 - .../core/usecase/stock/StockUseCaseCallShould.kt | 4 ++-- 7 files changed, 13 insertions(+), 20 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt index cfaa708024..96896d68a3 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.data.database +import android.util.Log import com.google.common.truth.Truth.assertThat import java.io.IOException import kotlin.jvm.Throws @@ -92,6 +93,8 @@ abstract class ObjectStoreAbstractIntegrationShould internal con cv1.remove("_id") val cv2 = m2.toContentValues() cv2.remove("_id") + Log.v("fafitolio1", cv1.toString()) + Log.v("fafitolio2", cv2.toString()) assertThat(cv1).isEqualTo(cv2) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.java index d045d1ebc7..d1fc530c35 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.java @@ -34,12 +34,14 @@ import static org.apache.commons.lang3.math.NumberUtils.toDouble; +import java.util.Locale; + public class D2Modulus implements ExpressionItem { @Override public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - return String.format("%.1f", + return String.format(Locale.US, "%.1f", toDouble(visitor.castStringVisit(ctx.expr(0)), 0.0) % toDouble(visitor.castStringVisit(ctx.expr(1)), 0.0)); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCase.java b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCase.java index 77de826402..f40ddc9ada 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCase.java +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/InternalStockUseCase.java @@ -33,25 +33,21 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.gabrielittner.auto.value.cursor.ColumnAdapter; import com.google.auto.value.AutoValue; -import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreBooleanColumnAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreInternalStockUseCaseTransactionListColumnAdapter; import org.hisp.dhis.android.core.common.BaseObject; -import org.hisp.dhis.android.core.common.ObjectWithDeleteInterface; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; import java.util.List; @AutoValue @JsonDeserialize(builder = $$AutoValue_InternalStockUseCase.Builder.class) -public abstract class InternalStockUseCase extends BaseObject - implements ObjectWithUidInterface, ObjectWithDeleteInterface { +public abstract class InternalStockUseCase extends BaseObject implements ObjectWithUidInterface { public static final String TRANSACTIONS = "transactions"; @@ -80,11 +76,6 @@ public abstract class InternalStockUseCase extends BaseObject @JsonProperty() public abstract String stockOnHand(); - @Nullable - @JsonIgnore() - @ColumnAdapter(IgnoreBooleanColumnAdapter.class) - public abstract Boolean deleted(); - @Nullable @JsonProperty() @ColumnAdapter(IgnoreInternalStockUseCaseTransactionListColumnAdapter.class) @@ -103,6 +94,7 @@ public static Builder builder() { @AutoValue.Builder @JsonPOJOBuilder(withPrefix = "") public static abstract class Builder extends BaseObject.Builder { + public abstract Builder id(Long id); @JsonProperty("programUid") public abstract Builder uid(String uid); @@ -117,8 +109,6 @@ public static abstract class Builder extends BaseObject.Builder { public abstract Builder stockOnHand(String stockOnHand); - public abstract Builder deleted(Boolean deleted); - public abstract Builder transactions(List transactions); public abstract InternalStockUseCase build(); diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTableInfo.kt index 3a39949b83..79dcaeebfd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTableInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTableInfo.kt @@ -30,7 +30,6 @@ package org.hisp.dhis.android.core.usecase.stock 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.DeletableDataColumns object StockUseCaseTableInfo { @@ -45,7 +44,7 @@ object StockUseCaseTableInfo { } } - class Columns : DeletableDataColumns() { + class Columns : CoreColumns() { override fun all(): Array { return CollectionsHelper.appendInNewArray( super.all(), diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt index b2ef770fe2..7e1664118f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseHandler.kt @@ -30,7 +30,7 @@ package org.hisp.dhis.android.core.usecase.stock.internal import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction -import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl +import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableWithoutDeleteInterfaceHandlerImpl import org.hisp.dhis.android.core.arch.handlers.internal.LinkHandler import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCase import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction @@ -38,7 +38,7 @@ import org.hisp.dhis.android.core.usecase.stock.InternalStockUseCaseTransaction internal class StockUseCaseHandler @Inject constructor( store: IdentifiableObjectStore, private val transactionLinkHandler: LinkHandler, -) : IdentifiableHandlerImpl(store) { +) : IdentifiableWithoutDeleteInterfaceHandlerImpl(store) { override fun beforeCollectionHandled( oCollection: Collection diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseSamples.kt b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseSamples.kt index c878007f46..eeefd7d590 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseSamples.kt +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/usecase/stock/InternalStockUseCaseSamples.kt @@ -39,7 +39,6 @@ internal object InternalStockUseCaseSamples { .programType("program_type") .itemCode("item_code") .itemDescription("item_description") - .deleted(false) .transactions(listOf(InternalStockUseCaseTransactionSamples.get())) .build() } diff --git a/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCallShould.kt b/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCallShould.kt index 0046f2343d..d9850efd65 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCallShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCallShould.kt @@ -30,7 +30,7 @@ package org.hisp.dhis.android.core.usecase.stock import com.nhaarman.mockitokotlin2.* import io.reactivex.Single import org.hisp.dhis.android.core.arch.api.executors.internal.RxAPICallExecutor -import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl +import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableWithoutDeleteInterfaceHandlerImpl import org.hisp.dhis.android.core.maintenance.D2ErrorSamples import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseCall import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseService @@ -41,7 +41,7 @@ import org.junit.runners.JUnit4 @RunWith(JUnit4::class) class StockUseCaseCallShould { - private val handler: IdentifiableHandlerImpl = mock() + private val handler: IdentifiableWithoutDeleteInterfaceHandlerImpl = mock() private val service: StockUseCaseService = mock() private val stockUseCaseSingle: Single> = mock() private val apiCallExecutor: RxAPICallExecutor = mock() From 41c8aa4b1954272e765371292a1ae876e5f7ff1a Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 14 Dec 2022 11:33:28 +0100 Subject: [PATCH 010/201] Fix WipeDBCallMockIntegrationShould --- .../core/wipe/WipeDBCallMockIntegrationShould.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.kt index de6a024792..8faeb29498 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.kt @@ -34,6 +34,8 @@ import org.hisp.dhis.android.core.data.maps.MapLayerSamples import org.hisp.dhis.android.core.data.sms.SMSOngoingSubmissionSample import org.hisp.dhis.android.core.data.trackedentity.ownership.ProgramTempOwnerSamples import org.hisp.dhis.android.core.data.tracker.importer.internal.TrackerJobObjectSamples +import org.hisp.dhis.android.core.data.usecase.stock.InternalStockUseCaseSamples +import org.hisp.dhis.android.core.data.usecase.stock.InternalStockUseCaseTransactionSamples 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 @@ -51,6 +53,8 @@ import org.hisp.dhis.android.core.sms.data.localdbrepository.internal.SMSConfigS import org.hisp.dhis.android.core.sms.data.localdbrepository.internal.SMSOngoingSubmissionStore import org.hisp.dhis.android.core.trackedentity.ownership.ProgramTempOwnerStore import org.hisp.dhis.android.core.tracker.importer.internal.TrackerJobObjectStore +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseStore +import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseTransactionLinkStore import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyDispatcher import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.Test @@ -127,6 +131,15 @@ class WipeDBCallMockIntegrationShould : BaseMockIntegrationTestEmptyDispatcher() SMSConfigStoreImpl.create(databaseAdapter).insert(KeyValuePairSamples.keyValuePairSample) SMSOngoingSubmissionStore.create(databaseAdapter).insert(SMSOngoingSubmissionSample.get) + StockUseCaseStore.create(databaseAdapter).insert( + InternalStockUseCaseSamples.get() + .toBuilder().uid("lxAQ7Zs9VYR").build() + ) + StockUseCaseTransactionLinkStore.create(databaseAdapter).insert( + InternalStockUseCaseTransactionSamples.get() + .toBuilder().programUid("lxAQ7Zs9VYR").build() + ) + MapLayerStore.create(databaseAdapter).insert(MapLayerSamples.get()) MapLayerImageryProviderStore.create(databaseAdapter).insert(MapLayerImageryProviderSamples.get()) } From 27fdc0d6bfdfac35c8708f4d358461ee8885fde9 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 14 Dec 2022 11:39:48 +0100 Subject: [PATCH 011/201] Remove log --- .../core/data/database/ObjectStoreAbstractIntegrationShould.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt index 96896d68a3..cfaa708024 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/data/database/ObjectStoreAbstractIntegrationShould.kt @@ -27,7 +27,6 @@ */ package org.hisp.dhis.android.core.data.database -import android.util.Log import com.google.common.truth.Truth.assertThat import java.io.IOException import kotlin.jvm.Throws @@ -93,8 +92,6 @@ abstract class ObjectStoreAbstractIntegrationShould internal con cv1.remove("_id") val cv2 = m2.toContentValues() cv2.remove("_id") - Log.v("fafitolio1", cv1.toString()) - Log.v("fafitolio2", cv2.toString()) assertThat(cv1).isEqualTo(cv2) } From a9aace52e93f2adfde6fd4588bad2064fc8b1b07 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 15 Dec 2022 08:51:17 +0100 Subject: [PATCH 012/201] KtlintFormat --- .../android/core/wipe/WipeDBCallMockIntegrationShould.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.kt index 8faeb29498..2dc32f48a1 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/wipe/WipeDBCallMockIntegrationShould.kt @@ -132,12 +132,12 @@ class WipeDBCallMockIntegrationShould : BaseMockIntegrationTestEmptyDispatcher() SMSOngoingSubmissionStore.create(databaseAdapter).insert(SMSOngoingSubmissionSample.get) StockUseCaseStore.create(databaseAdapter).insert( - InternalStockUseCaseSamples.get() - .toBuilder().uid("lxAQ7Zs9VYR").build() + InternalStockUseCaseSamples.get() + .toBuilder().uid("lxAQ7Zs9VYR").build() ) StockUseCaseTransactionLinkStore.create(databaseAdapter).insert( - InternalStockUseCaseTransactionSamples.get() - .toBuilder().programUid("lxAQ7Zs9VYR").build() + InternalStockUseCaseTransactionSamples.get() + .toBuilder().programUid("lxAQ7Zs9VYR").build() ) MapLayerStore.create(databaseAdapter).insert(MapLayerSamples.get()) From 8a00d82887a4ff00ea34393bc639b39ce37adaef Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 15 Dec 2022 10:34:11 +0100 Subject: [PATCH 013/201] Update migrations --- core/src/main/assets/migrations/134.sql | 6 +-- core/src/main/assets/migrations/136.sql | 6 --- .../assets/snapshots/{136.sql => 135.sql} | 0 .../internal/BaseDatabaseOpenHelper.java | 2 +- .../android/core/map/MapModuleDownloader.kt | 46 ------------------- 5 files changed, 4 insertions(+), 56 deletions(-) delete mode 100644 core/src/main/assets/migrations/136.sql rename core/src/main/assets/snapshots/{136.sql => 135.sql} (100%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/map/MapModuleDownloader.kt diff --git a/core/src/main/assets/migrations/134.sql b/core/src/main/assets/migrations/134.sql index e3dd13d55b..0c651fae88 100644 --- a/core/src/main/assets/migrations/134.sql +++ b/core/src/main/assets/migrations/134.sql @@ -1,4 +1,4 @@ -# Add Manage StockThemes (ANDROSDK-1574); +# Add Manage StockUseCases (ANDROSDK-1602); -CREATE TABLE StockTheme (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, itemCode TEXT, itemDescription TEXT, programType TEXT, description TEXT, stockOnHand TEXT, FOREIGN KEY (uid) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE StockThemeTransaction (_id INTEGER PRIMARY KEY AUTOINCREMENT, programUid TEXT NOT NULL, sortOrder INTEGER, transactionType TEXT, distributedTo TEXT, stockDistributed TEXT, stockDiscarded TEXT, stockCorrected TEXT, FOREIGN KEY (programUid) REFERENCES StockTheme (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE StockUseCase (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, itemCode TEXT, itemDescription TEXT, programType TEXT, description TEXT, stockOnHand TEXT, FOREIGN KEY (uid) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE StockUseCaseTransaction (_id INTEGER PRIMARY KEY AUTOINCREMENT, programUid TEXT NOT NULL, sortOrder INTEGER, transactionType TEXT, distributedTo TEXT, stockDistributed TEXT, stockDiscarded TEXT, stockCorrected TEXT, FOREIGN KEY (programUid) REFERENCES StockUseCase (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); diff --git a/core/src/main/assets/migrations/136.sql b/core/src/main/assets/migrations/136.sql deleted file mode 100644 index 7977c66f58..0000000000 --- a/core/src/main/assets/migrations/136.sql +++ /dev/null @@ -1,6 +0,0 @@ -# Rename to StockUseCases (ANDROSDK-1602); - -DROP TABLE IF EXISTS StockThemeTransaction; -DROP TABLE IF EXISTS StockTheme; -CREATE TABLE StockUseCase (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, itemCode TEXT, itemDescription TEXT, programType TEXT, description TEXT, stockOnHand TEXT, FOREIGN KEY (uid) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); -CREATE TABLE StockUseCaseTransaction (_id INTEGER PRIMARY KEY AUTOINCREMENT, programUid TEXT NOT NULL, sortOrder INTEGER, transactionType TEXT, distributedTo TEXT, stockDistributed TEXT, stockDiscarded TEXT, stockCorrected TEXT, FOREIGN KEY (programUid) REFERENCES StockUseCase (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); diff --git a/core/src/main/assets/snapshots/136.sql b/core/src/main/assets/snapshots/135.sql similarity index 100% rename from core/src/main/assets/snapshots/136.sql rename to core/src/main/assets/snapshots/135.sql 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 250756be02..b56d735f31 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 = 136; + static final int VERSION = 135; private final AssetManager assetManager; private final int targetVersion; diff --git a/core/src/main/java/org/hisp/dhis/android/core/map/MapModuleDownloader.kt b/core/src/main/java/org/hisp/dhis/android/core/map/MapModuleDownloader.kt deleted file mode 100644 index 0287ffdecd..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/map/MapModuleDownloader.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.android.core.map - -import dagger.Reusable -import io.reactivex.Completable -import javax.inject.Inject -import org.hisp.dhis.android.core.arch.modules.internal.UntypedModuleDownloader -import org.hisp.dhis.android.core.usecase.stock.internal.StockUseCaseCall - -@Reusable -internal class MapModuleDownloader @Inject constructor( - private val stockUseCaseCall: StockUseCaseCall -) : UntypedModuleDownloader { - - override fun downloadMetadata(): Completable { - return Completable.fromAction { - stockUseCaseCall.getCompletable(false).blockingAwait() - } - } -} From 775622db3fdb9b080b4960f36863593cc93bf4d1 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 15 Dec 2022 20:02:20 +1100 Subject: [PATCH 014/201] [ANDROSDK-1569] Refactor parser package to Kotlin --- build.gradle | 4 +- ...ectionRepositoryMockIntegrationShould.java | 3 - .../indicatorengine/IndicatorEngine.kt | 15 +- .../indicatorengine/IndicatorSQLEngine.kt | 15 +- .../dataitem/DataElementItem.kt | 2 +- .../dataitem/IndicatorDataItem.kt | 16 +- .../dataitem/ProgramAttributeItem.kt | 2 +- .../dataitem/ProgramDataElementItem.kt | 2 +- .../dataitem/ProgramIndicatorItem.kt | 2 +- .../internal/IdentifiableObjectStore.kt | 1 + ...oryOptionOrganisationUnitEntityDIModule.kt | 4 +- ...taSetOrganisationUnitLinkEntityDIModule.kt | 4 +- ...OrganisationUnitGroupLinkEntityDIModule.kt | 4 +- ...ganisationUnitProgramLinkEntityDIModule.kt | 4 +- .../expression/CommonExpressionVisitor.java | 450 ------------------ .../expression/CommonExpressionVisitor.kt | 200 ++++++++ .../CommonExpressionVisitorScope.kt | 123 +++++ .../{CommonParser.java => CommonParser.kt} | 24 +- ...temMethod.java => ExpressionItemMethod.kt} | 10 +- .../internal/expression/ParserUtils.java | 218 --------- .../parser/internal/expression/ParserUtils.kt | 140 ++++++ .../internal/service/ExpressionService.java | 268 ----------- .../internal/service/ExpressionService.kt | 226 +++++++++ .../DimItemDataElementAndOperand.java | 121 ----- .../dataitem/DimItemDataElementAndOperand.kt | 103 ++++ ...imensionalItem.java => DimensionalItem.kt} | 56 +-- .../service/dataitem/DimensionalItemId.java | 74 --- .../service/dataitem/DimensionalItemId.kt | 41 ++ .../service/dataitem/ItemConstant.java | 99 ---- .../internal/service/dataitem/ItemConstant.kt | 79 +++ ...mOrgUnitGroup.java => ItemOrgUnitGroup.kt} | 74 ++- ...lementObject.java => DataElementObject.kt} | 22 +- .../dataobject/DataElementOperandObject.java | 81 ---- .../dataobject/DataElementOperandObject.kt | 56 +++ ...emObject.java => DimensionalItemObject.kt} | 7 +- .../service/utils/ExpressionHelper.kt | 15 +- .../internal/ProgramExpressionItem.kt | 4 +- .../internal/ProgramIndicatorContext.kt | 2 +- .../internal/ProgramIndicatorExecutor.kt | 28 +- .../ProgramIndicatorItemIdsCollector.kt | 18 +- .../internal/ProgramIndicatorSQLContext.kt | 2 +- .../internal/ProgramIndicatorSQLExecutor.kt | 25 +- .../internal/ProgramIndicatorSQLUtils.kt | 10 +- .../internal/dataitem/ProgramItemAttribute.kt | 14 +- .../dataitem/ProgramItemPsEventdate.kt | 4 +- .../dataitem/ProgramItemStageElement.kt | 17 +- .../internal/function/D2AddDays.kt | 2 +- .../internal/function/D2Condition.kt | 2 +- .../internal/function/D2CountIfCondition.kt | 2 +- .../internal/function/D2HasValue.kt | 8 +- .../internal/function/D2RelationshipCount.kt | 2 +- .../internal/function/ProgramCountFunction.kt | 6 +- .../internal/variable/VAnalyticsEndDate.kt | 2 +- .../internal/variable/VAnalyticsStartDate.kt | 2 +- .../internal/variable/VCompletedDate.kt | 4 +- .../internal/variable/VCreationDate.kt | 4 +- .../internal/variable/VCurrentDate.kt | 2 +- .../internal/variable/VDueDate.kt | 2 +- .../internal/variable/VEnrollmentCount.kt | 4 +- .../internal/variable/VEnrollmentDate.kt | 4 +- .../internal/variable/VEnrollmentStatus.kt | 4 +- .../internal/variable/VEventCount.kt | 4 +- .../internal/variable/VEventDate.kt | 2 +- .../internal/variable/VEventStatus.kt | 2 +- .../internal/variable/VIncidentDate.kt | 4 +- .../internal/variable/VOrgUnitCount.kt | 4 +- .../internal/variable/VProgramStageId.kt | 2 +- .../internal/variable/VProgramStageName.kt | 4 +- .../internal/variable/VSyncDate.kt | 4 +- .../internal/variable/VTeiCount.kt | 4 +- .../internal/variable/VValueCount.kt | 6 +- .../internal/variable/VZeroPosValueCount.kt | 6 +- .../AnalyticsSettingEntityDIModule.kt | 4 +- .../DataDimensionItemEntityDIModule.kt | 4 +- ...lizationCategoryDimensionEntityDIModule.kt | 4 +- .../expression/ExpressionServiceShould.java | 409 ---------------- .../expression/ExpressionServiceShould.kt | 405 ++++++++++++++++ .../service/utils/ExpressionHelperShould.kt | 8 +- 78 files changed, 1610 insertions(+), 2005 deletions(-) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitorScope.kt rename core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/{CommonParser.java => CommonParser.kt} (76%) rename core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/{ExpressionItemMethod.java => ExpressionItemMethod.kt} (89%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimItemDataElementAndOperand.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimItemDataElementAndOperand.kt rename core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/{DimensionalItem.java => DimensionalItem.kt} (65%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItemId.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItemId.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemConstant.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemConstant.kt rename core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/{ItemOrgUnitGroup.java => ItemOrgUnitGroup.kt} (50%) rename core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/{DataElementObject.java => DataElementObject.kt} (78%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementOperandObject.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementOperandObject.kt rename core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/{DimensionalItemObject.java => DimensionalItemObject.kt} (95%) delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt diff --git a/build.gradle b/build.gradle index 82e4bb4e3c..d8bddd0e79 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { dependencies { classpath "com.android.tools.build:gradle:7.0.4" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jlleitschuh.gradle:ktlint-gradle:10.1.0" + classpath "org.jlleitschuh.gradle:ktlint-gradle:11.0.0" classpath "org.jacoco:org.jacoco.core:0.8.8" } } @@ -60,7 +60,7 @@ subprojects { project -> apply plugin: "org.jlleitschuh.gradle.ktlint" ktlint { - version = "0.37.2" + version = "0.41.0" android = true outputColorName = "RED" reporters { 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 5499a1070b..be6983ecef 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 @@ -105,9 +105,6 @@ public void filter_d2_error_by_created() { List d2Errors = d2.maintenanceModule().d2Errors() .byCreated().inPeriods(Lists.newArrayList(todayPeriod)).blockingGet(); - for (D2Error error : d2Errors) { - System.out.println("AAAAA " + error.errorDescription()); - } assertThat(d2Errors.size()).isEqualTo(2); } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt index 83df967eb9..0353fb04af 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorEngine.kt @@ -41,6 +41,7 @@ import org.hisp.dhis.android.core.dataelement.DataElement import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitorScope import org.hisp.dhis.android.core.parser.internal.expression.CommonParser import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.parser.internal.service.ExpressionService @@ -116,11 +117,13 @@ internal class IndicatorEngine @Inject constructor( } private fun newVisitor(indicatorContext: IndicatorContext): CommonExpressionVisitor { - return CommonExpressionVisitor.newBuilder() - .withItemMap(IndicatorParserUtils.INDICATOR_EXPRESSION_ITEMS) - .withItemMethod(ParserUtils.ITEM_EVALUATE) - .withConstantMap(constantMap) - .withIndicatorContext(indicatorContext) - .buildForAnalyticsIndicator() + return CommonExpressionVisitor( + CommonExpressionVisitorScope.AnalyticsIndicator( + itemMap = IndicatorParserUtils.INDICATOR_EXPRESSION_ITEMS, + itemMethod = ParserUtils.ITEM_EVALUATE, + constantMap = constantMap, + indicatorContext = indicatorContext + ) + ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt index 1f5ba765fe..3dd4150bfc 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorSQLEngine.kt @@ -42,6 +42,7 @@ import org.hisp.dhis.android.core.dataelement.DataElement import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitorScope import org.hisp.dhis.android.core.parser.internal.expression.CommonParser import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.program.ProgramIndicatorCollectionRepository @@ -123,11 +124,13 @@ internal class IndicatorSQLEngine @Inject constructor( } private fun newVisitor(indicatorContext: IndicatorContext): CommonExpressionVisitor { - return CommonExpressionVisitor.newBuilder() - .withItemMap(IndicatorParserUtils.INDICATOR_EXPRESSION_ITEMS) - .withItemMethod(ParserUtils.ITEM_GET_SQL) - .withConstantMap(constantMap) - .withIndicatorContext(indicatorContext) - .buildForAnalyticsIndicator() + return CommonExpressionVisitor( + CommonExpressionVisitorScope.AnalyticsIndicator( + itemMap = IndicatorParserUtils.INDICATOR_EXPRESSION_ITEMS, + itemMethod = ParserUtils.ITEM_GET_SQL, + constantMap = constantMap, + indicatorContext = indicatorContext + ) + ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/DataElementItem.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/DataElementItem.kt index 0fc89e6ea1..230fe3ee37 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/DataElementItem.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/DataElementItem.kt @@ -50,6 +50,6 @@ internal class DataElementItem : IndicatorDataItem { } override fun getEvaluator(visitor: CommonExpressionVisitor): AnalyticsEvaluator { - return visitor.indicatorContext.dataElementEvaluator + return visitor.indicatorContext!!.dataElementEvaluator } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/IndicatorDataItem.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/IndicatorDataItem.kt index 8ec0fc6a0f..1a973dd9a0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/IndicatorDataItem.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/IndicatorDataItem.kt @@ -47,7 +47,7 @@ internal interface IndicatorDataItem : ExpressionItem { getMetadataEntry(evaluationItem, visitor)?.let { metadataEntry -> getEvaluator(visitor).evaluate( evaluationItem = evaluationItem, - metadata = visitor.indicatorContext.contextMetadata + metadataEntry + metadata = visitor.indicatorContext!!.contextMetadata + metadataEntry ) } } ?: ParserUtils.DOUBLE_VALUE_IF_NULL @@ -58,7 +58,7 @@ internal interface IndicatorDataItem : ExpressionItem { getMetadataEntry(evaluationItem, visitor)?.let { metadataEntry -> getEvaluator(visitor).getSql( evaluationItem = evaluationItem, - metadata = visitor.indicatorContext.contextMetadata + metadataEntry + metadata = visitor.indicatorContext!!.contextMetadata + metadataEntry )?.let { "($it)" } } } @@ -75,7 +75,7 @@ internal interface IndicatorDataItem : ExpressionItem { return getDataItem(ctx, visitor)?.let { dataItem -> AnalyticsServiceEvaluationItem( dimensionItems = listOf(dataItem), - filters = visitor.indicatorContext.evaluationItem.allDimensionItems, + filters = visitor.indicatorContext!!.evaluationItem.allDimensionItems, aggregationType = visitor.indicatorContext.evaluationItem.aggregationType ) } @@ -88,7 +88,7 @@ internal interface IndicatorDataItem : ExpressionItem { ): Pair? { return when (val dataItem = evaluationItem.dimensionItems.first()) { is DimensionItem.DataItem.DataElementOperandItem -> { - val dataElement = visitor.indicatorContext.dataElementStore.selectByUid(dataItem.dataElement) + val dataElement = visitor.indicatorContext!!.dataElementStore.selectByUid(dataItem.dataElement) val coc = visitor.indicatorContext.categoryOptionComboStore.selectByUid(dataItem.categoryOptionCombo) if (dataElement == null || coc == null) { @@ -110,13 +110,13 @@ internal interface IndicatorDataItem : ExpressionItem { } is DimensionItem.DataItem.DataElementItem -> { val dataElement = - visitor.indicatorContext.dataElementStore.selectByUid(dataItem.uid) + visitor.indicatorContext!!.dataElementStore.selectByUid(dataItem.uid) ?: throw AnalyticsException.InvalidDataElement(dataItem.uid) dataItem.uid to MetadataItem.DataElementItem(dataElement) } is DimensionItem.DataItem.ProgramIndicatorItem -> { - val programIndicator = visitor.indicatorContext.programIndicatorRepository + val programIndicator = visitor.indicatorContext!!.programIndicatorRepository .withAnalyticsPeriodBoundaries() .uid(dataItem.uid) .blockingGet() @@ -125,7 +125,7 @@ internal interface IndicatorDataItem : ExpressionItem { dataItem.uid to MetadataItem.ProgramIndicatorItem(programIndicator) } is DimensionItem.DataItem.EventDataItem.DataElement -> { - val program = visitor.indicatorContext.programStore.selectByUid(dataItem.program) + val program = visitor.indicatorContext!!.programStore.selectByUid(dataItem.program) ?: throw AnalyticsException.InvalidProgram(dataItem.program) val dataElement = visitor.indicatorContext.dataElementStore.selectByUid(dataItem.dataElement) @@ -135,7 +135,7 @@ internal interface IndicatorDataItem : ExpressionItem { metadataItem.id to metadataItem } is DimensionItem.DataItem.EventDataItem.Attribute -> { - val program = visitor.indicatorContext.programStore.selectByUid(dataItem.program) + val program = visitor.indicatorContext!!.programStore.selectByUid(dataItem.program) ?: throw AnalyticsException.InvalidProgram(dataItem.program) val attribute = visitor.indicatorContext.trackedEntityAttributeStore.selectByUid(dataItem.attribute) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramAttributeItem.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramAttributeItem.kt index ed6f92ed1e..74ff748536 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramAttributeItem.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramAttributeItem.kt @@ -47,6 +47,6 @@ internal class ProgramAttributeItem : IndicatorDataItem { } override fun getEvaluator(visitor: CommonExpressionVisitor): AnalyticsEvaluator { - return visitor.indicatorContext.eventDataItemEvaluator + return visitor.indicatorContext!!.eventDataItemEvaluator } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramDataElementItem.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramDataElementItem.kt index 9863077216..ec4f95d47c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramDataElementItem.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramDataElementItem.kt @@ -47,6 +47,6 @@ internal class ProgramDataElementItem : IndicatorDataItem { } override fun getEvaluator(visitor: CommonExpressionVisitor): AnalyticsEvaluator { - return visitor.indicatorContext.eventDataItemEvaluator + return visitor.indicatorContext!!.eventDataItemEvaluator } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramIndicatorItem.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramIndicatorItem.kt index 4a58b7868f..f262729b82 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramIndicatorItem.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/ProgramIndicatorItem.kt @@ -45,6 +45,6 @@ internal class ProgramIndicatorItem : IndicatorDataItem { } override fun getEvaluator(visitor: CommonExpressionVisitor): AnalyticsEvaluator { - return visitor.indicatorContext.programIndicatorEvaluator + return visitor.indicatorContext!!.programIndicatorEvaluator } } 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 d3f62d0cdf..723ca951db 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 @@ -29,6 +29,7 @@ package org.hisp.dhis.android.core.arch.db.stores.internal import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction import org.hisp.dhis.android.core.common.ObjectWithUidInterface + internal interface IdentifiableObjectStore : ObjectStore { @Throws(RuntimeException::class) fun delete(uid: String) diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionOrganisationUnitEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionOrganisationUnitEntityDIModule.kt index 4affc085f5..9b584c5efd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionOrganisationUnitEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionOrganisationUnitEntityDIModule.kt @@ -49,6 +49,6 @@ internal class CategoryOptionOrganisationUnitEntityDIModule { @Reusable fun handler(store: LinkStore): LinkHandler { - return LinkHandlerImpl(store) - } + return LinkHandlerImpl(store) + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetOrganisationUnitLinkEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetOrganisationUnitLinkEntityDIModule.kt index 4398a3251b..8a7aa796a3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetOrganisationUnitLinkEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/internal/DataSetOrganisationUnitLinkEntityDIModule.kt @@ -50,6 +50,6 @@ internal class DataSetOrganisationUnitLinkEntityDIModule { @Reusable fun handler(store: LinkStore): LinkHandler { - return LinkHandlerImpl(store) - } + return LinkHandlerImpl(store) + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitOrganisationUnitGroupLinkEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitOrganisationUnitGroupLinkEntityDIModule.kt index b9e3c6bc7e..301854947d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitOrganisationUnitGroupLinkEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitOrganisationUnitGroupLinkEntityDIModule.kt @@ -51,6 +51,6 @@ internal class OrganisationUnitOrganisationUnitGroupLinkEntityDIModule { @Reusable fun handler(store: LinkStore): LinkHandler { - return LinkHandlerImpl(store) - } + return LinkHandlerImpl(store) + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitProgramLinkEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitProgramLinkEntityDIModule.kt index 7b6984e579..0ff28ef6c5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitProgramLinkEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/internal/OrganisationUnitProgramLinkEntityDIModule.kt @@ -50,6 +50,6 @@ internal class OrganisationUnitProgramLinkEntityDIModule { @Reusable fun handler(store: LinkStore): LinkHandler { - return LinkHandlerImpl(store) - } + return LinkHandlerImpl(store) + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.java deleted file mode 100644 index 7e565296a9..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.java +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.parser.internal.expression; - -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL; - -import com.google.common.base.Joiner; - -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ParseTree; -import org.apache.commons.lang3.Validate; -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.IndicatorContext; -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; -import org.hisp.dhis.android.core.category.CategoryOptionCombo; -import org.hisp.dhis.android.core.constant.Constant; -import org.hisp.dhis.android.core.dataelement.DataElement; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup; -import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemId; -import org.hisp.dhis.android.core.program.ProgramStage; -import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorContext; -import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorExecutor; -import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLContext; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute; -import org.hisp.dhis.antlr.AntlrExpressionVisitor; -import org.hisp.dhis.antlr.ParserExceptionWithoutContext; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -@SuppressWarnings({"PMD.TooManyFields", "PMD.ExcessivePublicCount"}) -public class CommonExpressionVisitor - extends AntlrExpressionVisitor { - - private IdentifiableObjectStore dataElementStore; - - private IdentifiableObjectStore attributeStore; - - private IdentifiableObjectStore categoryOptionComboStore; - - private IdentifiableObjectStore organisationUnitGroupStore; - - private IdentifiableObjectStore programStageStore; - - /** - * Map of ExprItem instances to call for each expression item - */ - private Map itemMap; - - /** - * Method to call within the ExprItem instance - */ - private ExpressionItemMethod itemMethod; - - /** - * Used to collect the string replacements to build a description. - */ - private final Map itemDescriptions = new HashMap<>(); - - /** - * Constants to use in evaluating an expression. - */ - private Map constantMap = new HashMap<>(); - - /** - * Used to collect the dimensional item ids in the expression. - */ - private Set itemIds = new HashSet<>(); - - /** - * Organisation unit group counts to use in evaluating an expression. - */ - Map orgUnitCountMap = new HashMap<>(); - - /** - * Count of days in period to use in evaluating an expression. - */ - private Double days; - - /** - * Values to use for variables in evaluating an org.hisp.dhis.rules.parser.expression. - */ - private Map itemValueMap = new HashMap<>(); - - /** - * Expression state - */ - private final ExpressionState state = new ExpressionState(); - - /** - * Default value for data type double. - */ - public static final double DEFAULT_DOUBLE_VALUE = 1d; - - // Program indicators - - private ProgramIndicatorContext programIndicatorContext; - - private ProgramIndicatorExecutor programIndicatorExecutor; - - private ProgramIndicatorSQLContext programIndicatorSQLContext; - - // Analytic indicator - - private IndicatorContext indicatorContext; - - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - protected CommonExpressionVisitor() { - // This constructor is intentionally empty. - } - - /** - * Creates a new Builder for CommonExpressionVisitor. - * - * @return a Builder for CommonExpressionVisitor. - */ - public static Builder newBuilder() { - return new CommonExpressionVisitor.Builder(); - } - - // ------------------------------------------------------------------------- - // Visitor methods - // ------------------------------------------------------------------------- - - @Override - public Object visitExpr(ExprContext ctx) { - if (ctx.it != null) { - ExpressionItem item = itemMap.get(ctx.it.getType()); - - if (item == null) { - throw new ParserExceptionWithoutContext( - "Item " + ctx.it.getText() + " not supported for this type of expression"); - } - - return itemMethod.apply(item, ctx, this); - } - - if (ctx.expr().size() > 0) { - // If there's an expr, visit the expr - return visit(ctx.expr(0)); - } - - return visit(ctx.getChild(0)); // All others: visit first child. - } - - // ------------------------------------------------------------------------- - // Logic for expression items - // ------------------------------------------------------------------------- - - /** - * Visits a context while allowing null values (not replacing them - * with 0 or ''), even if we would otherwise be replacing them. - * - * @param ctx any context - * @return the value while allowing nulls - */ - public Object visitAllowingNulls(ParserRuleContext ctx) { - boolean savedReplaceNulls = state.getReplaceNulls(); - - state.setReplaceNulls(false); - - Object result = visit(ctx); - - state.setReplaceNulls(savedReplaceNulls); - - return result; - } - - /** - * Handles nulls and missing values. - *

- * If we should replace nulls with the default value, then do so, and - * remember how many items found, and how many of them had values, for - * subsequent MissingValueStrategy analysis. - *

- * If we should not replace nulls with the default value, then don't, - * as this is likely for some function that is testing for nulls, and - * a missing value should not count towards the MissingValueStrategy. - * - * @param value the (possibly null) value - * @return the value we should return. - */ - public Object handleNulls(Object value) { - if (state.getReplaceNulls()) { - state.setItemsFound(state.getItemsFound() + 1); - - if (value == null) { - return DOUBLE_VALUE_IF_NULL; - } else { - state.setItemValuesFound(state.getItemValuesFound() + 1); - if (ParserUtils.isZeroOrPositive(value.toString())) { - state.setItemZeroPosValuesFound(state.getItemZeroPosValuesFound() + 1); - } - } - } - - return value; - } - - /** - * Regenerates an expression by visiting all the children of the - * expression node (including any terminal nodes). - * - * @param ctx the expression context - * @return the regenerated expression (as a String) - */ - public Object regenerateAllChildren(ExprContext ctx) { - List result = new ArrayList<>(); - for (ParseTree child : ctx.children) { - result.add(castStringVisit(child)); - } - return Joiner.on(' ').join(result); - } - - // ------------------------------------------------------------------------- - // Getters and setters - // ------------------------------------------------------------------------- - - public IdentifiableObjectStore getDataElementStore() { - return dataElementStore; - } - - public IdentifiableObjectStore getTrackedEntityAttributeStore() { - return attributeStore; - } - - public IdentifiableObjectStore getCategoryOptionComboStore() { - return categoryOptionComboStore; - } - - public IdentifiableObjectStore getOrganisationUnitGroupStore() { - return organisationUnitGroupStore; - } - - public IdentifiableObjectStore getProgramStageStore() { - return programStageStore; - } - - public Map getItemDescriptions() { - return itemDescriptions; - } - - public Map getConstantMap() { - return constantMap; - } - - public Set getItemIds() { - return itemIds; - } - - public void setItemIds(Set itemIds) { - this.itemIds = itemIds; - } - - public Map getOrgUnitCountMap() { - return orgUnitCountMap; - } - - public void setOrgUnitCountMap(Map orgUnitCountMap) { - this.orgUnitCountMap = orgUnitCountMap; - } - - public Map getItemValueMap() { - return itemValueMap; - } - - public void setItemValueMap(Map itemValueMap) { - this.itemValueMap = itemValueMap; - } - - public Double getDays() { - return this.days; - } - - public void setDays(Double days) { - this.days = days; - } - - public ExpressionState getState() { - return state; - } - - public ProgramIndicatorContext getProgramIndicatorContext() { - return programIndicatorContext; - } - - public ProgramIndicatorExecutor getProgramIndicatorExecutor() { - return programIndicatorExecutor; - } - - public ProgramIndicatorSQLContext getProgramIndicatorSQLContext() { - return programIndicatorSQLContext; - } - - public IndicatorContext getIndicatorContext() { - return indicatorContext; - } - - // ------------------------------------------------------------------------- - // Builder - // ------------------------------------------------------------------------- - - /** - * Builder for {@link CommonExpressionVisitor} instances. - */ - public static class Builder { - private final CommonExpressionVisitor visitor; - - protected Builder() { - this.visitor = new CommonExpressionVisitor(); - } - - public Builder withItemMap(Map itemMap) { - this.visitor.itemMap = itemMap; - return this; - } - - public Builder withItemMethod(ExpressionItemMethod itemMethod) { - this.visitor.itemMethod = itemMethod; - return this; - } - - public Builder withConstantMap(Map constantMap) { - this.visitor.constantMap = constantMap; - return this; - } - - public Builder withDataElementStore(IdentifiableObjectStore store) { - this.visitor.dataElementStore = store; - return this; - } - - public Builder withTrackedEntityAttributeStore(IdentifiableObjectStore store) { - this.visitor.attributeStore = store; - return this; - } - - public Builder withCategoryOptionComboStore(IdentifiableObjectStore store) { - this.visitor.categoryOptionComboStore = store; - return this; - } - - public Builder withOrganisationUnitGroupStore(IdentifiableObjectStore store) { - this.visitor.organisationUnitGroupStore = store; - return this; - } - - public Builder withProgramStageStore(IdentifiableObjectStore store) { - this.visitor.programStageStore = store; - return this; - } - - public Builder withProgramIndicatorContext(ProgramIndicatorContext programIndicatorContext) { - this.visitor.programIndicatorContext = programIndicatorContext; - return this; - } - - public Builder withProgramIndicatorExecutor(ProgramIndicatorExecutor programIndicatorExecutor) { - this.visitor.programIndicatorExecutor = programIndicatorExecutor; - return this; - } - - public Builder withProgramIndicatorSQLContext(ProgramIndicatorSQLContext programIndicatorSQLContext) { - this.visitor.programIndicatorSQLContext = programIndicatorSQLContext; - return this; - } - - public Builder withIndicatorContext(IndicatorContext indicatorContext) { - this.visitor.indicatorContext = indicatorContext; - return this; - } - - private CommonExpressionVisitor validateCommonProperties() { - Validate.notNull(this.visitor.constantMap, missingProperty("constantMap")); - Validate.notNull(this.visitor.itemMap, missingProperty("itemMap")); - Validate.notNull(this.visitor.itemMethod, missingProperty("itemMethod")); - return visitor; - } - - public CommonExpressionVisitor buildForExpressions() { - Validate.notNull(this.visitor.dataElementStore, missingProperty("dataElementStore")); - Validate.notNull(this.visitor.categoryOptionComboStore, missingProperty("categoryOptionComboStore")); - Validate.notNull(this.visitor.organisationUnitGroupStore, missingProperty("organisationUnitGroupStore")); - Validate.notNull(this.visitor.programStageStore, missingProperty("programStageStore")); - - return validateCommonProperties(); - } - - public CommonExpressionVisitor buildForProgramIndicator() { - Validate.notNull(this.visitor.programIndicatorContext, missingProperty("programIndicatorContext")); - Validate.notNull(this.visitor.programIndicatorExecutor, missingProperty("programIndicatorExecutor")); - Validate.notNull(this.visitor.attributeStore, missingProperty("trackedEntityAttributeStore")); - Validate.notNull(this.visitor.programStageStore, missingProperty("programStageStore")); - - return validateCommonProperties(); - } - - public CommonExpressionVisitor buildForProgramSQLIndicator() { - Validate.notNull(this.visitor.programIndicatorSQLContext, missingProperty("programIndicatorSQLContext")); - Validate.notNull(this.visitor.dataElementStore, missingProperty("dataElementStore")); - Validate.notNull(this.visitor.attributeStore, missingProperty("trackedEntityAttributeStore")); - - return validateCommonProperties(); - } - - public CommonExpressionVisitor buildForAnalyticsIndicator() { - Validate.notNull(this.visitor.indicatorContext, missingProperty("indicatorContext")); - - return validateCommonProperties(); - } - } - - private static String missingProperty(String property) { - return "Missing required property '" + property + "'."; - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.kt new file mode 100644 index 0000000000..a1bac0c7a2 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.kt @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression + +import org.antlr.v4.runtime.ParserRuleContext +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.IndicatorContext +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.category.CategoryOptionCombo +import org.hisp.dhis.android.core.constant.Constant +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.isZeroOrPositive +import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemId +import org.hisp.dhis.android.core.program.ProgramStage +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorContext +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorExecutor +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLContext +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute +import org.hisp.dhis.antlr.AntlrExpressionVisitor +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +internal class CommonExpressionVisitor constructor( + val scope: CommonExpressionVisitorScope +) : AntlrExpressionVisitor() { + + /** + * Map of ExprItem instances to call for each expression item + */ + private val itemMap: Map = scope.itemMap + + /** + * Method to call within the ExprItem instance + */ + private val itemMethod: ExpressionItemMethod = scope.itemMethod + + /** + * Constants to use in evaluating an expression. + */ + val constantMap: Map = scope.constantMap + + /** + * Stores + */ + val dataElementStore: IdentifiableObjectStore? = scope.dataElementStore + val trackedEntityAttributeStore: IdentifiableObjectStore? = scope.trackedAttributeStore + val categoryOptionComboStore: IdentifiableObjectStore? = scope.categoryOptionComboStore + val organisationUnitGroupStore: IdentifiableObjectStore? = scope.organisationUnitGroupStore + val programStageStore: IdentifiableObjectStore? = scope.programStageStore + + /** + * Context + */ + val programIndicatorContext: ProgramIndicatorContext? = scope.programIndicatorContext + val programIndicatorExecutor: ProgramIndicatorExecutor? = scope.programIndicatorExecutor + val programIndicatorSQLContext: ProgramIndicatorSQLContext? = scope.programIndicatorSQLContext + val indicatorContext: IndicatorContext? = scope.indicatorContext + + /** + * Used to collect the string replacements to build a description. + */ + val itemDescriptions: MutableMap = HashMap() + + /** + * Used to collect the dimensional item ids in the expression. + */ + var itemIds: MutableSet = HashSet() + + /** + * Organisation unit group counts to use in evaluating an expression. + */ + var orgUnitCountMap: Map = HashMap() + + /** + * Count of days in period to use in evaluating an expression. + */ + var days: Double? = null + + /** + * Values to use for variables in evaluating an org.hisp.dhis.rules.parser.expression. + */ + var itemValueMap: Map = HashMap() + + /** + * Expression state + */ + val state = ExpressionState() + + // ------------------------------------------------------------------------- + // Visitor methods + // ------------------------------------------------------------------------- + override fun visitExpr(ctx: ExprContext): Any? { + if (ctx.it != null) { + val item = itemMap[ctx.it.type] + ?: throw ParserExceptionWithoutContext( + "Item " + ctx.it.text + " not supported for this type of expression" + ) + return itemMethod.apply(item, ctx, this) + } + return if (ctx.expr().size > 0) { + // If there's an expr, visit the expr + visit(ctx.expr(0)) + } else { + // All others: visit first child. + visit(ctx.getChild(0)) + } + } + + // ------------------------------------------------------------------------- + // Logic for expression items + // ------------------------------------------------------------------------- + /** + * Visits a context while allowing null values (not replacing them + * with 0 or ''), even if we would otherwise be replacing them. + * + * @param ctx any context + * @return the value while allowing nulls + */ + fun visitAllowingNulls(ctx: ParserRuleContext?): Any? { + val savedReplaceNulls = state.replaceNulls + state.replaceNulls = false + val result = visit(ctx) + state.replaceNulls = savedReplaceNulls + return result + } + + /** + * Handles nulls and missing values. + * + * + * If we should replace nulls with the default value, then do so, and + * remember how many items found, and how many of them had values, for + * subsequent MissingValueStrategy analysis. + * + * + * If we should not replace nulls with the default value, then don't, + * as this is likely for some function that is testing for nulls, and + * a missing value should not count towards the MissingValueStrategy. + * + * @param value the (possibly null) value + * @return the value we should return. + */ + fun handleNulls(value: Any?): Any? { + if (state.replaceNulls) { + state.itemsFound = state.itemsFound + 1 + if (value == null) { + return ParserUtils.DOUBLE_VALUE_IF_NULL + } else { + state.itemValuesFound = state.itemValuesFound + 1 + if (isZeroOrPositive(value.toString())) { + state.itemZeroPosValuesFound = state.itemZeroPosValuesFound + 1 + } + } + } + return value + } + + /** + * Regenerates an expression by visiting all the children of the + * expression node (including any terminal nodes). + * + * @param ctx the expression context + * @return the regenerated expression (as a String) + */ + fun regenerateAllChildren(ctx: ExprContext): Any { + return ctx.children.joinToString(separator = " ") { castStringVisit(it) } + } + + companion object { + /** + * Default value for data type double. + */ + const val DEFAULT_DOUBLE_VALUE = 1.0 + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitorScope.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitorScope.kt new file mode 100644 index 0000000000..b5fc69d5a4 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitorScope.kt @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.hisp.dhis.android.core.parser.internal.expression + +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.IndicatorContext +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.category.CategoryOptionCombo +import org.hisp.dhis.android.core.constant.Constant +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup +import org.hisp.dhis.android.core.program.ProgramStage +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorContext +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorExecutor +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLContext +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute + +@Suppress("LongParameterList") +internal sealed class CommonExpressionVisitorScope( + val constantMap: Map, + val itemMap: Map, + val itemMethod: ExpressionItemMethod, + val dataElementStore: IdentifiableObjectStore? = null, + val categoryOptionComboStore: IdentifiableObjectStore? = null, + val organisationUnitGroupStore: IdentifiableObjectStore? = null, + val programStageStore: IdentifiableObjectStore? = null, + val trackedAttributeStore: IdentifiableObjectStore? = null, + val indicatorContext: IndicatorContext? = null, + val programIndicatorContext: ProgramIndicatorContext? = null, + val programIndicatorSQLContext: ProgramIndicatorSQLContext? = null, + val programIndicatorExecutor: ProgramIndicatorExecutor? = null +) { + class Expression( + constantMap: Map, + itemMap: Map, + itemMethod: ExpressionItemMethod, + dataElementStore: IdentifiableObjectStore, + categoryOptionComboStore: IdentifiableObjectStore, + organisationUnitGroupStore: IdentifiableObjectStore, + programStageStore: IdentifiableObjectStore, + ) : CommonExpressionVisitorScope( + constantMap, + itemMap, + itemMethod, + dataElementStore = dataElementStore, + categoryOptionComboStore = categoryOptionComboStore, + organisationUnitGroupStore = organisationUnitGroupStore, + programStageStore = programStageStore + ) + + class ProgramIndicator( + constantMap: Map, + itemMap: Map, + itemMethod: ExpressionItemMethod, + programIndicatorContext: ProgramIndicatorContext, + programIndicatorExecutor: ProgramIndicatorExecutor, + dataElementStore: IdentifiableObjectStore, + trackedEntityAttributeStore: IdentifiableObjectStore, + programStageStore: IdentifiableObjectStore + ) : CommonExpressionVisitorScope( + constantMap, + itemMap, + itemMethod, + programIndicatorContext = programIndicatorContext, + programIndicatorExecutor = programIndicatorExecutor, + dataElementStore = dataElementStore, + trackedAttributeStore = trackedEntityAttributeStore, + programStageStore = programStageStore + ) + + class ProgramSQLIndicator( + constantMap: Map, + itemMap: Map, + itemMethod: ExpressionItemMethod, + programIndicatorSQLContext: ProgramIndicatorSQLContext, + dataElementStore: IdentifiableObjectStore, + trackedEntityAttributeStore: IdentifiableObjectStore + ) : CommonExpressionVisitorScope( + constantMap, + itemMap, + itemMethod, + programIndicatorSQLContext = programIndicatorSQLContext, + dataElementStore = dataElementStore, + trackedAttributeStore = trackedEntityAttributeStore + ) + + class AnalyticsIndicator( + constantMap: Map, + itemMap: Map, + itemMethod: ExpressionItemMethod, + indicatorContext: IndicatorContext + ) : CommonExpressionVisitorScope( + constantMap, + itemMap, + itemMethod, + indicatorContext = indicatorContext + ) +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonParser.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonParser.kt similarity index 76% rename from core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonParser.java rename to core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonParser.kt index 52961d67ec..2cf827df90 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonParser.java +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonParser.kt @@ -25,26 +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.parser.internal.expression -package org.hisp.dhis.android.core.parser.internal.expression; +import android.os.Build +import org.hisp.dhis.antlr.Parser -import android.os.Build; +internal object CommonParser { -import org.hisp.dhis.antlr.Parser; - -public final class CommonParser { - - private CommonParser() { - } - - public static Object visit(String expression, CommonExpressionVisitor visitor) { - int sdkVersion = Build.VERSION.SDK_INT; + @JvmStatic + fun visit(expression: String?, visitor: CommonExpressionVisitor): Any? { + val sdkVersion = Build.VERSION.SDK_INT // In unit test, sdk value is 0. Ignore it and use cache by default. - if (sdkVersion > 0 && sdkVersion < Build.VERSION_CODES.LOLLIPOP) { - return Parser.visit(expression, visitor, false); + return if (sdkVersion > 0 && sdkVersion < Build.VERSION_CODES.LOLLIPOP) { + Parser.visit(expression, visitor, false) } else { - return Parser.visit(expression, visitor); + Parser.visit(expression, visitor) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionItemMethod.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionItemMethod.kt similarity index 89% rename from core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionItemMethod.java rename to core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionItemMethod.kt index 3584ac2670..05af11e46c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionItemMethod.java +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionItemMethod.kt @@ -25,18 +25,16 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.parser.internal.expression -package org.hisp.dhis.android.core.parser.internal.expression; - -import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext; +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext /** * Applies a method in an ExprFunction. * * @author Jim Grace */ -@FunctionalInterface -public interface ExpressionItemMethod { +internal fun interface ExpressionItemMethod { /** * Invokes a method in an expression function * @@ -45,5 +43,5 @@ public interface ExpressionItemMethod { * @param visitor the visitor class for supporting methods * @return the method result from the expression item class */ - Object apply(ExpressionItem item, ExprContext ctx, CommonExpressionVisitor visitor); + fun apply(item: ExpressionItem, ctx: ExprContext, visitor: CommonExpressionVisitor): Any } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.java deleted file mode 100644 index a42b004528..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.parser.internal.expression; - -import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionFirstNonNull; -import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionGreatest; -import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionIf; -import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionIsNotNull; -import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionIsNull; -import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionLeast; -import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionLog; -import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionLog10; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorCompareEqual; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorCompareGreaterThan; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorCompareGreaterThanOrEqual; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorCompareLessThan; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorCompareLessThanOrEqual; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorCompareNotEqual; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorGroupingParentheses; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorLogicalAnd; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorLogicalNot; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorLogicalOr; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorMathDivide; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorMathMinus; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorMathModulus; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorMathMultiply; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorMathPlus; -import org.hisp.dhis.android.core.parser.internal.expression.operator.OperatorMathPower; -import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemConstant; -import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemDays; -import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemOrgUnitGroup; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Pattern; - -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.AMPERSAND_2; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.AND; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.C_BRACE; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.DAYS; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.DIV; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.EQ; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.EXCLAMATION_POINT; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.FIRST_NON_NULL; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.GEQ; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.GREATEST; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.GT; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.IF; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.IS_NOT_NULL; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.IS_NULL; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.LEAST; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.LEQ; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.LOG; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.LOG10; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.LT; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.MINUS; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.MOD; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.MUL; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.NE; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.NOT; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.OR; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.OUG_BRACE; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.PAREN; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.PLUS; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.POWER; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.VERTICAL_BAR_2; - -@SuppressWarnings({"PMD.ExcessiveImports", "PMD.TooManyStaticImports"}) -public final class ParserUtils { - - public final static double DOUBLE_VALUE_IF_NULL = 0.0; - - private static final String NUMERIC_REGEXP = "^(-?0|-?[1-9]\\d*)(\\.\\d+)?(E(-)?\\d+)?$"; - - private static final Pattern NUMERIC_PATTERN = Pattern.compile(NUMERIC_REGEXP); - - private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; - - public final static ExpressionItemMethod ITEM_GET_DESCRIPTIONS = ExpressionItem::getDescription; - - public final static ExpressionItemMethod ITEM_GET_IDS = ExpressionItem::getItemId; - - public final static ExpressionItemMethod ITEM_GET_ORG_UNIT_GROUPS = ExpressionItem::getOrgUnitGroup; - - public final static ExpressionItemMethod ITEM_EVALUATE = ExpressionItem::evaluate; - - public final static ExpressionItemMethod ITEM_GET_SQL = ExpressionItem::getSql; - - public final static ExpressionItemMethod ITEM_REGENERATE = ExpressionItem::regenerate; - - public final static ExpressionItemMethod ITEM_VALUE_COUNT = ExpressionItem::count; - - public final static Map COMMON_EXPRESSION_ITEMS; - - static { - Map m = new HashMap<>(); - - m.put(PAREN, new OperatorGroupingParentheses()); - m.put(PLUS, new OperatorMathPlus()); - m.put(MINUS, new OperatorMathMinus()); - m.put(POWER, new OperatorMathPower()); - m.put(MUL, new OperatorMathMultiply()); - m.put(DIV, new OperatorMathDivide()); - m.put(MOD, new OperatorMathModulus()); - m.put(NOT, new OperatorLogicalNot()); - m.put(EXCLAMATION_POINT, new OperatorLogicalNot()); - m.put(AND, new OperatorLogicalAnd()); - m.put(AMPERSAND_2, new OperatorLogicalAnd()); - m.put(OR, new OperatorLogicalOr()); - m.put(VERTICAL_BAR_2, new OperatorLogicalOr()); - - // Comparison operators - - m.put(EQ, new OperatorCompareEqual()); - m.put(NE, new OperatorCompareNotEqual()); - m.put(GT, new OperatorCompareGreaterThan()); - m.put(LT, new OperatorCompareLessThan()); - m.put(GEQ, new OperatorCompareGreaterThanOrEqual()); - m.put(LEQ, new OperatorCompareLessThanOrEqual()); - - // Functions - - m.put(FIRST_NON_NULL, new FunctionFirstNonNull()); - m.put(GREATEST, new FunctionGreatest()); - m.put(IF, new FunctionIf()); - m.put(IS_NOT_NULL, new FunctionIsNotNull()); - m.put(IS_NULL, new FunctionIsNull()); - m.put(LEAST, new FunctionLeast()); - - m.put(LOG, new FunctionLog()); - m.put(LOG10, new FunctionLog10()); - - // Common variables - - m.put(OUG_BRACE, new ItemOrgUnitGroup()); - m.put(DAYS, new ItemDays()); - m.put(C_BRACE, new ItemConstant()); - - COMMON_EXPRESSION_ITEMS = m; - } - - private ParserUtils() { - } - - static boolean isZeroOrPositive(String value) { - return isNumeric(value) && Double.parseDouble(value) >= 0d; - } - - public static boolean isNumeric(String value) { - return value != null && isDouble(value) && NUMERIC_PATTERN.matcher(value).matches(); - } - - private static boolean isDouble(String value) { - try { - Double.valueOf(value); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - public static String fromDouble(Double value) { - if (value != null && !Double.isNaN(value)) { - Double rounded = getRounded(value, 2); - return String.valueOf(rounded).replaceAll("\\.0+$", ""); - } - - return ""; - } - - public static double getRounded(double value, int decimals) { - final double factor = Math.pow(10, decimals); - - return Math.round(value * factor) / factor; - } - - /** - * Formats a Date to the format YYYY-MM-DD. - * - * @param date the Date to parse. - * @return A formatted date string. Null if argument is null. - */ - public static String getMediumDateString(Date date) { - final SimpleDateFormat format = new SimpleDateFormat(); - - format.applyPattern(DEFAULT_DATE_FORMAT); - - return date == null ? null : format.format(date); - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt new file mode 100644 index 0000000000..5e0958ef79 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression + +import java.text.SimpleDateFormat +import java.util.* +import java.util.regex.Pattern +import kotlin.math.pow +import kotlin.math.roundToInt +import org.hisp.dhis.android.core.parser.internal.expression.function.* +import org.hisp.dhis.android.core.parser.internal.expression.operator.* +import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemConstant +import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemDays +import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemOrgUnitGroup +import org.hisp.dhis.parser.expression.antlr.ExpressionParser +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +internal object ParserUtils { + const val DOUBLE_VALUE_IF_NULL = 0.0 + private const val NUMERIC_REGEXP = "^(-?0|-?[1-9]\\d*)(\\.\\d+)?(E(-)?\\d+)?$" + private val NUMERIC_PATTERN = Pattern.compile(NUMERIC_REGEXP) + private const val DEFAULT_DATE_FORMAT = "yyyy-MM-dd" + + val ITEM_GET_DESCRIPTIONS = ExpressionItem::getDescription + val ITEM_GET_IDS = ExpressionItem::getItemId + val ITEM_GET_ORG_UNIT_GROUPS = ExpressionItem::getOrgUnitGroup + val ITEM_EVALUATE = ExpressionItem::evaluate + val ITEM_GET_SQL = ExpressionItem::getSql + val ITEM_REGENERATE = ExpressionItem::regenerate + val ITEM_VALUE_COUNT = ExpressionItem::count + + val COMMON_EXPRESSION_ITEMS = hashMapOf( + ExpressionParser.PAREN to OperatorGroupingParentheses(), + ExpressionParser.PLUS to OperatorMathPlus(), + ExpressionParser.MINUS to OperatorMathMinus(), + ExpressionParser.POWER to OperatorMathPower(), + ExpressionParser.MUL to OperatorMathMultiply(), + ExpressionParser.DIV to OperatorMathDivide(), + ExpressionParser.DIV to OperatorMathDivide(), + ExpressionParser.MOD to OperatorMathModulus(), + ExpressionParser.NOT to OperatorLogicalNot(), + ExpressionParser.EXCLAMATION_POINT to OperatorLogicalNot(), + ExpressionParser.AND to OperatorLogicalAnd(), + ExpressionParser.AMPERSAND_2 to OperatorLogicalAnd(), + ExpressionParser.OR to OperatorLogicalOr(), + ExpressionParser.VERTICAL_BAR_2 to OperatorLogicalOr(), + ExpressionParser.EQ to OperatorCompareEqual(), + ExpressionParser.NE to OperatorCompareNotEqual(), + ExpressionParser.GT to OperatorCompareGreaterThan(), + ExpressionParser.LT to OperatorCompareLessThan(), + ExpressionParser.GEQ to OperatorCompareGreaterThanOrEqual(), + ExpressionParser.LEQ to OperatorCompareLessThanOrEqual(), + + // Functions + ExpressionParser.FIRST_NON_NULL to FunctionFirstNonNull(), + ExpressionParser.GREATEST to FunctionGreatest(), + ExpressionParser.IF to FunctionIf(), + ExpressionParser.IS_NOT_NULL to FunctionIsNotNull(), + ExpressionParser.IS_NULL to FunctionIsNull(), + ExpressionParser.LEAST to FunctionLeast(), + ExpressionParser.LOG to FunctionLog(), + ExpressionParser.LOG10 to FunctionLog10(), + + // Common variables + ExpressionParser.OUG_BRACE to ItemOrgUnitGroup(), + ExpressionParser.DAYS to ItemDays(), + ExpressionParser.C_BRACE to ItemConstant(), + ) + + @JvmStatic + fun isZeroOrPositive(value: String): Boolean { + return isNumeric(value) && value.toDouble() >= 0.0 + } + + fun isNumeric(value: String?): Boolean { + return value != null && isDouble(value) && NUMERIC_PATTERN.matcher(value).matches() + } + + private fun isDouble(value: String): Boolean { + return try { + value.toDouble() + true + } catch (e: NumberFormatException) { + false + } + } + + fun fromDouble(value: Double?): String { + return if (value != null && !value.isNaN()) { + val rounded = getRounded(value, 2) + rounded.toString().replace("\\.0+$".toRegex(), "") + } else { + "" + } + } + + fun getRounded(value: Double, decimals: Int): Double { + val factor = 10.0.pow(decimals.toDouble()) + return (value * factor).roundToInt() / factor + } + + /** + * Formats a Date to the format YYYY-MM-DD. + * + * @param date the Date to parse. + * @return A formatted date string. Null if argument is null. + */ + fun getMediumDateString(date: Date?): String? { + return date?.let { + val format = SimpleDateFormat() + format.applyPattern(DEFAULT_DATE_FORMAT) + format.format(date) + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.java deleted file mode 100644 index f9343b1304..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.parser.internal.service; - -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.COMMON_EXPRESSION_ITEMS; -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_EVALUATE; -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_GET_DESCRIPTIONS; -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_GET_IDS; -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_REGENERATE; -import static org.hisp.dhis.android.core.validation.MissingValueStrategy.NEVER_SKIP; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.HASH_BRACE; - -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; -import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStore; -import org.hisp.dhis.android.core.common.ObjectWithUid; -import org.hisp.dhis.android.core.constant.Constant; -import org.hisp.dhis.android.core.dataelement.DataElement; -import org.hisp.dhis.android.core.dataelement.DataElementOperand; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup; -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.CommonParser; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItemMethod; -import org.hisp.dhis.android.core.parser.internal.expression.literal.RegenerateLiteral; -import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimItemDataElementAndOperand; -import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemId; -import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject; -import org.hisp.dhis.android.core.program.ProgramStage; -import org.hisp.dhis.android.core.validation.MissingValueStrategy; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.inject.Inject; - -@SuppressWarnings({ - "PMD.TooManyStaticImports", - "PMD.ExcessiveImports", - "PMD.CyclomaticComplexity", - "PMD.StdCyclomaticComplexity"}) -public class ExpressionService { - - private final IdentifiableObjectStore dataElementStore; - private final CategoryOptionComboStore categoryOptionComboStore; - private final IdentifiableObjectStore organisationUnitGroupStore; - private final IdentifiableObjectStore programStageStore; - - private final Map validationRuleExpressionItems; - - @Inject - public ExpressionService(IdentifiableObjectStore dataElementStore, - CategoryOptionComboStore categoryOptionComboStore, - IdentifiableObjectStore organisationUnitGroupStore, - IdentifiableObjectStore programStageStore) { - this.dataElementStore = dataElementStore; - this.categoryOptionComboStore = categoryOptionComboStore; - this.organisationUnitGroupStore = organisationUnitGroupStore; - this.programStageStore = programStageStore; - this.validationRuleExpressionItems = getValidationRuleExpressionItems(); - } - - private Map getValidationRuleExpressionItems() { - Map expressionItems = new HashMap<>(COMMON_EXPRESSION_ITEMS); - - expressionItems.put(HASH_BRACE, new DimItemDataElementAndOperand()); - - return expressionItems; - } - - public Set getDimensionalItemIds(String expression) { - if (expression == null) { - return Collections.emptySet(); - } - - Set itemIds = new HashSet<>(); - CommonExpressionVisitor visitor = newVisitor(ITEM_GET_IDS, Collections.emptyMap()); - visitor.setItemIds(itemIds); - - CommonParser.visit(expression, visitor); - - return itemIds; - } - - public Set getDataElementOperands(String expression) { - Set dimensionalItemIds = getDimensionalItemIds(expression); - - Set dataElementOperands = new HashSet<>(); - for (DimensionalItemId di : dimensionalItemIds) { - if (di.isDataElementOrOperand()) { - dataElementOperands.add(DataElementOperand.builder() - .dataElement(ObjectWithUid.create(di.id0())) - .categoryOptionCombo(di.id1() == null ? null : ObjectWithUid.create(di.id1())) - .build()); - } - } - return dataElementOperands; - } - - public String getExpressionDescription(String expression, Map constantMap) { - - if (expression == null) { - return ""; - } - - CommonExpressionVisitor visitor = newVisitor(ITEM_GET_DESCRIPTIONS, constantMap); - - CommonParser.visit(expression, visitor); - - Map itemDescriptions = visitor.getItemDescriptions(); - - String description = expression; - - for (Map.Entry entry : itemDescriptions.entrySet()) { - description = description.replace(entry.getKey(), entry.getValue()); - } - - return description; - } - - public Object getExpressionValue(String expression) { - return getExpressionValue(expression, Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), - 0, NEVER_SKIP); - } - - public Object getExpressionValue(String expression, - Map valueMap, - Map constantMap, - Map orgUnitCountMap, - Integer days, - MissingValueStrategy missingValueStrategy) { - - if (expression == null) { - return null; - } - - CommonExpressionVisitor visitor = newVisitor( - ITEM_EVALUATE, - constantMap - ); - - Map itemValueMap = new HashMap<>(); - for (Map.Entry entry : valueMap.entrySet()) { - itemValueMap.put(entry.getKey().getDimensionItem(), entry.getValue()); - } - - visitor.setItemValueMap(itemValueMap); - visitor.setOrgUnitCountMap(orgUnitCountMap); - - if (days != null) { - visitor.setDays(Double.valueOf(days)); - } - - Object value = CommonParser.visit(expression, visitor); - - int itemsFound = visitor.getState().getItemsFound(); - int itemValuesFound = visitor.getState().getItemValuesFound(); - - switch (missingValueStrategy) { - case SKIP_IF_ANY_VALUE_MISSING: - if (itemValuesFound < itemsFound) { - return null; - } - - case SKIP_IF_ALL_VALUES_MISSING: - if (itemsFound != 0 && itemValuesFound == 0) { - return null; - } - - case NEVER_SKIP: - default: - if (value == null) { - // TODO Handle other ParseType - return 0d; - } - } - - if (value instanceof Double && Double.isNaN((double) value)) { - return null; - } else { - return value; - } - } - - public String regenerateExpression(String expression, - Map valueMap, - Map constantMap, - Map orgUnitCountMap, - Integer days) { - - if (expression == null) { - return ""; - } - - CommonExpressionVisitor visitor = newVisitor( - ITEM_REGENERATE, - constantMap - ); - - Map itemValueMap = new HashMap<>(); - for (Map.Entry entry : valueMap.entrySet()) { - itemValueMap.put(entry.getKey().getDimensionItem(), entry.getValue()); - } - - visitor.setItemValueMap(itemValueMap); - visitor.setOrgUnitCountMap(orgUnitCountMap); - visitor.setExpressionLiteral(new RegenerateLiteral()); - - if (days != null) { - visitor.setDays(Double.valueOf(days)); - } - - return (String) CommonParser.visit(expression, visitor); - } - - // ------------------------------------------------------------------------- - // Supportive methods - // ------------------------------------------------------------------------- - - /** - * Creates a new ExpressionItemsVisitor object. - */ - private CommonExpressionVisitor newVisitor( - //ParseType parseType, - ExpressionItemMethod itemMethod, - //List samplePeriods, - Map constantMap) { - return CommonExpressionVisitor.newBuilder() - //.withItemMap( PARSE_TYPE_EXPRESSION_ITEMS.get( parseType ) ) - .withItemMap(validationRuleExpressionItems) - .withItemMethod(itemMethod) - .withConstantMap(constantMap) - .withDataElementStore(dataElementStore) - .withCategoryOptionComboStore(categoryOptionComboStore) - .withOrganisationUnitGroupStore(organisationUnitGroupStore) - .withProgramStageStore(programStageStore) - //.withSamplePeriods( samplePeriods )() - .buildForExpressions(); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt new file mode 100644 index 0000000000..e58a9bd4ee --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.service + +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStore +import org.hisp.dhis.android.core.common.ObjectWithUid +import org.hisp.dhis.android.core.constant.Constant +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.dataelement.DataElementOperand +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitorScope +import org.hisp.dhis.android.core.parser.internal.expression.CommonParser.visit +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItemMethod +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.COMMON_EXPRESSION_ITEMS +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_EVALUATE +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_GET_DESCRIPTIONS +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_GET_IDS +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_REGENERATE +import org.hisp.dhis.android.core.parser.internal.expression.literal.RegenerateLiteral +import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimItemDataElementAndOperand +import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemId +import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject +import org.hisp.dhis.android.core.program.ProgramStage +import org.hisp.dhis.android.core.validation.MissingValueStrategy +import org.hisp.dhis.parser.expression.antlr.ExpressionParser + +internal class ExpressionService @Inject constructor( + private val dataElementStore: IdentifiableObjectStore, + private val categoryOptionComboStore: CategoryOptionComboStore, + private val organisationUnitGroupStore: IdentifiableObjectStore, + private val programStageStore: IdentifiableObjectStore +) { + private val validationRuleExpressionItems: Map = getValidationRuleExpressionItems() + + private fun getValidationRuleExpressionItems(): Map { + return COMMON_EXPRESSION_ITEMS + ( + ExpressionParser.HASH_BRACE to DimItemDataElementAndOperand() + ) + } + + fun getDimensionalItemIds(expression: String?): Set { + return if (expression == null) { + emptySet() + } else { + // TODO REVIEW + val itemIds: MutableSet = HashSet() + val visitor = newVisitor(ITEM_GET_IDS, emptyMap()) + visitor.itemIds = itemIds + visit(expression, visitor) + itemIds + } + } + + fun getDataElementOperands(expression: String?): Set { + val dimensionalItemIds = getDimensionalItemIds(expression) + + return dimensionalItemIds + .filter { it.isDataElementOrOperand } + .map { + DataElementOperand.builder() + .dataElement(ObjectWithUid.create(it.id0)) + .categoryOptionCombo(it.id1?.let { id -> ObjectWithUid.create(id) }) + .build() + } + .toSet() + } + + fun getExpressionDescription(expression: String?, constantMap: Map): String { + return if (expression == null) { + "" + } else { + val visitor = newVisitor(ITEM_GET_DESCRIPTIONS, constantMap) + visit(expression, visitor) + val itemDescriptions = visitor.itemDescriptions + var description: String = expression + for ((key, value) in itemDescriptions) { + description = description.replace(key, value) + } + description + } + } + + fun getExpressionValue(expression: String?): Any? { + return getExpressionValue( + expression, emptyMap(), emptyMap(), emptyMap(), 0, MissingValueStrategy.NEVER_SKIP + ) + } + + @Suppress("LongParameterList", "ComplexMethod", "ReturnCount") + fun getExpressionValue( + expression: String?, + valueMap: Map, + constantMap: Map, + orgUnitCountMap: Map, + days: Int?, + missingValueStrategy: MissingValueStrategy + ): Any? { + return expression?.let { + val visitor = newVisitor( + ITEM_EVALUATE, + constantMap + ) + val itemValueMap = valueMap.map { it.key.dimensionItem to it.value }.toMap() + + visitor.itemValueMap = itemValueMap + visitor.orgUnitCountMap = orgUnitCountMap + if (days != null) { + visitor.days = days.toDouble() + } + val value = visit(expression, visitor) + val itemsFound = visitor.state.itemsFound + val itemValuesFound = visitor.state.itemValuesFound + + when (missingValueStrategy) { + MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING -> { + if (itemValuesFound < itemsFound) { + return null + } + if (itemsFound != 0 && itemValuesFound == 0) { + return null + } + if (value == null) { + // TODO Handle other ParseType + return 0.0 + } + } + MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING -> { + if (itemsFound != 0 && itemValuesFound == 0) { + return null + } + if (value == null) { + return 0.0 + } + } + MissingValueStrategy.NEVER_SKIP -> if (value == null) { + return 0.0 + } + } + + if (value is Double && value.isNaN()) { + null + } else { + value + } + } + } + + fun regenerateExpression( + expression: String?, + valueMap: Map, + constantMap: Map, + orgUnitCountMap: Map, + days: Int? + ): String { + return if (expression == null) { + "" + } else { + val visitor = newVisitor( + ITEM_REGENERATE, + constantMap + ) + + val itemValueMap = valueMap.map { it.key.dimensionItem to it.value }.toMap() + visitor.itemValueMap = itemValueMap + visitor.orgUnitCountMap = orgUnitCountMap + visitor.setExpressionLiteral(RegenerateLiteral()) + if (days != null) { + visitor.days = days.toDouble() + } + visit(expression, visitor) as String + } + } + // ------------------------------------------------------------------------- + // Supportive methods + // ------------------------------------------------------------------------- + /** + * Creates a new ExpressionItemsVisitor object. + */ + private fun newVisitor( + // ParseType parseType, + itemMethod: ExpressionItemMethod, + // List samplePeriods, + constantMap: Map + ): CommonExpressionVisitor { + return CommonExpressionVisitor( + CommonExpressionVisitorScope.Expression( + itemMap = validationRuleExpressionItems, + itemMethod = itemMethod, + constantMap = constantMap, + dataElementStore = dataElementStore, + categoryOptionComboStore = categoryOptionComboStore, + organisationUnitGroupStore = organisationUnitGroupStore, + programStageStore = programStageStore + ) + ) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimItemDataElementAndOperand.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimItemDataElementAndOperand.java deleted file mode 100644 index bb02ed346e..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimItemDataElementAndOperand.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.parser.internal.service.dataitem; - -import org.hisp.dhis.android.core.category.CategoryOptionCombo; -import org.hisp.dhis.android.core.dataelement.DataElement; -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.antlr.ParserExceptionWithoutContext; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext; - -import static org.apache.commons.lang3.ObjectUtils.anyNotNull; -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL; -import static org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemType.DATA_ELEMENT; -import static org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemType.DATA_ELEMENT_OPERAND; - -/** - * Parsed expression item as handled by the expression service. - *

- * When getting item id and org unit group, just return default values - * (because not every item implements these, only those that need to.) - * - * @author Jim Grace - */ -public class DimItemDataElementAndOperand extends DimensionalItem { - - @Override - public Object getDescription(ExprContext ctx, CommonExpressionVisitor visitor) { - - DataElement dataElement = visitor.getDataElementStore().selectByUid(ctx.uid0.getText()); - - if (dataElement != null) { - StringBuilder description = new StringBuilder(dataElement.displayName()); - - if (isDataElementOperandSyntax(ctx)) { - CategoryOptionCombo categoryOptionCombo = - visitor.getCategoryOptionComboStore().selectByUid(ctx.uid1.getText()); - - String cocDescription = categoryOptionCombo == null ? ctx.uid1.getText() : - categoryOptionCombo.displayName(); - - description.append(" (").append(cocDescription).append(')'); - } - - visitor.getItemDescriptions().put(ctx.getText(), description.toString()); - } - - return DOUBLE_VALUE_IF_NULL; - } - - @Override - public DimensionalItemId getDimensionalItemId(ExprContext ctx) { - if (isDataElementOperandSyntax(ctx)) { - return DimensionalItemId.builder() - .dimensionalItemType(DATA_ELEMENT_OPERAND) - .id0(ctx.uid0.getText()) - .id1(ctx.uid1 == null ? null : ctx.uid1.getText()) - .id2(ctx.uid2 == null ? null : ctx.uid2.getText()) - .build(); - } else { - return DimensionalItemId.builder() - .dimensionalItemType(DATA_ELEMENT) - .id0(ctx.uid0.getText()) - .build(); - } - } - - @Override - public String getId(ExprContext ctx) { - if (isDataElementOperandSyntax(ctx)) { - return ctx.uid0.getText() + "." + - (ctx.uid1 == null ? "*" : ctx.uid1.getText()) + - (ctx.uid2 == null ? "" : "." + ctx.uid2.getText()); - } else { - // Data element: - return ctx.uid0.getText(); - } - } - - /** - * Does an item of the form #{...} have the syntax of a - * data element operand (as opposed to a data element)? - * - * @param ctx the item context - * @return true if data element operand syntax - */ - private boolean isDataElementOperandSyntax(ExprContext ctx) { - if (ctx.uid0 == null) { - throw new ParserExceptionWithoutContext("Data Element or DataElementOperand must have a uid " + - ctx.getText()); - } - - return anyNotNull(ctx.uid1, ctx.uid2); - } - -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimItemDataElementAndOperand.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimItemDataElementAndOperand.kt new file mode 100644 index 0000000000..78bcfe2ba6 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimItemDataElementAndOperand.kt @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.service.dataitem + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +/** + * Parsed expression item as handled by the expression service. + * + * + * When getting item id and org unit group, just return default values + * (because not every item implements these, only those that need to.) + * + * @author Jim Grace + */ +internal class DimItemDataElementAndOperand : DimensionalItem() { + override fun getDescription(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val dataElement = visitor.dataElementStore!!.selectByUid(ctx.uid0.text) + + dataElement?.displayName()?.let { deName -> + visitor.itemDescriptions[ctx.text] = + buildString { + append(deName) + if (isDataElementOperandSyntax(ctx)) { + val categoryOptionCombo = visitor.categoryOptionComboStore!!.selectByUid(ctx.uid1.text) + val cocDescription = categoryOptionCombo?.displayName() ?: ctx.uid1.text + append(" ($cocDescription)") + } + } + } + + return DOUBLE_VALUE_IF_NULL + } + + override fun getDimensionalItemId(ctx: ExprContext): DimensionalItemId { + return if (isDataElementOperandSyntax(ctx)) { + DimensionalItemId( + dimensionalItemType = DimensionalItemType.DATA_ELEMENT_OPERAND, + id0 = ctx.uid0.text, + id1 = ctx.uid1?.text, + id2 = ctx.uid2?.text + ) + } else { + DimensionalItemId( + dimensionalItemType = DimensionalItemType.DATA_ELEMENT, + id0 = ctx.uid0.text + ) + } + } + + override fun getId(ctx: ExprContext): String { + return if (isDataElementOperandSyntax(ctx)) { + ctx.uid0.text + "." + + (ctx.uid1?.text ?: "*") + + (ctx.uid2?.text?.let { ".$it" } ?: "") + } else { + // Data element: + ctx.uid0.text + } + } + + /** + * Does an item of the form #{...} have the syntax of a + * data element operand (as opposed to a data element)? + * + * @param ctx the item context + * @return true if data element operand syntax + */ + private fun isDataElementOperandSyntax(ctx: ExprContext): Boolean { + if (ctx.uid0 == null) { + throw ParserExceptionWithoutContext("Data Element or DataElementOperand must have a uid " + ctx.text) + } + return listOf(ctx.uid1, ctx.uid2).any { it != null } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItem.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItem.kt similarity index 65% rename from core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItem.java rename to core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItem.kt index a8555469c0..7be4d71537 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItem.java +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItem.kt @@ -25,51 +25,36 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.parser.internal.service.dataitem -package org.hisp.dhis.android.core.parser.internal.service.dataitem; - -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext; - -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL; +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext /** * Parsed dimensional item as handled by the expression service. * * @author Jim Grace */ -public abstract class DimensionalItem - implements ExpressionItem { - - @Override - public final Object getItemId(ExprContext ctx, CommonExpressionVisitor visitor) { - visitor.getItemIds().add(getDimensionalItemId(ctx)); - - return DOUBLE_VALUE_IF_NULL; +internal abstract class DimensionalItem : ExpressionItem { + override fun getItemId(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + visitor.itemIds.add(getDimensionalItemId(ctx)) + return DOUBLE_VALUE_IF_NULL } - @Override - public final Object getOrgUnitGroup(ExprContext ctx, CommonExpressionVisitor visitor) { - return DOUBLE_VALUE_IF_NULL; + override fun getOrgUnitGroup(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + return DOUBLE_VALUE_IF_NULL } - @Override - public final Object evaluate(ExprContext ctx, CommonExpressionVisitor visitor) { - Double value = visitor.getItemValueMap().get(getId(ctx)); - - return visitor.handleNulls(value); + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val value = visitor.itemValueMap[getId(ctx)] + return visitor.handleNulls(value)!! } - @Override - public final Object regenerate(ExprContext ctx, CommonExpressionVisitor visitor) { - Double value = visitor.getItemValueMap().get(getId(ctx)); - - if (value == null) { - return ctx.getText(); - } else { - return value.toString(); - } + override fun regenerate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val value = visitor.itemValueMap[getId(ctx)] + return value?.toString() ?: ctx.text } /** @@ -78,15 +63,16 @@ public final Object regenerate(ExprContext ctx, CommonExpressionVisitor visitor) * @param ctx the parser item context * @return the DimensionalItemId object for this item */ - public abstract DimensionalItemId getDimensionalItemId(ExprContext ctx); + abstract fun getDimensionalItemId(ctx: ExprContext): DimensionalItemId /** * Returns the id for this item. - *

+ * + * * For example, uid, or uid1.uid2, etc. * * @param ctx the parser item context * @return the id for this item */ - public abstract String getId(ExprContext ctx); + abstract fun getId(ctx: ExprContext): String } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItemId.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItemId.java deleted file mode 100644 index e59453918e..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItemId.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.parser.internal.service.dataitem; - -import androidx.annotation.Nullable; - -import com.google.auto.value.AutoValue; - -import static org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemType.DATA_ELEMENT; -import static org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemType.DATA_ELEMENT_OPERAND; - -@AutoValue -public abstract class DimensionalItemId { - - public abstract DimensionalItemType dimensionalItemType(); - - public abstract String id0(); - - @Nullable - public abstract String id1(); - - @Nullable - public abstract String id2(); - - public static Builder builder() { - return new AutoValue_DimensionalItemId.Builder(); - } - - @AutoValue.Builder - public abstract static class Builder { - - public abstract Builder dimensionalItemType(DimensionalItemType dimensionalItemType); - - public abstract Builder id0(String id0); - - public abstract Builder id1(@Nullable String id1); - - public abstract Builder id2(@Nullable String id2); - - public abstract DimensionalItemId build(); - } - - public boolean isDataElementOrOperand() { - return dimensionalItemType() == DATA_ELEMENT - || dimensionalItemType() == DATA_ELEMENT_OPERAND; - } - -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItemId.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItemId.kt new file mode 100644 index 0000000000..db66d65b50 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/DimensionalItemId.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.service.dataitem + +internal class DimensionalItemId( + val dimensionalItemType: DimensionalItemType, + val id0: String, + val id1: String? = null, + val id2: String? = null +) { + val isDataElementOrOperand: Boolean + get() = ( + dimensionalItemType === DimensionalItemType.DATA_ELEMENT || + dimensionalItemType === DimensionalItemType.DATA_ELEMENT_OPERAND + ) +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemConstant.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemConstant.java deleted file mode 100644 index 35046a2283..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemConstant.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.parser.internal.service.dataitem; - -import org.hisp.dhis.android.core.constant.Constant; -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.antlr.ParserExceptionWithoutContext; - -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext; - -/** - * Expression item Constant - * - * @author Jim Grace - */ -public class ItemConstant implements ExpressionItem { - - @Override - public Object getDescription(ExprContext ctx, CommonExpressionVisitor visitor) { - Constant constant = visitor.getConstantMap().get(ctx.uid0.getText()); - - if (constant == null) { - throw new ParserExceptionWithoutContext("No constant defined for " + ctx.uid0.getText()); - } - - visitor.getItemDescriptions().put(ctx.getText(), constant.displayName()); - - return DOUBLE_VALUE_IF_NULL; - } - - @Override - public Object getItemId(ExprContext ctx, CommonExpressionVisitor visitor) { - visitor.getItemIds().add(getDimensionalItemId(ctx)); - - return DOUBLE_VALUE_IF_NULL; - } - - @Override - public Object evaluate(ExprContext ctx, CommonExpressionVisitor visitor) { - Constant constant = visitor.getConstantMap().get(ctx.uid0.getText()); - - if (constant == null) { - throw new ParserExceptionWithoutContext("Can't find constant to evaluate " + ctx.uid0.getText()); - } - - return constant.value(); - } - - @Override - public final Object regenerate(ExprContext ctx, CommonExpressionVisitor visitor) { - Constant constant = visitor.getConstantMap().get(ctx.uid0.getText()); - - if (constant == null || constant.value() == null) { - return ctx.getText(); - } else { - return constant.value().toString(); - } - } - - @Override - public Object getSql(ExprContext ctx, CommonExpressionVisitor visitor) { - return evaluate(ctx, visitor); - } - - private DimensionalItemId getDimensionalItemId(ExprContext ctx) { - return DimensionalItemId.builder() - .dimensionalItemType(DimensionalItemType.CONSTANT) - .id0(ctx.uid0.getText()) - .build(); - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemConstant.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemConstant.kt new file mode 100644 index 0000000000..05cc79be99 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemConstant.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.service.dataitem + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +/** + * Expression item Constant + * + * @author Jim Grace + */ +internal class ItemConstant : ExpressionItem { + override fun getDescription(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val constantDisplayName = visitor.constantMap[ctx.uid0.text]?.displayName() + ?: throw ParserExceptionWithoutContext("No constant defined for " + ctx.uid0.text) + visitor.itemDescriptions[ctx.text] = constantDisplayName + + return DOUBLE_VALUE_IF_NULL + } + + override fun getItemId(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + visitor.itemIds.add(getDimensionalItemId(ctx)) + + return DOUBLE_VALUE_IF_NULL + } + + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val constantValue = visitor.constantMap[ctx.uid0.text]?.value() + ?: throw ParserExceptionWithoutContext("Can't find constant to evaluate " + ctx.uid0.text) + + return constantValue + } + + override fun regenerate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val constant = visitor.constantMap[ctx.uid0.text] + + return constant?.value()?.toString() ?: ctx.text + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + return evaluate(ctx, visitor) + } + + fun getDimensionalItemId(ctx: ExprContext): DimensionalItemId { + return DimensionalItemId( + dimensionalItemType = DimensionalItemType.CONSTANT, + id0 = ctx.uid0.text + ) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemOrgUnitGroup.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemOrgUnitGroup.kt similarity index 50% rename from core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemOrgUnitGroup.java rename to core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemOrgUnitGroup.kt index bc3f2d136e..770de7e76f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemOrgUnitGroup.java +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemOrgUnitGroup.kt @@ -25,35 +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.parser.internal.service.dataitem -package org.hisp.dhis.android.core.parser.internal.service.dataitem; - -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup; -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.antlr.ParserExceptionWithoutContext; - -import static org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL; -import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext; +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.DOUBLE_VALUE_IF_NULL +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext /** * Expression item OrganisationUnitGroup * * @author Jim Grace */ -public class ItemOrgUnitGroup implements ExpressionItem { - - @Override - public Object getDescription(ExprContext ctx, CommonExpressionVisitor visitor) { - OrganisationUnitGroup orgUnitGroup = visitor.getOrganisationUnitGroupStore().selectByUid(ctx.uid0.getText()); +internal class ItemOrgUnitGroup : ExpressionItem { + override fun getDescription(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val orgUnitGroupName = visitor.organisationUnitGroupStore!!.selectByUid(ctx.uid0.text)?.displayName() + ?: throw ParserExceptionWithoutContext("No organization unit group defined for " + ctx.uid0.text) - if (orgUnitGroup == null) { - throw new ParserExceptionWithoutContext("No organization unit group defined for " + ctx.uid0.getText()); - } + visitor.itemDescriptions[ctx.text] = orgUnitGroupName - visitor.getItemDescriptions().put(ctx.getText(), orgUnitGroup.displayName()); - - return DOUBLE_VALUE_IF_NULL; + return DOUBLE_VALUE_IF_NULL } /* @@ -65,40 +57,28 @@ public Object getOrgUnitGroup( ExprContext ctx, CommonExpressionVisitor visitor return DOUBLE_VALUE_IF_NULL; } */ + override fun getItemId(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + visitor.itemIds.add(getDimensionalItemId(ctx)) - @Override - public Object getItemId(ExprContext ctx, CommonExpressionVisitor visitor) { - visitor.getItemIds().add(getDimensionalItemId(ctx)); - - return DOUBLE_VALUE_IF_NULL; + return DOUBLE_VALUE_IF_NULL } - @Override - public Object evaluate(ExprContext ctx, CommonExpressionVisitor visitor) { - Integer count = visitor.getOrgUnitCountMap().get(ctx.uid0.getText()); - - if (count == null) { - throw new ParserExceptionWithoutContext("Can't find count for orgunitGroup unit " + ctx.uid0.getText()); - } + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val count = visitor.orgUnitCountMap[ctx.uid0.text] + ?: throw ParserExceptionWithoutContext("Can't find count for orgunitGroup unit " + ctx.uid0.text) - return count.doubleValue(); + return count.toDouble() } - @Override - public final Object regenerate(ExprContext ctx, CommonExpressionVisitor visitor) { - Integer count = visitor.getOrgUnitCountMap().get(ctx.uid0.getText()); - - if (count == null) { - return ctx.getText(); - } else { - return count.toString(); - } + override fun regenerate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val count = visitor.orgUnitCountMap[ctx.uid0.text] + return count?.toString() ?: ctx.text } - private DimensionalItemId getDimensionalItemId(ExprContext ctx) { - return DimensionalItemId.builder() - .dimensionalItemType(DimensionalItemType.ORGANISATION_UNIT_GROUP) - .id0(ctx.uid0.getText()) - .build(); + private fun getDimensionalItemId(ctx: ExprContext): DimensionalItemId { + return DimensionalItemId( + dimensionalItemType = DimensionalItemType.ORGANISATION_UNIT_GROUP, + id0 = ctx.uid0.text + ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementObject.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementObject.kt similarity index 78% rename from core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementObject.java rename to core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementObject.kt index b53d48c79e..a348f7d941 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementObject.java +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementObject.kt @@ -25,23 +25,9 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.parser.internal.service.dataobject -package org.hisp.dhis.android.core.parser.internal.service.dataobject; - -import com.google.auto.value.AutoValue; - -@AutoValue -public abstract class DataElementObject implements DimensionalItemObject { - - abstract String dataElement(); - - public static DataElementObject create(String dataElement) { - return new AutoValue_DataElementObject(dataElement); - } - - @Override - public String getDimensionItem() { - return dataElement(); - } - +internal data class DataElementObject(val dataElement: String) : DimensionalItemObject { + override val dimensionItem: String + get() = dataElement } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementOperandObject.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementOperandObject.java deleted file mode 100644 index b128e2b619..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementOperandObject.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.parser.internal.service.dataobject; - -import androidx.annotation.Nullable; - -import com.google.auto.value.AutoValue; - -@AutoValue -public abstract class DataElementOperandObject implements DimensionalItemObject { - private static final String SEPARATOR = "."; - - private static final String SYMBOL_WILDCARD = "*"; - - abstract String dataElement(); - - @Nullable - abstract String categoryOptionCombo(); - - @Nullable - abstract String attributeOptionCombo(); - - public static DataElementOperandObject create(String dataElement, String categoryOptionCombo) { - return create(dataElement, categoryOptionCombo, null); - } - - public static DataElementOperandObject create(String dataElement, String categoryOptionCombo, - String attributeOptionCombo) { - return new AutoValue_DataElementOperandObject(dataElement, categoryOptionCombo, attributeOptionCombo); - } - - @Override - public String getDimensionItem() { - StringBuilder item = new StringBuilder(); - - if (dataElement() != null) { - item.append(dataElement()); - - if (categoryOptionCombo() == null) { - if (attributeOptionCombo() != null) { - item.append(SEPARATOR).append(SYMBOL_WILDCARD); - } - } else { - item.append(SEPARATOR).append(categoryOptionCombo()); - } - - if (attributeOptionCombo() != null) { - item.append(SEPARATOR).append(attributeOptionCombo()); - } - } - - return item.toString(); - } - -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementOperandObject.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementOperandObject.kt new file mode 100644 index 0000000000..fe113c90b0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DataElementOperandObject.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.service.dataobject + +internal data class DataElementOperandObject( + val dataElement: String, + val categoryOptionCombo: String?, + val attributeOptionCombo: String? = null +) : DimensionalItemObject { + override val dimensionItem: String + get() { + val item = StringBuilder() + item.append(dataElement) + if (categoryOptionCombo == null) { + if (attributeOptionCombo != null) { + item.append(SEPARATOR).append(SYMBOL_WILDCARD) + } + } else { + item.append(SEPARATOR).append(categoryOptionCombo) + } + if (attributeOptionCombo != null) { + item.append(SEPARATOR).append(attributeOptionCombo) + } + return item.toString() + } + + companion object { + private const val SEPARATOR = "." + private const val SYMBOL_WILDCARD = "*" + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DimensionalItemObject.java b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DimensionalItemObject.kt similarity index 95% rename from core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DimensionalItemObject.java rename to core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DimensionalItemObject.kt index c9e119779c..c37ebe9d68 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DimensionalItemObject.java +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataobject/DimensionalItemObject.kt @@ -25,12 +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.parser.internal.service.dataobject -package org.hisp.dhis.android.core.parser.internal.service.dataobject; - -public interface DimensionalItemObject { +internal interface DimensionalItemObject { /** * Gets the dimension item identifier. */ - String getDimensionItem(); + val dimensionItem: String } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelper.kt index e87e3e7b03..19c69acad4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelper.kt @@ -41,12 +41,15 @@ internal object ExpressionHelper { fun getValueMap(dataValues: List): Map { val valueMap: MutableMap = HashMap() for (dataValue in dataValues) { - val deId = dataValue.dataElement() - val cocId = dataValue.categoryOptionCombo() - val dataElementItem: DimensionalItemObject = DataElementObject.create(deId) - addDimensionalItemValueToMap(dataElementItem, dataValue.value(), valueMap) - val dataElementOperandItem: DimensionalItemObject = DataElementOperandObject.create(deId, cocId) - addDimensionalItemValueToMap(dataElementOperandItem, dataValue.value(), valueMap) + dataValue.dataElement()?.let { deId -> + val dataElementItem: DimensionalItemObject = DataElementObject(deId) + addDimensionalItemValueToMap(dataElementItem, dataValue.value(), valueMap) + + dataValue.categoryOptionCombo()?.let { cocId -> + val dataElementOperandItem: DimensionalItemObject = DataElementOperandObject(deId, cocId) + addDimensionalItemValueToMap(dataElementOperandItem, dataValue.value(), valueMap) + } + } } return valueMap } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramExpressionItem.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramExpressionItem.kt index e37ac42549..cb54b8b0dc 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramExpressionItem.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramExpressionItem.kt @@ -38,7 +38,7 @@ import org.hisp.dhis.android.core.program.programindicatorengine.internal.variab import org.hisp.dhis.antlr.ParserExceptionWithoutContext import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext -abstract class ProgramExpressionItem : ExpressionItem { +internal abstract class ProgramExpressionItem : ExpressionItem { protected fun getProgramArgType(ctx: ExprContext): ProgramExpressionItem { return when { @@ -51,7 +51,7 @@ abstract class ProgramExpressionItem : ExpressionItem { } protected fun getLatestEvent(visitor: CommonExpressionVisitor): Event? { - val events = visitor.programIndicatorContext.events + val events = visitor.programIndicatorContext!!.events return events.values.flatten().sortedByDescending { it.eventDate() }.firstOrNull() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorContext.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorContext.kt index f4e8d19641..6edd1be6f1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorContext.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorContext.kt @@ -32,7 +32,7 @@ import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.program.ProgramIndicator import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue -internal data class ProgramIndicatorContext( +data class ProgramIndicatorContext( val programIndicator: ProgramIndicator, val enrollment: Enrollment? = null, val attributeValues: Map = mapOf(), diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutor.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutor.kt index eed3964169..249888ca77 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutor.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutor.kt @@ -30,10 +30,10 @@ package org.hisp.dhis.android.core.program.programindicatorengine.internal import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.constant.Constant import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.parser.internal.expression.* import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitorScope import org.hisp.dhis.android.core.parser.internal.expression.CommonParser -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItemMethod -import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.program.ProgramIndicator import org.hisp.dhis.android.core.program.ProgramStage import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute @@ -66,7 +66,7 @@ internal class ProgramIndicatorExecutor constructor( val result = CommonParser.visit(expression, visitor) val resultStr = AntlrParserUtils.castString(result) if (ParserUtils.isNumeric(resultStr)) { - ParserUtils.fromDouble(java.lang.Double.valueOf(resultStr)) + ParserUtils.fromDouble(resultStr.toDouble()) } else { resultStr } @@ -108,15 +108,17 @@ internal class ProgramIndicatorExecutor constructor( } private fun newVisitor(itemMethod: ExpressionItemMethod): CommonExpressionVisitor { - return CommonExpressionVisitor.newBuilder() - .withItemMap(ProgramIndicatorParserUtils.PROGRAM_INDICATOR_EXPRESSION_ITEMS) - .withItemMethod(itemMethod) - .withConstantMap(constantMap) - .withProgramIndicatorContext(programIndicatorContext) - .withProgramIndicatorExecutor(this) - .withDataElementStore(dataElementStore) - .withTrackedEntityAttributeStore(trackedEntityAttributeStore) - .withProgramStageStore(programStageStore) - .buildForProgramIndicator() + return CommonExpressionVisitor( + CommonExpressionVisitorScope.ProgramIndicator( + itemMap = ProgramIndicatorParserUtils.PROGRAM_INDICATOR_EXPRESSION_ITEMS, + itemMethod = itemMethod, + constantMap = constantMap, + programIndicatorContext = programIndicatorContext, + programIndicatorExecutor = this, + dataElementStore = dataElementStore, + trackedEntityAttributeStore = trackedEntityAttributeStore, + programStageStore = programStageStore + ) + ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorItemIdsCollector.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorItemIdsCollector.kt index de7d550959..2008d6c738 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorItemIdsCollector.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorItemIdsCollector.kt @@ -51,11 +51,11 @@ internal class ProgramIndicatorItemIdsCollector : ExpressionBaseListener() { val dataElementId = ctx.uid1.text itemIds.add( - DimensionalItemId.builder() - .dimensionalItemType(DimensionalItemType.TRACKED_ENTITY_DATA_VALUE) - .id0(stageId) - .id1(dataElementId) - .build() + DimensionalItemId( + dimensionalItemType = DimensionalItemType.TRACKED_ENTITY_DATA_VALUE, + id0 = stageId, + id1 = dataElementId + ) ) } @@ -65,10 +65,10 @@ internal class ProgramIndicatorItemIdsCollector : ExpressionBaseListener() { val attributeId = ctx.uid0.text itemIds.add( - DimensionalItemId.builder() - .dimensionalItemType(DimensionalItemType.TRACKED_ENTITY_ATTRIBUTE) - .id0(attributeId) - .build() + DimensionalItemId( + dimensionalItemType = DimensionalItemType.TRACKED_ENTITY_ATTRIBUTE, + id0 = attributeId + ) ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLContext.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLContext.kt index 8c4daf25e6..271a0d4e81 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLContext.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLContext.kt @@ -30,7 +30,7 @@ package org.hisp.dhis.android.core.program.programindicatorengine.internal import org.hisp.dhis.android.core.period.Period import org.hisp.dhis.android.core.program.ProgramIndicator -internal data class ProgramIndicatorSQLContext( +data class ProgramIndicatorSQLContext( val programIndicator: ProgramIndicator, val periods: List? ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt index 459a6243e0..9e786141b9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt @@ -42,10 +42,7 @@ import org.hisp.dhis.android.core.constant.Constant import org.hisp.dhis.android.core.dataelement.DataElement import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo import org.hisp.dhis.android.core.event.EventTableInfo -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor -import org.hisp.dhis.android.core.parser.internal.expression.CommonParser -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItemMethod -import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.parser.internal.expression.* import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLUtils.enrollment import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLUtils.event import org.hisp.dhis.android.core.program.programindicatorengine.internal.literal.ProgramIndicatorSQLLiteral @@ -107,7 +104,7 @@ internal class ProgramIndicatorSQLExecutor @Inject constructor( Parser.listen(programIndicator.expression(), collector) val sqlVisitor = newVisitor(ParserUtils.ITEM_GET_SQL, context) - sqlVisitor.itemIds = collector.itemIds.toSet() + sqlVisitor.itemIds = collector.itemIds.toMutableSet() sqlVisitor.setExpressionLiteral(ProgramIndicatorSQLLiteral()) val aggregator = ProgramIndicatorEvaluatorHelper.getAggregator(evaluationItem, programIndicator) @@ -134,13 +131,15 @@ internal class ProgramIndicatorSQLExecutor @Inject constructor( itemMethod: ExpressionItemMethod, context: ProgramIndicatorSQLContext ): CommonExpressionVisitor { - return CommonExpressionVisitor.newBuilder() - .withItemMap(ProgramIndicatorParserUtils.PROGRAM_INDICATOR_SQL_EXPRESSION_ITEMS) - .withItemMethod(itemMethod) - .withConstantMap(constantMap()) - .withProgramIndicatorSQLContext(context) - .withDataElementStore(dataElementStore) - .withTrackedEntityAttributeStore(trackedEntityAttributeStore) - .buildForProgramSQLIndicator() + return CommonExpressionVisitor( + CommonExpressionVisitorScope.ProgramSQLIndicator( + itemMap = ProgramIndicatorParserUtils.PROGRAM_INDICATOR_SQL_EXPRESSION_ITEMS, + itemMethod = itemMethod, + constantMap = constantMap(), + programIndicatorSQLContext = context, + dataElementStore = dataElementStore, + trackedEntityAttributeStore = trackedEntityAttributeStore + ) + ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLUtils.kt index 386fde8bfe..44840e4f15 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLUtils.kt @@ -156,17 +156,17 @@ internal object ProgramIndicatorSQLUtils { conditionalValueExpression: String? = null ): String { val stageElementItems = itemIds.filter { - it.dimensionalItemType() == DimensionalItemType.TRACKED_ENTITY_DATA_VALUE + it.dimensionalItemType == DimensionalItemType.TRACKED_ENTITY_DATA_VALUE } val attributeItems = itemIds.filter { - it.dimensionalItemType() == DimensionalItemType.TRACKED_ENTITY_ATTRIBUTE + it.dimensionalItemType == DimensionalItemType.TRACKED_ENTITY_ATTRIBUTE } val stageElementsSql = if (!stageElementItems.isNullOrEmpty()) { val stageElementWhereClause = stageElementItems.joinToString(" OR ") { - "(${EventTableInfo.Columns.PROGRAM_STAGE} = '${it.id0()}' " + + "(${EventTableInfo.Columns.PROGRAM_STAGE} = '${it.id0}' " + "AND " + - "${TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT} = '${it.id1()}')" + "${TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT} = '${it.id1}')" } "SELECT COUNT(*) " + @@ -187,7 +187,7 @@ internal object ProgramIndicatorSQLUtils { val attributesSql = if (!attributeItems.isNullOrEmpty()) { val attributesWhereClause = "${TrackedEntityAttributeValueTableInfo.Columns.TRACKED_ENTITY_ATTRIBUTE} IN " + - "(${attributeItems.joinToString(",") { "'${it.id0()}'" }})" + "(${attributeItems.joinToString(",") { "'${it.id0}'" }})" "SELECT COUNT(*) " + "FROM ${TrackedEntityAttributeValueTableInfo.TABLE_INFO.name()} " + diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemAttribute.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemAttribute.kt index 725f26ee8f..9ce4d3ed0a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemAttribute.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemAttribute.kt @@ -46,7 +46,7 @@ internal class ProgramItemAttribute : ProgramExpressionItem() { val attributeUid = ctx.uid0.text - val attributeValue = visitor.programIndicatorContext.attributeValues[attributeUid] + val attributeValue = visitor.programIndicatorContext!!.attributeValues[attributeUid] val attribute = getAttribute(visitor, attributeUid) val value = attributeValue?.value() @@ -73,7 +73,7 @@ internal class ProgramItemAttribute : ProgramExpressionItem() { val selectExpression = ProgramIndicatorSQLUtils.getAttributeWhereClause( column = valueCastExpression, attributeUid = attributeUid, - programIndicator = visitor.programIndicatorSQLContext.programIndicator + programIndicator = visitor.programIndicatorSQLContext!!.programIndicator ) return if (visitor.state.replaceNulls) { @@ -85,15 +85,15 @@ internal class ProgramItemAttribute : ProgramExpressionItem() { override fun getItemId(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { return visitor.itemIds.add( - DimensionalItemId.builder() - .dimensionalItemType(DimensionalItemType.TRACKED_ENTITY_ATTRIBUTE) - .id0(ctx.uid0.text) - .build() + DimensionalItemId( + dimensionalItemType = DimensionalItemType.TRACKED_ENTITY_ATTRIBUTE, + id0 = ctx.uid0.text + ) ) } private fun getAttribute(visitor: CommonExpressionVisitor, uid: String): TrackedEntityAttribute { - return visitor.trackedEntityAttributeStore.selectByUid(uid) + return visitor.trackedEntityAttributeStore!!.selectByUid(uid) ?: throw IllegalArgumentException("Attribute $uid does not exist.") } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemPsEventdate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemPsEventdate.kt index b4d986717b..f8635f9d58 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemPsEventdate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemPsEventdate.kt @@ -40,7 +40,7 @@ internal class ProgramItemPsEventdate : ProgramExpressionItem() { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { val programStageUid = ctx.uid0.text - val eventList = visitor.programIndicatorContext.events[programStageUid] + val eventList = visitor.programIndicatorContext!!.events[programStageUid] return if (eventList == null) { null @@ -52,7 +52,7 @@ internal class ProgramItemPsEventdate : ProgramExpressionItem() { override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { val programStageId = ctx.uid0.text - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EventTableInfo.Columns.EVENT_DATE}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemStageElement.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemStageElement.kt index e731e33ef7..62ee7f46bf 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemStageElement.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemStageElement.kt @@ -27,7 +27,6 @@ */ package org.hisp.dhis.android.core.program.programindicatorengine.internal.dataitem -import java.util.* import org.hisp.dhis.android.core.dataelement.DataElement import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor @@ -49,7 +48,7 @@ internal class ProgramItemStageElement : ProgramExpressionItem() { val stageId = ctx.uid0.text val dataElementId = ctx.uid1.text - val eventList = visitor.programIndicatorContext.events[stageId] + val eventList = visitor.programIndicatorContext!!.events[stageId] var value: String? = null if (eventList != null) { @@ -100,7 +99,7 @@ internal class ProgramItemStageElement : ProgramExpressionItem() { column = valueCastExpression, programStageUid = programStageId, dataElementUid = dataElementId, - programIndicator = visitor.programIndicatorSQLContext.programIndicator + programIndicator = visitor.programIndicatorSQLContext!!.programIndicator ) return if (visitor.state.replaceNulls) { @@ -115,16 +114,16 @@ internal class ProgramItemStageElement : ProgramExpressionItem() { val dataElementId = ctx.uid1.text return visitor.itemIds.add( - DimensionalItemId.builder() - .dimensionalItemType(DimensionalItemType.TRACKED_ENTITY_DATA_VALUE) - .id0(stageId) - .id1(dataElementId) - .build() + DimensionalItemId( + dimensionalItemType = DimensionalItemType.TRACKED_ENTITY_DATA_VALUE, + id0 = stageId, + id1 = dataElementId + ) ) } private fun getDataElement(visitor: CommonExpressionVisitor, uid: String): DataElement { - return visitor.dataElementStore.selectByUid(uid) + return visitor.dataElementStore!!.selectByUid(uid) ?: throw IllegalArgumentException("DataElement $uid does not exist.") } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2AddDays.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2AddDays.kt index 6c3f793ae7..fa27dc7057 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2AddDays.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2AddDays.kt @@ -35,7 +35,7 @@ import org.joda.time.DateTime internal class D2AddDays : ExpressionItem { - override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { val dateStr = visitor.castStringVisit(ctx.expr(0)) val days = visitor.castStringVisit(ctx.expr(1)) val date = DateTime(dateStr) diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Condition.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Condition.kt index 5f9577fec3..3b41be5800 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Condition.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Condition.kt @@ -41,7 +41,7 @@ internal class D2Condition : ExpressionItem { val valueIfTrue = visitor.castStringVisit(ctx.expr(0)) val valueIfFalse = visitor.castStringVisit(ctx.expr(1)) - val testResult = visitor.programIndicatorExecutor.getProgramIndicatorExpressionValue(testExpression) + val testResult = visitor.programIndicatorExecutor!!.getProgramIndicatorExpressionValue(testExpression) return if ("true" == testResult) { valueIfTrue diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CountIfCondition.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CountIfCondition.kt index a32e32f60b..0fa386b649 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CountIfCondition.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CountIfCondition.kt @@ -36,7 +36,7 @@ internal class D2CountIfCondition : ProgramCountFunction() { override fun countIf(ctx: ExprContext, visitor: CommonExpressionVisitor, value: String?): Boolean { val expression = value + trimQuotes(ctx.stringLiteral().text) - val result = visitor.programIndicatorExecutor.getProgramIndicatorExpressionValue(expression) + val result = visitor.programIndicatorExecutor!!.getProgramIndicatorExpressionValue(expression) return "true" == result } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2HasValue.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2HasValue.kt index b454dca410..de79bd6121 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2HasValue.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2HasValue.kt @@ -55,14 +55,14 @@ internal class D2HasValue : ProgramExpressionItem() { private fun hasProgramAttribute(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { val attribute = ctx.uid0.text - return visitor.programIndicatorContext.attributeValues.values.any { attributeValue -> + return visitor.programIndicatorContext!!.attributeValues.values.any { attributeValue -> attribute == attributeValue.trackedEntityAttribute() && attributeValue.value() != null }.toString() } private fun hasProgramItemStageElement(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { val programStage = ctx.uid0.text - val stageEvents = visitor.programIndicatorContext.events[programStage] ?: emptyList() + val stageEvents = visitor.programIndicatorContext!!.events[programStage] ?: emptyList() val dataElement = ctx.uid1.text return stageEvents.any { event -> @@ -85,7 +85,7 @@ internal class D2HasValue : ProgramExpressionItem() { private fun hasProgramAttributeSQL(ctx: ExprContext, visitor: CommonExpressionVisitor): String { val attributeUid = ctx.uid0.text - val enrollmentSelector = getEnrollmentWhereClause(visitor.programIndicatorSQLContext.programIndicator) + val enrollmentSelector = getEnrollmentWhereClause(visitor.programIndicatorSQLContext!!.programIndicator) return "EXISTS(SELECT 1 " + "FROM ${TrackedEntityAttributeValueTableInfo.TABLE_INFO.name()} " + @@ -108,7 +108,7 @@ internal class D2HasValue : ProgramExpressionItem() { "ON ${TrackedEntityDataValueTableInfo.Columns.EVENT} = ${EventTableInfo.Columns.UID} " + "WHERE ${TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT} = '$dataElementUid' " + "AND ${EventTableInfo.Columns.PROGRAM_STAGE} = '$programStageUid' " + - "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext.programIndicator)} " + + "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext!!.programIndicator)} " + "AND ${TrackedEntityDataValueTableInfo.Columns.VALUE} IS NOT NULL " + "ORDER BY ${EventTableInfo.Columns.EVENT_DATE} DESC LIMIT 1" + ")" diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RelationshipCount.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RelationshipCount.kt index 22333fc178..6c69d2404b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RelationshipCount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RelationshipCount.kt @@ -48,7 +48,7 @@ internal class D2RelationshipCount : ExpressionItem { val rTypeUid = ctx.uid0?.text - val queries = getQueries(visitor.programIndicatorSQLContext.programIndicator) + val queries = getQueries(visitor.programIndicatorSQLContext!!.programIndicator) return "(SELECT COUNT(*) " + "FROM ${RelationshipItemTableInfo.TABLE_INFO.name()} $riAlias " + diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramCountFunction.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramCountFunction.kt index ba6f300912..46ced048bc 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramCountFunction.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/ProgramCountFunction.kt @@ -46,7 +46,7 @@ internal abstract class ProgramCountFunction : ProgramExpressionItem() { val dataElement = ctx.uid1.text var count = 0 - val stageEvents = visitor.programIndicatorContext.events[programStage] + val stageEvents = visitor.programIndicatorContext!!.events[programStage] stageEvents?.forEach { event -> event.trackedEntityDataValues()?.forEach { dataValue -> @@ -64,7 +64,7 @@ internal abstract class ProgramCountFunction : ProgramExpressionItem() { val programStageId = ctx.uid0.text val dataElementId = ctx.uid1.text - val dataElement = visitor.dataElementStore.selectByUid(dataElementId) + val dataElement = visitor.dataElementStore!!.selectByUid(dataElementId) ?: throw IllegalArgumentException("DataElement $dataElementId does not exist.") val valueCastExpression = getColumnValueCast( @@ -80,7 +80,7 @@ internal abstract class ProgramCountFunction : ProgramExpressionItem() { "ON ${TrackedEntityDataValueTableInfo.Columns.EVENT} = ${EventTableInfo.Columns.UID} " + "WHERE ${TrackedEntityDataValueTableInfo.Columns.DATA_ELEMENT} = '$dataElementId' " + "AND ${EventTableInfo.Columns.PROGRAM_STAGE} = '$programStageId' " + - "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext.programIndicator)} " + + "AND ${getDataValueEventWhereClause(visitor.programIndicatorSQLContext!!.programIndicator)} " + "AND ${TrackedEntityDataValueTableInfo.Columns.VALUE} IS NOT NULL " + "AND $valueCastExpression $conditionalSql " + ")" diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VAnalyticsEndDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VAnalyticsEndDate.kt index 1aff82d1c2..c6b640b2ea 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VAnalyticsEndDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VAnalyticsEndDate.kt @@ -35,7 +35,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VAnalyticsEndDate : ExpressionItem { override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val startDate = visitor.programIndicatorSQLContext.periods + val startDate = visitor.programIndicatorSQLContext!!.periods ?.mapNotNull { it.endDate() } ?.maxByOrNull { it.time } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VAnalyticsStartDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VAnalyticsStartDate.kt index d87a8cab57..5b7b47eae5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VAnalyticsStartDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VAnalyticsStartDate.kt @@ -35,7 +35,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VAnalyticsStartDate : ExpressionItem { override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val startDate = visitor.programIndicatorSQLContext.periods + val startDate = visitor.programIndicatorSQLContext!!.periods ?.mapNotNull { it.startDate() } ?.minByOrNull { it.time } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCompletedDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCompletedDate.kt index 8e7f6837e8..aa590891cc 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCompletedDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCompletedDate.kt @@ -39,7 +39,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VCompletedDate : ProgramExpressionItem() { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { - val enrollment = visitor.programIndicatorContext.enrollment + val enrollment = visitor.programIndicatorContext!!.enrollment return if (enrollment == null) { getLatestEvent(visitor)?.let { ParserUtils.getMediumDateString(it.completedDate()) } @@ -49,7 +49,7 @@ internal class VCompletedDate : ProgramExpressionItem() { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EnrollmentTableInfo.Columns.COMPLETED_DATE}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCreationDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCreationDate.kt index f2726af316..d333518589 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCreationDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCreationDate.kt @@ -39,7 +39,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VCreationDate : ProgramExpressionItem() { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { - val enrollment = visitor.programIndicatorContext.enrollment + val enrollment = visitor.programIndicatorContext!!.enrollment return if (enrollment == null) { getLatestEvent(visitor)?.let { ParserUtils.getMediumDateString(it.created()) } @@ -49,7 +49,7 @@ internal class VCreationDate : ProgramExpressionItem() { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EnrollmentTableInfo.Columns.CREATED}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCurrentDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCurrentDate.kt index 78e9c7d2e1..888e5053bc 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCurrentDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VCurrentDate.kt @@ -35,7 +35,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VCurrentDate : ExpressionItem { - override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { return ParserUtils.getMediumDateString(Date()) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VDueDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VDueDate.kt index 6810679ee7..61ebfc63ea 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VDueDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VDueDate.kt @@ -43,7 +43,7 @@ internal class VDueDate : ProgramExpressionItem() { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EventTableInfo.Columns.DUE_DATE}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentCount.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentCount.kt index 9167cd885d..f3746595ad 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentCount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentCount.kt @@ -39,13 +39,13 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VEnrollmentCount : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val enrollment = visitor.programIndicatorContext.enrollment + val enrollment = visitor.programIndicatorContext!!.enrollment return if (enrollment == null) 0.toString() else 1.toString() } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val enrollmentSelector = when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + val enrollmentSelector = when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EventTableInfo.Columns.ENROLLMENT}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentDate.kt index 49215cec93..64d26536b9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentDate.kt @@ -39,13 +39,13 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VEnrollmentDate : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { - return visitor.programIndicatorContext.enrollment?.let { + return visitor.programIndicatorContext!!.enrollment?.let { ParserUtils.getMediumDateString(it.enrollmentDate()) } } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> ProgramIndicatorSQLUtils.getEnrollmentColumnForEventWhereClause( column = EnrollmentTableInfo.Columns.ENROLLMENT_DATE diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentStatus.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentStatus.kt index 3be3e8a5c9..25c24a5b96 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentStatus.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEnrollmentStatus.kt @@ -38,11 +38,11 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VEnrollmentStatus : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { - return visitor.programIndicatorContext.enrollment?.status()?.name + return visitor.programIndicatorContext!!.enrollment?.status()?.name } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> ProgramIndicatorSQLUtils.getEnrollmentColumnForEventWhereClause( column = EnrollmentTableInfo.Columns.STATUS diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventCount.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventCount.kt index 300c7715ff..efbdc1a79a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventCount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventCount.kt @@ -39,11 +39,11 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VEventCount : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return visitor.programIndicatorContext.events.values.sumOf { it.size }.toString() + return visitor.programIndicatorContext!!.events.values.sumOf { it.size }.toString() } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val eventSelector = when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + val eventSelector = when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EventTableInfo.Columns.UID}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventDate.kt index 27235e9d89..6e54215b02 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventDate.kt @@ -43,7 +43,7 @@ internal class VEventDate : ProgramExpressionItem() { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EventTableInfo.Columns.EVENT_DATE}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventStatus.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventStatus.kt index 1bd950a16e..ece61c7c33 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventStatus.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VEventStatus.kt @@ -42,7 +42,7 @@ internal class VEventStatus : ProgramExpressionItem() { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EventTableInfo.Columns.STATUS}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VIncidentDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VIncidentDate.kt index f98fa03af2..eebb200d2a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VIncidentDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VIncidentDate.kt @@ -39,13 +39,13 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VIncidentDate : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { - return visitor.programIndicatorContext.enrollment?.let { + return visitor.programIndicatorContext!!.enrollment?.let { ParserUtils.getMediumDateString(it.incidentDate()) } } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> ProgramIndicatorSQLUtils.getEnrollmentColumnForEventWhereClause( column = EnrollmentTableInfo.Columns.INCIDENT_DATE diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VOrgUnitCount.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VOrgUnitCount.kt index f90fd810ad..d98a13cdb8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VOrgUnitCount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VOrgUnitCount.kt @@ -39,7 +39,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VOrgUnitCount : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val orgUnits = when (visitor.programIndicatorContext.programIndicator.analyticsType()) { + val orgUnits = when (visitor.programIndicatorContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> visitor.programIndicatorContext.events.values.flatMap { it.map { it.organisationUnit() } } AnalyticsType.ENROLLMENT -> @@ -50,7 +50,7 @@ internal class VOrgUnitCount : ExpressionItem { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val eventSelector = when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + val eventSelector = when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EventTableInfo.Columns.ORGANISATION_UNIT}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VProgramStageId.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VProgramStageId.kt index bf228c442c..4dc4267882 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VProgramStageId.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VProgramStageId.kt @@ -41,7 +41,7 @@ internal class VProgramStageId : ProgramExpressionItem() { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "${ProgramIndicatorSQLUtils.event}.${EventTableInfo.Columns.PROGRAM_STAGE}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VProgramStageName.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VProgramStageName.kt index 459cf458f2..f3a0378dea 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VProgramStageName.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VProgramStageName.kt @@ -40,12 +40,12 @@ internal class VProgramStageName : ProgramExpressionItem() { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { return getLatestEvent(visitor)?.programStage()?.let { - visitor.programStageStore.selectByUid(it)?.name() + visitor.programStageStore!!.selectByUid(it)?.name() } } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "(SELECT ${ProgramStageTableInfo.Columns.NAME} " + "FROM ${ProgramStageTableInfo.TABLE_INFO.name()} ps " + diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VSyncDate.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VSyncDate.kt index bf7de775e4..ca0e33c6c3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VSyncDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VSyncDate.kt @@ -39,7 +39,7 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VSyncDate : ProgramExpressionItem() { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { - val enrollment = visitor.programIndicatorContext.enrollment + val enrollment = visitor.programIndicatorContext!!.enrollment return if (enrollment == null) { getLatestEvent(visitor)?.let { ParserUtils.getMediumDateString(it.lastUpdated()) } @@ -49,7 +49,7 @@ internal class VSyncDate : ProgramExpressionItem() { } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - return when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + return when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> "$event.${EnrollmentTableInfo.Columns.LAST_UPDATED}" AnalyticsType.ENROLLMENT, null -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VTeiCount.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VTeiCount.kt index 90ea38f89b..b1db9770cf 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VTeiCount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VTeiCount.kt @@ -38,13 +38,13 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VTeiCount : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val count = if (visitor.programIndicatorContext.enrollment == null) 0 else 1 + val count = if (visitor.programIndicatorContext!!.enrollment == null) 0 else 1 return count.toString() } override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val teiSelector = when (visitor.programIndicatorSQLContext.programIndicator.analyticsType()) { + val teiSelector = when (visitor.programIndicatorSQLContext!!.programIndicator.analyticsType()) { AnalyticsType.EVENT -> ProgramIndicatorSQLUtils.getEnrollmentColumnForEventWhereClause( column = EnrollmentTableInfo.Columns.TRACKED_ENTITY_INSTANCE diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VValueCount.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VValueCount.kt index 9cc1bee81c..b5df9718d5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VValueCount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VValueCount.kt @@ -35,10 +35,10 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VValueCount : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val expression = visitor.programIndicatorContext.programIndicator.expression() + val expression = visitor.programIndicatorContext!!.programIndicator.expression() ?: throw IllegalArgumentException("Expression is null") - return visitor.programIndicatorExecutor.getValueCount(expression).toString() + return visitor.programIndicatorExecutor!!.getValueCount(expression).toString() } override fun count(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { @@ -48,7 +48,7 @@ internal class VValueCount : ExpressionItem { override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { return ProgramIndicatorSQLUtils.valueCountExpression( itemIds = visitor.itemIds, - programIndicator = visitor.programIndicatorSQLContext.programIndicator + programIndicator = visitor.programIndicatorSQLContext!!.programIndicator ) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VZeroPosValueCount.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VZeroPosValueCount.kt index 1fe9a96554..f48fd9e587 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VZeroPosValueCount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VZeroPosValueCount.kt @@ -35,10 +35,10 @@ import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext internal class VZeroPosValueCount : ExpressionItem { override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { - val expression = visitor.programIndicatorContext.programIndicator.expression() + val expression = visitor.programIndicatorContext!!.programIndicator.expression() ?: throw IllegalArgumentException("Expression is null") - return visitor.programIndicatorExecutor.getZeroPosValueCount(expression).toString() + return visitor.programIndicatorExecutor!!.getZeroPosValueCount(expression).toString() } override fun count(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { @@ -48,7 +48,7 @@ internal class VZeroPosValueCount : ExpressionItem { override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { return ProgramIndicatorSQLUtils.valueCountExpression( itemIds = visitor.itemIds, - programIndicator = visitor.programIndicatorSQLContext.programIndicator, + programIndicator = visitor.programIndicatorSQLContext!!.programIndicator, conditionalValueExpression = " >= 0" ) } 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 a090033f96..5d7d803617 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 @@ -136,6 +136,6 @@ internal class AnalyticsSettingEntityDIModule { @Reusable fun teiChildrenAppenders(dataChildrenAppender: AnalyticsTeiDataChildrenAppender): Map> { - return mapOf(AnalyticsTeiDataChildrenAppender.KEY to dataChildrenAppender) - } + return mapOf(AnalyticsTeiDataChildrenAppender.KEY to dataChildrenAppender) + } } 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 index 27c2b263d3..5f589c9e88 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/DataDimensionItemEntityDIModule.kt @@ -49,6 +49,6 @@ internal class DataDimensionItemEntityDIModule { @Reusable fun handler(store: LinkStore): LinkHandler { - return LinkHandlerImpl(store) - } + return LinkHandlerImpl(store) + } } 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 index ccca796658..875eca5db9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/internal/VisualizationCategoryDimensionEntityDIModule.kt @@ -49,6 +49,6 @@ internal class VisualizationCategoryDimensionEntityDIModule { @Reusable fun handler(store: LinkStore): LinkHandler { - return LinkHandlerImpl(store) - } + return LinkHandlerImpl(store) + } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.java b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.java deleted file mode 100644 index 699ca9bea1..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.java +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.parser.internal.expression; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -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.category.internal.CategoryOptionComboStore; -import org.hisp.dhis.android.core.constant.Constant; -import org.hisp.dhis.android.core.dataelement.DataElement; -import org.hisp.dhis.android.core.dataelement.DataElementOperand; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup; -import org.hisp.dhis.android.core.parser.internal.service.ExpressionService; -import org.hisp.dhis.android.core.parser.internal.service.dataobject.DataElementObject; -import org.hisp.dhis.android.core.parser.internal.service.dataobject.DataElementOperandObject; -import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject; -import org.hisp.dhis.android.core.program.ProgramStage; -import org.hisp.dhis.android.core.validation.MissingValueStrategy; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class ExpressionServiceShould { - - private String dataElementId1 = "sK2wroysTNW"; - private String dataElementId2 = "lZGmxYbs97q"; - - private String categoryOptionComboId1 = "tpghB93ks57"; - private String categoryOptionComboId2 = "zDhUuAYrxNC"; - - private String constantId = "e19hj1w7yKP"; - - private String orgunitGroupId = "RAL7YE4KJ58"; - - private String days = "[days]"; - - @Mock - IdentifiableObjectStore dataElementStore; - - @Mock - DataElement dataElement1, dataElement2; - - @Mock - CategoryOptionComboStore categoryOptionComboStore; - - @Mock - CategoryOptionCombo categoryOptionCombo1, categoryOptionCombo2; - - @Mock - IdentifiableObjectStore organisationUnitGroupStore; - - @Mock - IdentifiableObjectStore programStageStore; - - @Mock - OrganisationUnitGroup organisationUnitGroup; - - @Mock - Constant constant; - - private Map constantMap; - - private ExpressionService service; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - service = new ExpressionService( - dataElementStore, - categoryOptionComboStore, - organisationUnitGroupStore, - programStageStore); - - when(dataElementStore.selectByUid(dataElementId1)).thenReturn(dataElement1); - when(dataElementStore.selectByUid(dataElementId2)).thenReturn(dataElement2); - - when(categoryOptionComboStore.selectByUid(categoryOptionComboId1)).thenReturn(categoryOptionCombo1); - when(categoryOptionComboStore.selectByUid(categoryOptionComboId2)).thenReturn(categoryOptionCombo2); - - when(organisationUnitGroupStore.selectByUid(orgunitGroupId)).thenReturn(organisationUnitGroup); - - constantMap = Collections.singletonMap(constantId, constant); - } - - @Test - public void evaluate_dataelements() { - String expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + deOperand(dataElementId2, categoryOptionComboId2); - - Map valueMap = new HashMap<>(); - valueMap.put(DataElementOperandObject.create(dataElementId1, categoryOptionComboId1), 5.0); - valueMap.put(DataElementOperandObject.create(dataElementId2, categoryOptionComboId2), 3.0); - - Double result = (Double) service.getExpressionValue(expression, valueMap, constantMap, - Collections.emptyMap(), 10, MissingValueStrategy.NEVER_SKIP); - assertThat(result).isEqualTo(8.0); - } - - @Test - public void evaluate_constants() { - String expression = de(dataElementId1) + " + " + constant(constantId); - - Map valueMap = new HashMap<>(); - valueMap.put(DataElementOperandObject.create(dataElementId1, null), 5.0); - - when(constant.value()).thenReturn(4.0); - - Double result = (Double) service.getExpressionValue(expression, valueMap, constantMap, Collections.emptyMap(), - 10, MissingValueStrategy.NEVER_SKIP); - assertThat(result).isEqualTo(9.0); - } - - @Test - public void evaluate_without_coc() { - String expression = de(dataElementId1) + " + " + de(dataElementId2); - - Map valueMap = new HashMap<>(); - valueMap.put(DataElementOperandObject.create(dataElementId1, null), 5.0); - valueMap.put(DataElementOperandObject.create(dataElementId2, null), 3.0); - - Double result = (Double) service.getExpressionValue(expression, valueMap, constantMap, - Collections.emptyMap(), 10, MissingValueStrategy.NEVER_SKIP); - assertThat(result).isEqualTo(8.0); - } - - @Test - public void evaluate_days() { - String expression = de(dataElementId1) + " + " + days; - - Map valueMap = new HashMap<>(); - valueMap.put(DataElementOperandObject.create(dataElementId1, null), 5.0); - - Double result = (Double) service.getExpressionValue(expression, valueMap, constantMap, - Collections.emptyMap(), 10, MissingValueStrategy.NEVER_SKIP); - assertThat(result).isEqualTo(15.0); - } - - @Test - public void evaluate_orgunit_groups() { - String expression = de(dataElementId1) + " + " + oug(orgunitGroupId); - - Map valueMap = new HashMap<>(); - valueMap.put(DataElementOperandObject.create(dataElementId1, null), 5.0); - - Map orgunitMap = new HashMap<>(); - orgunitMap.put(orgunitGroupId, 20); - - Double result = (Double) service.getExpressionValue(expression, valueMap, constantMap, - orgunitMap, 10, MissingValueStrategy.NEVER_SKIP); - assertThat(result).isEqualTo(25.0); - } - - @Test - public void evaluate_missing_strategies_with_some_missing_values() { - String expression = de(dataElementId1) + " + " + de(dataElementId2); - - Map valueMap = new HashMap<>(); - valueMap.put(DataElementOperandObject.create(dataElementId1, null), 5.0); - - Double resultNeverSkip = (Double) service.getExpressionValue(expression, valueMap, - constantMap, Collections.emptyMap(), 10, MissingValueStrategy.NEVER_SKIP); - assertThat(resultNeverSkip).isEqualTo(5.0); - - Double resultSkipIfAny = (Double) service.getExpressionValue(expression, valueMap, - constantMap, Collections.emptyMap(), 10, MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING); - assertThat(resultSkipIfAny).isNull(); - - Double resultSkipIfAll = (Double) service.getExpressionValue(expression, valueMap, - constantMap, Collections.emptyMap(), 10, MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING); - assertThat(resultSkipIfAll).isEqualTo(5.0); - } - - @Test - public void evaluate_missing_strategies_with_all_missing_values() { - String expression = de(dataElementId1) + " + " + de(dataElementId2); - - Map valueMap = Collections.emptyMap(); - - Double resultNeverSkip = (Double) service.getExpressionValue(expression, valueMap, - constantMap, Collections.emptyMap(), 10, MissingValueStrategy.NEVER_SKIP); - assertThat(resultNeverSkip).isEqualTo(0.0); - - Double resultSkipIfAny = (Double) service.getExpressionValue(expression, valueMap, - constantMap, Collections.emptyMap(), 10, MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING); - assertThat(resultSkipIfAny).isNull(); - - Double resultSkipIfAll = (Double) service.getExpressionValue(expression, valueMap, - constantMap, Collections.emptyMap(), 10, MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING); - assertThat(resultSkipIfAll).isNull(); - } - - @Test - public void evaluate_null_expression() { - assertThat(service.getExpressionValue(null)).isNull(); - assertThat(service.getExpressionDescription(null, Collections.emptyMap())).isEqualTo(""); - assertThat(service.getDataElementOperands(null)).isEmpty(); - assertThat(service.regenerateExpression(null, Collections.emptyMap(), constantMap, - Collections.emptyMap(), 10)).isEqualTo(""); - } - - @Test - public void evaluate_number_comparison() { - assertThat((Boolean) service.getExpressionValue("5.0 < 8.0")).isTrue(); - assertThat((Boolean) service.getExpressionValue("5.0 < 5.0")).isFalse(); - - assertThat((Boolean) service.getExpressionValue("5.0 <= 8.0")).isTrue(); - assertThat((Boolean) service.getExpressionValue("5.0 <= 5.0")).isTrue(); - - assertThat((Boolean) service.getExpressionValue("5.0 == 8.0")).isFalse(); - assertThat((Boolean) service.getExpressionValue("5.0 == 5.0")).isTrue(); - - assertThat((Boolean) service.getExpressionValue("5.0 != 8.0")).isTrue(); - assertThat((Boolean) service.getExpressionValue("5.0 != 5.0")).isFalse(); - } - - @Test - public void evaluate_logical_operators() { - assertThat((Boolean) service.getExpressionValue("true && true")).isTrue(); - assertThat((Boolean) service.getExpressionValue("true and true")).isTrue(); - - assertThat((Boolean) service.getExpressionValue("true || false")).isTrue(); - assertThat((Boolean) service.getExpressionValue("true or false")).isTrue(); - - assertThat((Boolean) service.getExpressionValue("5.0 == 8.0 && 4.0 == 4.0")).isFalse(); - assertThat((Boolean) service.getExpressionValue("5.0 == 5.0 && 4.0 == 4.0")).isTrue(); - - assertThat((Boolean) service.getExpressionValue("5.0 != 8.0 || 5.0 == 8.0")).isTrue(); - assertThat((Boolean) service.getExpressionValue("5.0 != 5.0 || 8.0 != 8.0")).isFalse(); - } - - @Test - public void evaluate_functions() { - assertThat(service.getExpressionValue("firstNonNull(4 , 'two', 6)")).isEqualTo(4.0); - assertThat(service.getExpressionValue("firstNonNull('two' , 4, 6)")).isEqualTo("two"); - - assertThat(service.getExpressionValue("greatest(5, 2, 7, 3)")).isEqualTo(7.0); - assertThat(service.getExpressionValue("greatest(-5, -2, -7)")).isEqualTo(-2.0); - - assertThat(service.getExpressionValue("if(5 > 2, 5, 2)")).isEqualTo(5.0); - assertThat(service.getExpressionValue("if(5 < 2, 5, 2)")).isEqualTo(2.0); - - assertThat(service.getExpressionValue("isNotNull(5)")).isEqualTo(true); - assertThat(service.getExpressionValue("isNull(5)")).isEqualTo(false); - - assertThat(service.getExpressionValue("least(5, 2, 7, 3)")).isEqualTo(2.0); - assertThat(service.getExpressionValue("least(-5, -2, -7)")).isEqualTo(-7.0); - - assertThat((double) service.getExpressionValue("log(100)")).isAtLeast(4.6); - assertThat((double) service.getExpressionValue("log(100)")).isAtMost(4.7); - assertThat(service.getExpressionValue("log10(100)")).isEqualTo(2.0); - } - - @Test - public void evaluate_divide_by_zero() { - assertThat(service.getExpressionValue("4 / 0")).isEqualTo(null); - } - - @Test - public void get_dataelement_ids() { - String expression = de(dataElementId1) + " + " + de(dataElementId2); - Set dataElementOperands = service.getDataElementOperands(expression); - - assertThat(dataElementOperands.size()).isEqualTo(2); - for (DataElementOperand deo : dataElementOperands) { - if (!deo.dataElement().uid().equals(dataElementId1) && !deo.dataElement().uid().equals(dataElementId2)) { - throw new RuntimeException("Should not reach this point"); - } - assertThat(deo.categoryOptionCombo()).isNull(); - } - } - - @Test - public void get_dataelement_operands_ids() { - String expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + deOperand(dataElementId2, categoryOptionComboId2); - Set dataElementOperands = service.getDataElementOperands(expression); - - assertThat(dataElementOperands.size()).isEqualTo(2); - for (DataElementOperand deo : dataElementOperands) { - if (deo.dataElement().uid().equals(dataElementId1)) { - assertThat(deo.categoryOptionCombo().uid()).isEqualTo(categoryOptionComboId1); - } else if (deo.dataElement().uid().equals(dataElementId2)) { - assertThat(deo.categoryOptionCombo().uid()).isEqualTo(categoryOptionComboId2); - } else { - throw new RuntimeException("Should not reach this point"); - } - } - } - - @Test - public void get_dataelement_ids_in_empty_expression() { - String expression = days + " + " + constant(constantId) + " + " + oug(constantId); - Set dataElementOperands = service.getDataElementOperands(expression); - - assertThat(dataElementOperands).isEmpty(); - } - - @Test - public void get_description_when_all_items_exist() { - when(dataElement1.displayName()).thenReturn("Data Element 1"); - when(dataElement2.displayName()).thenReturn("Data Element 2"); - when(categoryOptionCombo1.displayName()).thenReturn("COC 1"); - when(organisationUnitGroup.displayName()).thenReturn("Org Unit Group"); - when(constant.displayName()).thenReturn("Constant"); - - String expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + - de(dataElementId2) + " * " + - oug(orgunitGroupId) + " + " + - constant(constantId); - - String description = service.getExpressionDescription(expression, constantMap); - - assertThat(description).isEqualTo("Data Element 1 (COC 1) + Data Element 2 * Org Unit Group + Constant"); - } - - @Test - public void get_description_with_missing_items() { - when(dataElement1.displayName()).thenReturn("Data Element 1"); - - String expression = de(dataElementId1) + " + " + de("atGmxEbs97n"); - - String description = service.getExpressionDescription(expression, Collections.emptyMap()); - - assertThat(description).isEqualTo("Data Element 1 + " + de("atGmxEbs97n")); - } - - @Test - public void regenerate_expression() { - String expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + - de(dataElementId2) + " / " + - constant(constantId) + " * " + - oug(orgunitGroupId) + " - " + - days; - - Map valueMap = new HashMap<>(); - valueMap.put(DataElementOperandObject.create(dataElementId1, categoryOptionComboId1), 5.0); - valueMap.put(DataElementObject.create(dataElementId2), 3.0); - when(constant.value()).thenReturn(3.14); - - Map orgunitMap = new HashMap<>(); - orgunitMap.put(orgunitGroupId, 20); - - Object regeneratedExpression = service.regenerateExpression(expression, valueMap, constantMap, - orgunitMap, 10); - - assertThat(regeneratedExpression).isEqualTo("5.0 + 3.0 / 3.14 * 20 - 10.0"); - } - - @Test - public void regenerate_expression_with_missing_items() { - String expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + de(dataElementId2); - - Map valueMap = new HashMap<>(); - valueMap.put(DataElementOperandObject.create(dataElementId1, categoryOptionComboId1), 5.0); - - Object regeneratedExpression = service.regenerateExpression(expression, valueMap, constantMap, - Collections.emptyMap(), 10); - - assertThat(regeneratedExpression).isEqualTo("5.0 + " + de(dataElementId2)); - } - - private String constant(String uid) { - return "C{" + uid + "}"; - } - - private String de(String uid) { - return "#{" + uid + "}"; - } - - private String deOperand(String de, String coc) { - return "#{" + de + "." + coc + "}"; - } - - private String oug(String uid) { - return "OUG{" + uid + "}"; - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt new file mode 100644 index 0000000000..5e3f048fe2 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +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.category.internal.CategoryOptionComboStore +import org.hisp.dhis.android.core.constant.Constant +import org.hisp.dhis.android.core.dataelement.DataElement +import org.hisp.dhis.android.core.dataelement.DataElementOperand +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup +import org.hisp.dhis.android.core.parser.internal.service.ExpressionService +import org.hisp.dhis.android.core.parser.internal.service.dataobject.DataElementObject +import org.hisp.dhis.android.core.parser.internal.service.dataobject.DataElementOperandObject +import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject +import org.hisp.dhis.android.core.program.ProgramStage +import org.hisp.dhis.android.core.validation.MissingValueStrategy +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test + +class ExpressionServiceShould { + private val dataElementId1 = "sK2wroysTNW" + private val dataElementId2 = "lZGmxYbs97q" + private val categoryOptionComboId1 = "tpghB93ks57" + private val categoryOptionComboId2 = "zDhUuAYrxNC" + private val constantId = "e19hj1w7yKP" + private val orgunitGroupId = "RAL7YE4KJ58" + private val days = "[days]" + + private val dataElementStore: IdentifiableObjectStore = mock() + private val dataElement1: DataElement = mock() + private val dataElement2: DataElement = mock() + + private val categoryOptionComboStore: CategoryOptionComboStore = mock() + private val categoryOptionCombo1: CategoryOptionCombo = mock() + private val categoryOptionCombo2: CategoryOptionCombo = mock() + + private val organisationUnitGroupStore: IdentifiableObjectStore = mock() + private val organisationUnitGroup: OrganisationUnitGroup = mock() + + private val programStageStore: IdentifiableObjectStore = mock() + + private val constant: Constant = mock() + + private lateinit var constantMap: Map + private lateinit var service: ExpressionService + + @Before + fun setUp() { + service = ExpressionService( + dataElementStore, + categoryOptionComboStore, + organisationUnitGroupStore, + programStageStore + ) + constantMap = mapOf(constantId to constant) + + whenever(dataElementStore.selectByUid(dataElementId1)).thenReturn(dataElement1) + whenever(dataElementStore.selectByUid(dataElementId2)).thenReturn(dataElement2) + whenever(categoryOptionComboStore.selectByUid(categoryOptionComboId1)).thenReturn(categoryOptionCombo1) + whenever(categoryOptionComboStore.selectByUid(categoryOptionComboId2)).thenReturn(categoryOptionCombo2) + whenever(organisationUnitGroupStore.selectByUid(orgunitGroupId)).thenReturn(organisationUnitGroup) + } + + @Test + fun evaluate_dataelements() { + val expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + deOperand( + dataElementId2, + categoryOptionComboId2 + ) + val valueMap: Map = mapOf( + DataElementOperandObject(dataElementId1, categoryOptionComboId1) to 5.0, + DataElementOperandObject(dataElementId2, categoryOptionComboId2) to 3.0 + ) + + val result = service.getExpressionValue( + expression, + valueMap, + constantMap, + emptyMap(), + 10, + MissingValueStrategy.NEVER_SKIP + ) as Double? + + assertThat(result).isEqualTo(8.0) + } + + @Test + fun evaluate_constants() { + val expression = de(dataElementId1) + " + " + constant(constantId) + val valueMap: Map = mapOf( + DataElementOperandObject(dataElementId1, null) to 5.0 + ) + whenever(constant.value()).thenReturn(4.0) + + val result = service.getExpressionValue( + expression, valueMap, constantMap, emptyMap(), + 10, MissingValueStrategy.NEVER_SKIP + ) as Double? + + assertThat(result).isEqualTo(9.0) + } + + @Test + fun evaluate_without_coc() { + val expression = de(dataElementId1) + " + " + de(dataElementId2) + val valueMap: Map = mapOf( + DataElementOperandObject(dataElementId1, null) to 5.0, + DataElementOperandObject(dataElementId2, null) to 3.0 + ) + val result = service.getExpressionValue( + expression, + valueMap, + constantMap, + emptyMap(), + 10, + MissingValueStrategy.NEVER_SKIP + ) as Double? + + assertThat(result).isEqualTo(8.0) + } + + @Test + fun evaluate_days() { + val expression = de(dataElementId1) + " + " + days + val valueMap: Map = mapOf( + DataElementOperandObject(dataElementId1, null) to 5.0 + ) + val result = service.getExpressionValue( + expression, + valueMap, + constantMap, + emptyMap(), + 10, + MissingValueStrategy.NEVER_SKIP + ) as Double? + + assertThat(result).isEqualTo(15.0) + } + + @Test + fun evaluate_orgunit_groups() { + val expression = de(dataElementId1) + " + " + oug(orgunitGroupId) + val valueMap: Map = mapOf( + DataElementOperandObject(dataElementId1, null) to 5.0 + ) + val orgunitMap: Map = mapOf( + orgunitGroupId to 20 + ) + val result = service.getExpressionValue( + expression, + valueMap, + constantMap, + orgunitMap, + 10, + MissingValueStrategy.NEVER_SKIP + ) as Double? + + assertThat(result).isEqualTo(25.0) + } + + @Test + fun evaluate_missing_strategies_with_some_missing_values() { + val expression = de(dataElementId1) + " + " + de(dataElementId2) + val valueMap: Map = mapOf( + DataElementOperandObject(dataElementId1, null) to 5.0 + ) + + mapOf( + MissingValueStrategy.NEVER_SKIP to 5.0, + MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING to null, + MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING to 5.0 + ).forEach { (strategy, expected) -> + val result = service.getExpressionValue( + expression, valueMap, + constantMap, emptyMap(), 10, strategy + ) as Double? + + assertThat(result).isEqualTo(expected) + } + } + + @Test + fun evaluate_missing_strategies_with_all_missing_values() { + val expression = de(dataElementId1) + " + " + de(dataElementId2) + val valueMap: Map = emptyMap() + + mapOf( + MissingValueStrategy.NEVER_SKIP to 0.0, + MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING to null, + MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING to null + ).forEach { (strategy, expected) -> + val result = service.getExpressionValue( + expression, valueMap, + constantMap, emptyMap(), 10, strategy + ) as Double? + + assertThat(result).isEqualTo(expected) + } + } + + @Test + fun evaluate_null_expression() { + assertThat(service.getExpressionValue(null)).isNull() + assertThat(service.getExpressionDescription(null, emptyMap())).isEqualTo("") + assertThat(service.getDataElementOperands(null)).isEmpty() + assertThat(service.regenerateExpression(null, emptyMap(), constantMap, emptyMap(), 10)).isEqualTo("") + } + + @Test + fun evaluate_number_comparison() { + assertThat(service.getExpressionValue("5.0 < 8.0") as Boolean?).isTrue() + assertThat(service.getExpressionValue("5.0 < 5.0") as Boolean?).isFalse() + assertThat(service.getExpressionValue("5.0 <= 8.0") as Boolean?).isTrue() + assertThat(service.getExpressionValue("5.0 <= 5.0") as Boolean?).isTrue() + assertThat(service.getExpressionValue("5.0 == 8.0") as Boolean?).isFalse() + assertThat(service.getExpressionValue("5.0 == 5.0") as Boolean?).isTrue() + assertThat(service.getExpressionValue("5.0 != 8.0") as Boolean?).isTrue() + assertThat(service.getExpressionValue("5.0 != 5.0") as Boolean?).isFalse() + } + + @Test + fun evaluate_logical_operators() { + assertThat(service.getExpressionValue("true && true") as Boolean?).isTrue() + assertThat(service.getExpressionValue("true and true") as Boolean?).isTrue() + assertThat(service.getExpressionValue("true || false") as Boolean?).isTrue() + assertThat(service.getExpressionValue("true or false") as Boolean?).isTrue() + assertThat(service.getExpressionValue("5.0 == 8.0 && 4.0 == 4.0") as Boolean?).isFalse() + assertThat(service.getExpressionValue("5.0 == 5.0 && 4.0 == 4.0") as Boolean?).isTrue() + assertThat(service.getExpressionValue("5.0 != 8.0 || 5.0 == 8.0") as Boolean?).isTrue() + assertThat(service.getExpressionValue("5.0 != 5.0 || 8.0 != 8.0") as Boolean?).isFalse() + } + + @Test + fun evaluate_functions() { + assertThat(service.getExpressionValue("firstNonNull(4 , 'two', 6)")).isEqualTo(4.0) + assertThat(service.getExpressionValue("firstNonNull('two' , 4, 6)")).isEqualTo("two") + assertThat(service.getExpressionValue("greatest(5, 2, 7, 3)")).isEqualTo(7.0) + assertThat(service.getExpressionValue("greatest(-5, -2, -7)")).isEqualTo(-2.0) + assertThat(service.getExpressionValue("if(5 > 2, 5, 2)")).isEqualTo(5.0) + assertThat(service.getExpressionValue("if(5 < 2, 5, 2)")).isEqualTo(2.0) + assertThat(service.getExpressionValue("isNotNull(5)")).isEqualTo(true) + assertThat(service.getExpressionValue("isNull(5)")).isEqualTo(false) + assertThat(service.getExpressionValue("least(5, 2, 7, 3)")).isEqualTo(2.0) + assertThat(service.getExpressionValue("least(-5, -2, -7)")).isEqualTo(-7.0) + assertThat(service.getExpressionValue("log(100)") as Double).isAtLeast(4.6) + assertThat(service.getExpressionValue("log(100)") as Double).isAtMost(4.7) + assertThat(service.getExpressionValue("log10(100)")).isEqualTo(2.0) + } + + @Test + fun evaluate_divide_by_zero() { + assertThat(service.getExpressionValue("4 / 0")).isEqualTo(null) + } + + @Test + fun get_dataelement_ids() { + val expression = de(dataElementId1) + " + " + de(dataElementId2) + val dataElementOperands = service.getDataElementOperands(expression) + assertThat(dataElementOperands.size).isEqualTo(2) + + for (deo in dataElementOperands) { + if (deo.dataElement()!!.uid() != dataElementId1 && deo.dataElement()!!.uid() != dataElementId2) { + fail("Should not reach this point") + } + assertThat(deo.categoryOptionCombo()).isNull() + } + } + + @Test + fun get_dataelement_operands_ids() { + val expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + deOperand( + dataElementId2, + categoryOptionComboId2 + ) + val dataElementOperands = service.getDataElementOperands(expression) + assertThat(dataElementOperands.size).isEqualTo(2) + + for (deo in dataElementOperands) { + if (deo.dataElement()!!.uid() == dataElementId1) { + assertThat(deo.categoryOptionCombo()!!.uid()).isEqualTo(categoryOptionComboId1) + } else if (deo.dataElement()!!.uid() == dataElementId2) { + assertThat(deo.categoryOptionCombo()!!.uid()).isEqualTo(categoryOptionComboId2) + } else { + fail("Should not reach this point") + } + } + } + + @Test + fun get_dataelement_ids_in_empty_expression() { + val expression = days + " + " + constant(constantId) + " + " + oug(constantId) + val dataElementOperands: Set = service.getDataElementOperands(expression) + assertThat(dataElementOperands).isEmpty() + } + + @Test + fun get_description_when_all_items_exist() { + whenever(dataElement1.displayName()).thenReturn("Data Element 1") + whenever(dataElement2.displayName()).thenReturn("Data Element 2") + whenever(categoryOptionCombo1.displayName()).thenReturn("COC 1") + whenever(organisationUnitGroup.displayName()).thenReturn("Org Unit Group") + whenever(constant.displayName()).thenReturn("Constant") + + val expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + + de(dataElementId2) + " * " + + oug(orgunitGroupId) + " + " + + constant(constantId) + val description = service.getExpressionDescription(expression, constantMap) + + assertThat(description).isEqualTo("Data Element 1 (COC 1) + Data Element 2 * Org Unit Group + Constant") + } + + @Test + fun get_description_with_missing_items() { + whenever(dataElement1.displayName()).thenReturn("Data Element 1") + + val expression = de(dataElementId1) + " + " + de("atGmxEbs97n") + val description = service.getExpressionDescription(expression, emptyMap()) + + assertThat(description).isEqualTo("Data Element 1 + " + de("atGmxEbs97n")) + } + + @Test + fun regenerate_expression() { + val expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + + de(dataElementId2) + " / " + + constant(constantId) + " * " + + oug(orgunitGroupId) + " - " + + days + val valueMap: Map = mapOf( + DataElementOperandObject(dataElementId1, categoryOptionComboId1) to 5.0, + DataElementObject(dataElementId2) to 3.0 + ) + val orgunitMap: Map = mapOf( + orgunitGroupId to 20 + ) + whenever(constant.value()).thenReturn(3.14) + + val regeneratedExpression: Any = service.regenerateExpression( + expression, valueMap, constantMap, + orgunitMap, 10 + ) + + assertThat(regeneratedExpression).isEqualTo("5.0 + 3.0 / 3.14 * 20 - 10.0") + } + + @Test + fun regenerate_expression_with_missing_items() { + val expression = deOperand(dataElementId1, categoryOptionComboId1) + " + " + de(dataElementId2) + val valueMap: Map = mapOf( + DataElementOperandObject(dataElementId1, categoryOptionComboId1) to 5.0 + ) + + val regeneratedExpression: Any = + service.regenerateExpression(expression, valueMap, constantMap, emptyMap(), 10) + + assertThat(regeneratedExpression).isEqualTo("5.0 + " + de(dataElementId2)) + } + + private fun constant(uid: String): String { + return "C{$uid}" + } + + private fun de(uid: String): String { + return "#{$uid}" + } + + private fun deOperand(de: String, coc: String): String { + return "#{$de.$coc}" + } + + private fun oug(uid: String): String { + return "OUG{$uid}" + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelperShould.kt index e31bf04eb4..c97759a2f8 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelperShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelperShould.kt @@ -113,8 +113,8 @@ class ExpressionHelperShould { value: Double ) { val key = - if (categoryOptionComboId == null) DataElementObject.create(dataElementId) - else DataElementOperandObject.create(dataElementId, categoryOptionComboId) + if (categoryOptionComboId == null) DataElementObject(dataElementId) + else DataElementOperandObject(dataElementId, categoryOptionComboId) val entry = valueMap.entries.find { it.key == key } @@ -128,8 +128,8 @@ class ExpressionHelperShould { categoryOptionComboId: String? ) { val key = - if (categoryOptionComboId == null) DataElementObject.create(dataElementId) - else DataElementOperandObject.create(dataElementId, categoryOptionComboId) + if (categoryOptionComboId == null) DataElementObject(dataElementId) + else DataElementOperandObject(dataElementId, categoryOptionComboId) valueMap.entries.none { it.key == key } } From 2abd2fbcd4bb764ab6998100d451a6d5406de386 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 19 Dec 2022 09:37:01 +0100 Subject: [PATCH 015/201] [ANDROSDK-1613] Add confidential to TrackedEntityAttribute --- .../android/core/trackedentity/TrackedEntityAttribute.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttribute.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttribute.java index 4d2d5d1598..2c8fd16445 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttribute.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttribute.java @@ -105,6 +105,10 @@ public abstract class TrackedEntityAttribute extends BaseNameableObject @JsonProperty() public abstract Boolean displayOnVisitSchedule(); + @Nullable + @JsonProperty() + public abstract Boolean confidential(); + @Nullable @JsonProperty(TrackedEntityAttributeFields.ORG_UNIT_SCOPE) @ColumnName(TrackedEntityAttributeFields.ORG_UNIT_SCOPE) @@ -177,6 +181,8 @@ public abstract static class Builder extends BaseNameableObject.Builder public abstract Builder displayOnVisitSchedule(Boolean displayOnVisitSchedule); + public abstract Builder confidential(Boolean confidential); + @JsonProperty(TrackedEntityAttributeFields.ORG_UNIT_SCOPE) public abstract Builder orgUnitScope(Boolean orgUnitScope); From 98b1193c00ff03038eb6df41b2e3cd7ec8fa5075 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 19 Dec 2022 09:37:32 +0100 Subject: [PATCH 016/201] [ANDROSDK-1613] Add confidential to TrackedEntityAttribute fields, table info and store --- .../core/trackedentity/TrackedEntityAttributeTableInfo.java | 4 +++- .../trackedentity/internal/TrackedEntityAttributeFields.java | 3 ++- .../trackedentity/internal/TrackedEntityAttributeStore.java | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeTableInfo.java index 9359c86430..900e3322e9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeTableInfo.java @@ -67,6 +67,7 @@ public static class Columns extends NameableWithStyleColumns { public static final String FIELD_MASK = "fieldMask"; public static final String FORM_NAME = "formName"; public static final String DISPLAY_FORM_NAME = "displayFormName"; + public static final String CONFIDENTIAL = "confidential"; @Override public String[] all() { @@ -86,7 +87,8 @@ public String[] all() { FORM_NAME, DISPLAY_FORM_NAME, FIELD_MASK, - AGGREGATION_TYPE + AGGREGATION_TYPE, + CONFIDENTIAL ); } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeFields.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeFields.java index 98b8a6ab30..7589b5f1de 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeFields.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeFields.java @@ -77,7 +77,8 @@ public final class TrackedEntityAttributeFields { fh.nestedField(STYLE).with(ObjectStyleFields.allFields), fh.nestedField(ACCESS).with(AccessFields.read), fh.field(Columns.FORM_NAME), - fh.field(Columns.DISPLAY_FORM_NAME) + fh.field(Columns.DISPLAY_FORM_NAME), + fh.field(Columns.CONFIDENTIAL) ).build(); private TrackedEntityAttributeFields() { diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeStore.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeStore.java index 7ff1ec2c6c..919bea263a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackedEntityAttributeStore.java @@ -66,6 +66,7 @@ public void bindToStatement(@NonNull TrackedEntityAttribute o, @NonNull Statemen w.bind(26, o.displayFormName()); w.bind(27, o.fieldMask()); w.bind(28, o.aggregationType()); + w.bind(29, o.confidential()); } }; From 529275ccd8f415ba2ceacd37a7253c77535b99ab Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 19 Dec 2022 09:37:47 +0100 Subject: [PATCH 017/201] [ANDROSDK-1613] Add confidential to TrackedEntityAttribute collection repository --- .../TrackedEntityAttributeCollectionRepository.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeCollectionRepository.java index bc217706cd..6e54642561 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeCollectionRepository.java @@ -96,6 +96,10 @@ public BooleanFilterConnector byDisp return cf.bool(Columns.DISPLAY_ON_VISIT_SCHEDULE); } + public BooleanFilterConnector byConfidential() { + return cf.bool(Columns.CONFIDENTIAL); + } + public BooleanFilterConnector byOrgUnitScope() { return cf.bool(TrackedEntityAttributeFields.ORG_UNIT_SCOPE); } From 2113ef5b4ee6735b6306c31b917d988d8ced6f4a Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 19 Dec 2022 09:38:10 +0100 Subject: [PATCH 018/201] [ANDROSDK-1613] Add confidential to TrackedEntityAttribute samples and json --- .../core/data/trackedentity/TrackedEntityAttributeSamples.java | 1 + .../resources/trackedentity/tracked_entity_attributes.json | 1 + 2 files changed, 2 insertions(+) diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/TrackedEntityAttributeSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/TrackedEntityAttributeSamples.java index bcfab271ef..6f7996cf04 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/TrackedEntityAttributeSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/trackedentity/TrackedEntityAttributeSamples.java @@ -55,6 +55,7 @@ public static TrackedEntityAttribute get() { .generated(Boolean.TRUE) .aggregationType(AggregationType.DEFAULT) .displayOnVisitSchedule(Boolean.TRUE) + .confidential(Boolean.TRUE) .orgUnitScope(Boolean.TRUE) .unique(Boolean.TRUE) .inherit(Boolean.TRUE) diff --git a/core/src/sharedTest/resources/trackedentity/tracked_entity_attributes.json b/core/src/sharedTest/resources/trackedentity/tracked_entity_attributes.json index d786510fe9..4afb909d13 100644 --- a/core/src/sharedTest/resources/trackedentity/tracked_entity_attributes.json +++ b/core/src/sharedTest/resources/trackedentity/tracked_entity_attributes.json @@ -45,6 +45,7 @@ "orgunitScope": true, "displayInListNoProgram": true, "displayOnVisitSchedule": true, + "confidential": true, "sortOrderInListNoProgram": 0, "displayName": "Profile", "expression": "expression", From 5238c58448ac352381c7c542819d01138b791415 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 19 Dec 2022 09:38:27 +0100 Subject: [PATCH 019/201] [ANDROSDK-1613] Add confidential to TrackedEntityAttribute utils and tests --- .../CreateTrackedEntityAttributeUtils.java | 2 ++ ...ibuteCollectionRepositoryMockIntegrationShould.java | 10 ++++++++++ .../trackedentity/TrackedEntityAttributeShould.java | 1 + 3 files changed, 13 insertions(+) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/CreateTrackedEntityAttributeUtils.java b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/CreateTrackedEntityAttributeUtils.java index 3b1330e303..3b401be6fc 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/CreateTrackedEntityAttributeUtils.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/trackedentity/CreateTrackedEntityAttributeUtils.java @@ -66,6 +66,7 @@ public class CreateTrackedEntityAttributeUtils { private static final Integer ORG_UNIT_SCOPE = 0; // false private static final Integer UNIQUE = 1; // true private static final Integer INHERIT = 0; // false + private static final Integer CONFIDENTIAL = 0; // false public static ContentValues create(long id, String uid, String optionSetUid) { @@ -91,6 +92,7 @@ public static ContentValues create(long id, String uid, String optionSetUid) { values.put(Columns.DISPLAY_IN_LIST_NO_PROGRAM, DISPLAY_IN_LIST_NO_PROGRAM); values.put(Columns.GENERATED, GENERATED); values.put(Columns.DISPLAY_ON_VISIT_SCHEDULE, DISPLAY_ON_VISIT_SCHEDULE); + values.put(Columns.CONFIDENTIAL, CONFIDENTIAL); values.put(Columns.ORG_UNIT_SCOPE, ORG_UNIT_SCOPE); values.put(Columns.UNIQUE, UNIQUE); values.put(Columns.INHERIT, INHERIT); 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 6469c517ff..24e9f65754 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 @@ -140,6 +140,16 @@ public void by_display_on_visit_schedule() { assertThat(trackedEntityAttributes.size()).isEqualTo(1); } + @Test + public void by_confidential() { + List trackedEntityAttributes = + d2.trackedEntityModule().trackedEntityAttributes() + .byConfidential().isTrue() + .blockingGet(); + + assertThat(trackedEntityAttributes.size()).isEqualTo(1); + } + @Test public void by_orgunit_scope() { List trackedEntityAttributes = diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeShould.java b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeShould.java index 0dfb42cfcd..585f9a4bb9 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityAttributeShould.java @@ -66,6 +66,7 @@ public void map_from_json_string() throws IOException, ParseException { assertThat(trackedEntityAttribute.displayFormName()).isEqualTo("num"); assertThat(trackedEntityAttribute.displayInListNoProgram()).isFalse(); assertThat(trackedEntityAttribute.displayOnVisitSchedule()).isFalse(); + assertThat(trackedEntityAttribute.confidential()).isFalse(); assertThat(trackedEntityAttribute.generated()).isFalse(); assertThat(trackedEntityAttribute.aggregationType()).isEqualTo(AggregationType.DEFAULT); assertThat(trackedEntityAttribute.inherit()).isFalse(); From d17d550a4ad4fc96c3370a9f599f5e601004de1b Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 19 Dec 2022 09:47:15 +0100 Subject: [PATCH 020/201] [ANDROSDK-1613] Add migration and update the snapshot --- core/src/main/assets/migrations/136.sql | 3 +++ core/src/main/assets/snapshots/{135.sql => 136.sql} | 2 +- .../core/arch/db/access/internal/BaseDatabaseOpenHelper.java | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 core/src/main/assets/migrations/136.sql rename core/src/main/assets/snapshots/{135.sql => 136.sql} (99%) diff --git a/core/src/main/assets/migrations/136.sql b/core/src/main/assets/migrations/136.sql new file mode 100644 index 0000000000..bc74e52d09 --- /dev/null +++ b/core/src/main/assets/migrations/136.sql @@ -0,0 +1,3 @@ +# Add confidential to TrackedEntityAttribute (ANDROSDK-1613); + +ALTER TABLE TrackedEntityAttribute ADD COLUMN confidential INTEGER; diff --git a/core/src/main/assets/snapshots/135.sql b/core/src/main/assets/snapshots/136.sql similarity index 99% rename from core/src/main/assets/snapshots/135.sql rename to core/src/main/assets/snapshots/136.sql index eb8255b6a8..f979a04f74 100644 --- a/core/src/main/assets/snapshots/135.sql +++ b/core/src/main/assets/snapshots/136.sql @@ -61,7 +61,7 @@ CREATE TABLE ProgramRuleAction (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT CREATE TABLE OrganisationUnitLevel (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, level INTEGER); CREATE TABLE ProgramSection (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, description TEXT, program TEXT, sortOrder INTEGER, formName TEXT, color TEXT, icon TEXT, desktopRenderType TEXT, mobileRenderType TEXT, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE DataApproval (_id INTEGER PRIMARY KEY AUTOINCREMENT, workflow TEXT NOT NULL, organisationUnit TEXT NOT NULL, period TEXT NOT NULL, attributeOptionCombo TEXT NOT NULL, state TEXT, FOREIGN KEY (attributeOptionCombo) REFERENCES CategoryOptionCombo (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, UNIQUE (attributeOptionCombo, period, organisationUnit, workflow)); -CREATE TABLE TrackedEntityAttribute (_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, pattern TEXT, sortOrderInListNoProgram INTEGER, optionSet TEXT, valueType TEXT, expression TEXT, programScope INTEGER, displayInListNoProgram INTEGER, generated INTEGER, displayOnVisitSchedule INTEGER, orgunitScope INTEGER, uniqueProperty INTEGER, inherit INTEGER, formName TEXT, fieldMask TEXT, color TEXT, icon TEXT, displayFormName TEXT, aggregationType TEXT, FOREIGN KEY (optionSet) REFERENCES OptionSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE TrackedEntityAttribute (_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, pattern TEXT, sortOrderInListNoProgram INTEGER, optionSet TEXT, valueType TEXT, expression TEXT, programScope INTEGER, displayInListNoProgram INTEGER, generated INTEGER, displayOnVisitSchedule INTEGER, orgunitScope INTEGER, uniqueProperty INTEGER, inherit INTEGER, formName TEXT, fieldMask TEXT, color TEXT, icon TEXT, displayFormName TEXT, aggregationType TEXT, confidential INTEGER, FOREIGN KEY (optionSet) REFERENCES OptionSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE TrackerImportConflict (_id INTEGER PRIMARY KEY AUTOINCREMENT, conflict TEXT, value TEXT, trackedEntityInstance TEXT, enrollment TEXT, event TEXT, tableReference TEXT, errorCode TEXT, status TEXT, created TEXT, displayDescription TEXT, trackedEntityAttribute TEXT, dataElement TEXT, FOREIGN KEY (trackedEntityInstance) REFERENCES TrackedEntityInstance (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (enrollment) REFERENCES Enrollment (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (event) REFERENCES Event (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE DataSetOrganisationUnitLink (_id INTEGER PRIMARY KEY AUTOINCREMENT, dataSet TEXT NOT NULL, organisationUnit TEXT NOT NULL, FOREIGN KEY (dataSet) REFERENCES DataSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (organisationUnit, dataSet)); CREATE TABLE UserOrganisationUnit (_id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT NOT NULL, organisationUnit TEXT NOT NULL, organisationUnitScope TEXT NOT NULL, root INTEGER, userAssigned INTEGER, FOREIGN KEY (user) REFERENCES User (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (organisationUnitScope, user, organisationUnit)); 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 b56d735f31..250756be02 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 = 135; + static final int VERSION = 136; private final AssetManager assetManager; private final int targetVersion; From 1808f33ea09ea1fd726ed1bb2ef95a607a4d8e13 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 20 Dec 2022 13:45:27 +1100 Subject: [PATCH 021/201] [ANDROSDK-1569] Implement .periodOffset query modifier --- ...IndicatorEvaluatorIntegrationBaseShould.kt | 25 +++++++++ ...amIndicatorSQLExecutorIntegrationShould.kt | 3 +- .../internal/evaluator/AnalyticsEvaluator.kt | 7 ++- .../evaluator/AnalyticsEvaluatorHelper.kt | 10 +++- .../evaluator/AnalyticsPeriodHelper.kt | 42 ++++++++++++++ .../evaluator/DataElementSQLEvaluator.kt | 16 ++++-- .../evaluator/EventDataItemSQLEvaluator.kt | 16 ++++-- .../internal/evaluator/IndicatorEvaluator.kt | 7 ++- .../evaluator/IndicatorSQLEvaluator.kt | 7 ++- .../evaluator/ProgramIndicatorEvaluator.kt | 36 ++++++++---- .../ProgramIndicatorEvaluatorHelper.kt | 22 +++++--- .../evaluator/ProgramIndicatorSQLEvaluator.kt | 11 ++-- .../indicatorengine/IndicatorParserUtils.kt | 6 +- .../dataitem/IndicatorDataItem.kt | 6 +- .../expression/CommonExpressionVisitor.kt | 8 +++ .../parser/internal/expression/QueryMods.kt | 7 ++- .../expression/function/PeriodOffset.kt | 55 +++++++++++++++++++ .../internal/ProgramIndicatorSQLExecutor.kt | 14 +++-- 18 files changed, 242 insertions(+), 56 deletions(-) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/PeriodOffset.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt index 833c80849a..2ecc473c50 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt @@ -176,6 +176,31 @@ internal abstract class IndicatorEvaluatorIntegrationBaseShould : BaseEvaluatorI assertThat(overrideValue).isEqualTo("2.5") } + @Test + fun should_evaluate_period_offset() { + createDataValue("2", dataElementUid = dataElement1.uid(), periodId = period201911.periodId()!!) + createDataValue("3", dataElementUid = dataElement1.uid(), periodId = period201912.periodId()!!) + + val indicator = createIndicator(numerator = "(${de(dataElement1.uid())}.periodOffset(-2)).periodOffset(+1)") + + val value = evaluateForAbsolute(indicator, periodId = period201912.periodId()!!) + assertThat(value).isEqualTo("2.0") + } + + @Test + fun should_evaluate_relative_period_offset() { + createDataValue("2", dataElementUid = dataElement1.uid(), periodId = period201911.periodId()!!) + createDataValue("3", dataElementUid = dataElement1.uid(), periodId = period201912.periodId()!!) + createDataValue("20", dataElementUid = dataElement2.uid(), periodId = period201911.periodId()!!) + createDataValue("30", dataElementUid = dataElement2.uid(), periodId = period201912.periodId()!!) + + val expression = "${de(dataElement1.uid())} + ${de(dataElement2.uid())}.periodOffset(-1)" + val indicator = createIndicator(numerator = expression) + + val value = evaluateForThisMonth(indicator) + assertThat(value).isEqualTo("23.0") + } + private fun evaluateForThisMonth( indicator: Indicator, aggregationType: AggregationType = AggregationType.DEFAULT diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/BaseProgramIndicatorSQLExecutorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/BaseProgramIndicatorSQLExecutorIntegrationShould.kt index 4d22e03626..8c02c8fded 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/BaseProgramIndicatorSQLExecutorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/BaseProgramIndicatorSQLExecutorIntegrationShould.kt @@ -112,7 +112,8 @@ internal open class BaseProgramIndicatorSQLExecutorIntegrationShould : BaseEvalu evaluationItem = evaluationItem, metadata = metadata + (programIndicator.uid() to MetadataItem.ProgramIndicatorItem(programIndicator)) + - (periods?.associate { it.periodId()!! to MetadataItem.PeriodItem(it) } ?: emptyMap()) + (periods?.associate { it.periodId()!! to MetadataItem.PeriodItem(it) } ?: emptyMap()), + queryMods = 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 index 16aeaba5ef..4b81a3434a 100644 --- 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 @@ -30,16 +30,19 @@ 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 +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods internal interface AnalyticsEvaluator { fun evaluate( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods? = null ): String? fun getSql( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods? = null ): 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 index 9304481730..05ca57402a 100644 --- 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 @@ -95,9 +95,10 @@ internal object AnalyticsEvaluatorHelper { fun getReportingPeriods( items: List, - metadata: Map + metadata: Map, + periodOffset: Int? ): List { - return mutableListOf().apply { + val periods = mutableListOf().apply { items.forEach { i -> when (val item = i as DimensionItem.PeriodItem) { is DimensionItem.PeriodItem.Absolute -> { @@ -111,6 +112,11 @@ internal object AnalyticsEvaluatorHelper { } } } + return if (periodOffset != null && periodOffset != 0) { + AnalyticsPeriodHelper.shitPeriods(periods, periodOffset) + } else { + periods + } } fun getReportingPeriodsForAggregationType( diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt new file mode 100644 index 0000000000..7e888a6c55 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator + +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.internal.CalendarProviderFactory +import org.hisp.dhis.android.core.period.internal.ParentPeriodGeneratorImpl + +object AnalyticsPeriodHelper { + + private val periodGenerator = ParentPeriodGeneratorImpl.create(CalendarProviderFactory.calendarProvider) + + fun shitPeriods(periods: List, offset: Int): List { + return periods.mapNotNull { periodGenerator.generatePeriod(it.periodType()!!, it.startDate()!!, offset) } + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt index eee4a2961d..9279b6ee3a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt @@ -38,6 +38,7 @@ 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 +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.android.core.datavalue.DataValueTableInfo.Columns as dvColumns import org.hisp.dhis.android.core.period.PeriodTableInfo import org.hisp.dhis.android.core.period.PeriodTableInfo.Columns as peColumns @@ -48,9 +49,10 @@ internal class DataElementSQLEvaluator @Inject constructor( override fun evaluate( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { - val sqlQuery = getSql(evaluationItem, metadata) + val sqlQuery = getSql(evaluationItem, metadata, queryMods) return databaseAdapter.rawQuery(sqlQuery)?.use { c -> c.moveToFirst() @@ -60,7 +62,8 @@ internal class DataElementSQLEvaluator @Inject constructor( override fun getSql( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String { val items = AnalyticsDimensionHelper.getItemsByDimension(evaluationItem) @@ -70,7 +73,7 @@ internal class DataElementSQLEvaluator @Inject constructor( items.entries.forEach { entry -> when (entry.key) { is Dimension.Data -> appendDataWhereClause(entry.value, this) - is Dimension.Period -> appendPeriodWhereClause(entry.value, this, metadata, aggregator) + is Dimension.Period -> appendPeriodWhereClause(entry.value, this, metadata, aggregator, queryMods) is Dimension.OrganisationUnit -> appendOrgunitWhereClause(entry.value, this, metadata) is Dimension.Category -> appendCategoryWhereClause(entry.value, this, metadata) } @@ -178,9 +181,10 @@ internal class DataElementSQLEvaluator @Inject constructor( items: List, builder: WhereClauseBuilder, metadata: Map, - aggregationType: AggregationType + aggregationType: AggregationType, + queryMods: QueryMods?, ): WhereClauseBuilder { - val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(items, metadata) + val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(items, metadata, queryMods?.periodOffset) val periods = AnalyticsEvaluatorHelper.getReportingPeriodsForAggregationType(reportingPeriods, aggregationType) return builder.appendInSubQuery( diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt index c3dce2bc4c..55087aa7be 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt @@ -39,6 +39,7 @@ import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuil import org.hisp.dhis.android.core.common.AggregationType import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo import org.hisp.dhis.android.core.event.EventTableInfo +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueTableInfo import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueTableInfo.Columns as tavColumns import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueTableInfo @@ -50,9 +51,10 @@ internal class EventDataItemSQLEvaluator @Inject constructor( override fun evaluate( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { - val sqlQuery = getSql(evaluationItem, metadata) + val sqlQuery = getSql(evaluationItem, metadata, queryMods) return databaseAdapter.rawQuery(sqlQuery)?.use { c -> c.moveToFirst() @@ -63,7 +65,8 @@ internal class EventDataItemSQLEvaluator @Inject constructor( @Suppress("LongMethod") override fun getSql( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String { val items = AnalyticsDimensionHelper.getItemsByDimension(evaluationItem) @@ -75,7 +78,7 @@ internal class EventDataItemSQLEvaluator @Inject constructor( items.entries.forEach { entry -> when (entry.key) { is Dimension.Data -> appendDataWhereClause(entry.value, this) - is Dimension.Period -> appendPeriodWhereClause(entry.value, this, metadata, aggregator) + is Dimension.Period -> appendPeriodWhereClause(entry.value, this, metadata, aggregator, queryMods) is Dimension.OrganisationUnit -> appendOrgunitWhereClause(entry.value, this, metadata) is Dimension.Category -> appendCategoryWhereClause(entry.value, this, metadata) } @@ -224,9 +227,10 @@ internal class EventDataItemSQLEvaluator @Inject constructor( items: List, builder: WhereClauseBuilder, metadata: Map, - aggregation: AggregationType + aggregation: AggregationType, + queryMods: QueryMods?, ): WhereClauseBuilder { - val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(items, metadata) + val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(items, metadata, queryMods?.periodOffset) return if (reportingPeriods.isEmpty()) { builder diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluator.kt index 022a9fec70..ec790a27c9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluator.kt @@ -32,6 +32,7 @@ import org.hisp.dhis.android.core.analytics.AnalyticsException import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.IndicatorEngine +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods internal class IndicatorEvaluator @Inject constructor( private val indicatorEngine: IndicatorEngine @@ -39,7 +40,8 @@ internal class IndicatorEvaluator @Inject constructor( override fun evaluate( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { val indicator = IndicatorEvaluatorHelper.getIndicator(evaluationItem, metadata) val contextEvaluationItem = IndicatorEvaluatorHelper.getContextEvaluationItem(evaluationItem, indicator) @@ -53,7 +55,8 @@ internal class IndicatorEvaluator @Inject constructor( override fun getSql( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { throw AnalyticsException.SQLException("Method getSql not implemented for ProgramIndicatorEvaluator") } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorSQLEvaluator.kt index fa8816adf5..04cee4957b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorSQLEvaluator.kt @@ -31,6 +31,7 @@ import javax.inject.Inject import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.IndicatorSQLEngine +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods internal class IndicatorSQLEvaluator @Inject constructor( private val indicatorEngine: IndicatorSQLEngine @@ -38,7 +39,8 @@ internal class IndicatorSQLEvaluator @Inject constructor( override fun evaluate( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { val indicator = IndicatorEvaluatorHelper.getIndicator(evaluationItem, metadata) val contextEvaluationItem = IndicatorEvaluatorHelper.getContextEvaluationItem(evaluationItem, indicator) @@ -52,7 +54,8 @@ internal class IndicatorSQLEvaluator @Inject constructor( override fun getSql( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String { val indicator = IndicatorEvaluatorHelper.getIndicator(evaluationItem, metadata) val contextEvaluationItem = IndicatorEvaluatorHelper.getContextEvaluationItem(evaluationItem, indicator) diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluator.kt index 710489cf6c..a02def6f15 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluator.kt @@ -38,6 +38,7 @@ import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo 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.parser.internal.expression.QueryMods import org.hisp.dhis.android.core.program.ProgramIndicator import org.hisp.dhis.android.core.program.programindicatorengine.ProgramIndicatorEngine import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLUtils.enrollment @@ -51,7 +52,8 @@ internal class ProgramIndicatorEvaluator @Inject constructor( override fun evaluate( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { val programIndicator = ProgramIndicatorEvaluatorHelper.getProgramIndicator(evaluationItem, metadata) @@ -60,9 +62,9 @@ internal class ProgramIndicatorEvaluator @Inject constructor( val values: List = when (programIndicator.analyticsType()) { AnalyticsType.EVENT -> - evaluateEventProgramIndicator(programIndicator, evaluationItem, metadata) + evaluateEventProgramIndicator(programIndicator, evaluationItem, metadata, queryMods) AnalyticsType.ENROLLMENT, null -> - evaluateEnrollmentProgramIndicator(programIndicator, evaluationItem, metadata) + evaluateEnrollmentProgramIndicator(programIndicator, evaluationItem, metadata, queryMods) } return aggregateValues(aggregationType, values) @@ -70,7 +72,8 @@ internal class ProgramIndicatorEvaluator @Inject constructor( override fun getSql( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { throw AnalyticsException.SQLException("Method getSql not implemented for ProgramIndicatorEvaluator") } @@ -78,9 +81,10 @@ internal class ProgramIndicatorEvaluator @Inject constructor( private fun evaluateEventProgramIndicator( programIndicator: ProgramIndicator, evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): List { - return getFilteredEventUids(programIndicator, evaluationItem, metadata).map { + return getFilteredEventUids(programIndicator, evaluationItem, metadata, queryMods).map { programIndicatorEngine.getEventProgramIndicatorValue(it, programIndicator.uid()) } } @@ -88,10 +92,11 @@ internal class ProgramIndicatorEvaluator @Inject constructor( private fun getFilteredEventUids( programIndicator: ProgramIndicator, evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): List { val whereClause = ProgramIndicatorEvaluatorHelper - .getEventWhereClause(programIndicator, evaluationItem, metadata) + .getEventWhereClause(programIndicator, evaluationItem, metadata, queryMods) val rawClause = "SELECT * FROM ${EventTableInfo.TABLE_INFO.name()} $event WHERE $whereClause" return eventStore.selectRawQuery(rawClause).map { it.uid() } @@ -100,9 +105,10 @@ internal class ProgramIndicatorEvaluator @Inject constructor( private fun evaluateEnrollmentProgramIndicator( programIndicator: ProgramIndicator, evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods? ): List { - return getFilteredEnrollmentUids(programIndicator, evaluationItem, metadata).map { + return getFilteredEnrollmentUids(programIndicator, evaluationItem, metadata, queryMods).map { programIndicatorEngine.getEnrollmentProgramIndicatorValue(it, programIndicator.uid()) } } @@ -110,10 +116,16 @@ internal class ProgramIndicatorEvaluator @Inject constructor( private fun getFilteredEnrollmentUids( programIndicator: ProgramIndicator, evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): List { val whereClause = - ProgramIndicatorEvaluatorHelper.getEnrollmentWhereClause(programIndicator, evaluationItem, metadata) + ProgramIndicatorEvaluatorHelper.getEnrollmentWhereClause( + programIndicator, + evaluationItem, + metadata, + queryMods + ) val rawClause = "SELECT * FROM ${EnrollmentTableInfo.TABLE_INFO.name()} $enrollment WHERE $whereClause" return enrollmentStore.selectRawQuery(rawClause).map { it.uid() } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt index 691e2d849b..8c2670c9f3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt @@ -40,6 +40,7 @@ import org.hisp.dhis.android.core.common.AggregationType import org.hisp.dhis.android.core.common.AnalyticsType import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo import org.hisp.dhis.android.core.event.EventTableInfo +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.android.core.program.* import org.hisp.dhis.android.core.program.programindicatorengine.internal.AnalyticsBoundaryParser import org.hisp.dhis.android.core.program.programindicatorengine.internal.AnalyticsBoundaryTarget @@ -109,7 +110,8 @@ internal object ProgramIndicatorEvaluatorHelper { fun getEventWhereClause( programIndicator: ProgramIndicator, evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String { val items = AnalyticsDimensionHelper.getItemsByDimension(evaluationItem) @@ -135,7 +137,8 @@ internal object ProgramIndicatorEvaluatorHelper { programIndicator = programIndicator, dimensions = entry.value, builder = this, - metadata = metadata + metadata = metadata, + queryMods = queryMods ) } is Dimension.OrganisationUnit -> @@ -163,7 +166,8 @@ internal object ProgramIndicatorEvaluatorHelper { fun getEnrollmentWhereClause( programIndicator: ProgramIndicator, evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String { val items = AnalyticsDimensionHelper.getItemsByDimension(evaluationItem) @@ -184,7 +188,8 @@ internal object ProgramIndicatorEvaluatorHelper { programIndicator = programIndicator, dimensions = entry.value, builder = this, - metadata = metadata + metadata = metadata, + queryMods = queryMods ) is Dimension.OrganisationUnit -> AnalyticsEvaluatorHelper.appendOrgunitWhereClause( @@ -207,13 +212,15 @@ internal object ProgramIndicatorEvaluatorHelper { programIndicator: ProgramIndicator, defaultColumn: String, builder: WhereClauseBuilder, + queryMods: QueryMods? ) { if (hasDefaultBoundaries(programIndicator)) { builder.appendComplexQuery( buildDefaultBoundariesClause( column = defaultColumn, dimensions = dimensions, - metadata = metadata + metadata = metadata, + periodOffset = queryMods?.periodOffset ) ) } else { @@ -226,9 +233,10 @@ internal object ProgramIndicatorEvaluatorHelper { private fun buildDefaultBoundariesClause( column: String, dimensions: List, - metadata: Map + metadata: Map, + periodOffset: Int? ): String { - val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(dimensions, metadata) + val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(dimensions, metadata, periodOffset) return WhereClauseBuilder().apply { reportingPeriods.forEach { period -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorSQLEvaluator.kt index 1f6ba2fbd8..dc2ed38ee6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorSQLEvaluator.kt @@ -31,6 +31,7 @@ package org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator import javax.inject.Inject import org.hisp.dhis.android.core.analytics.aggregated.MetadataItem import org.hisp.dhis.android.core.analytics.aggregated.internal.AnalyticsServiceEvaluationItem +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorSQLExecutor internal class ProgramIndicatorSQLEvaluator @Inject constructor( @@ -39,15 +40,17 @@ internal class ProgramIndicatorSQLEvaluator @Inject constructor( override fun evaluate( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { - return programIndicatorSQLExecutor.getProgramIndicatorValue(evaluationItem, metadata) + return programIndicatorSQLExecutor.getProgramIndicatorValue(evaluationItem, metadata, queryMods) } override fun getSql( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String { - return programIndicatorSQLExecutor.getProgramIndicatorSQL(evaluationItem, metadata) + return programIndicatorSQLExecutor.getProgramIndicatorSQL(evaluationItem, metadata, queryMods) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt index 3925b32c16..3c0c924871 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt @@ -36,6 +36,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indica import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramDataElementItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramIndicatorItem import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.parser.internal.expression.function.PeriodOffset import org.hisp.dhis.android.core.period.Period import org.hisp.dhis.android.core.period.internal.PeriodHelper import org.hisp.dhis.android.core.program.programindicatorengine.internal.function.* @@ -103,7 +104,10 @@ internal object IndicatorParserUtils { ExpressionParser.HASH_BRACE to DataElementItem(), ExpressionParser.I_BRACE to ProgramIndicatorItem(), ExpressionParser.D_BRACE to ProgramDataElementItem(), - ExpressionParser.A_BRACE to ProgramAttributeItem() + ExpressionParser.A_BRACE to ProgramAttributeItem(), + + // Query modifiers + ExpressionParser.PERIOD_OFFSET to PeriodOffset(), ) fun getDays( diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/IndicatorDataItem.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/IndicatorDataItem.kt index 1a973dd9a0..19f394fc26 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/IndicatorDataItem.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/dataitem/IndicatorDataItem.kt @@ -47,7 +47,8 @@ internal interface IndicatorDataItem : ExpressionItem { getMetadataEntry(evaluationItem, visitor)?.let { metadataEntry -> getEvaluator(visitor).evaluate( evaluationItem = evaluationItem, - metadata = visitor.indicatorContext!!.contextMetadata + metadataEntry + metadata = visitor.indicatorContext!!.contextMetadata + metadataEntry, + queryMods = visitor.state.queryMods ) } } ?: ParserUtils.DOUBLE_VALUE_IF_NULL @@ -58,7 +59,8 @@ internal interface IndicatorDataItem : ExpressionItem { getMetadataEntry(evaluationItem, visitor)?.let { metadataEntry -> getEvaluator(visitor).getSql( evaluationItem = evaluationItem, - metadata = visitor.indicatorContext!!.contextMetadata + metadataEntry + metadata = visitor.indicatorContext!!.contextMetadata + metadataEntry, + queryMods = visitor.state.queryMods )?.let { "($it)" } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.kt index a1bac0c7a2..19e30c6ee8 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/CommonExpressionVisitor.kt @@ -149,6 +149,14 @@ internal class CommonExpressionVisitor constructor( return result } + fun visitWithQueryMods(ctx: ParserRuleContext, queryMods: QueryMods): Any? { + val savedQueryMods = state.queryMods + state.queryMods = queryMods + val result = visit(ctx) + state.queryMods = savedQueryMods + return result + } + /** * Handles nulls and missing values. * diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt index 81d1f191de..33e3249cd4 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt @@ -30,6 +30,7 @@ package org.hisp.dhis.android.core.parser.internal.expression import org.hisp.dhis.android.core.common.AggregationType -internal class QueryMods { - var aggregationType: AggregationType? = null -} +internal data class QueryMods( + var aggregationType: AggregationType? = null, + var periodOffset: Int? = null +) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/PeriodOffset.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/PeriodOffset.kt new file mode 100644 index 0000000000..a5d9b10833 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/PeriodOffset.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression.function + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +/** + * Function periodOffset + * + * @author Enrico Colasante + */ +internal class PeriodOffset : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + val state = visitor.state + val existingPeriodOffset = state.queryMods?.periodOffset ?: 0 + val parsedPeriodOffset = ctx.period?.text?.toIntOrNull() ?: 0 + val periodOffset = existingPeriodOffset + parsedPeriodOffset + + val queryMods = (state.queryMods ?: QueryMods()).copy(periodOffset = periodOffset) + + return visitor.visitWithQueryMods(ctx.expr(0), queryMods) + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + return evaluate(ctx, visitor) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt index 9e786141b9..a9a24f439b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt @@ -59,9 +59,10 @@ internal class ProgramIndicatorSQLExecutor @Inject constructor( fun getProgramIndicatorValue( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String? { - val sqlQuery = getProgramIndicatorSQL(evaluationItem, metadata) + val sqlQuery = getProgramIndicatorSQL(evaluationItem, metadata, queryMods) return databaseAdapter.rawQuery(sqlQuery)?.use { c -> c.moveToFirst() @@ -71,11 +72,12 @@ internal class ProgramIndicatorSQLExecutor @Inject constructor( fun getProgramIndicatorSQL( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): String { val programIndicator = ProgramIndicatorEvaluatorHelper.getProgramIndicator(evaluationItem, metadata) val periodItems = evaluationItem.allDimensionItems.filterIsInstance() - val periods = AnalyticsEvaluatorHelper.getReportingPeriods(periodItems, metadata) + val periods = AnalyticsEvaluatorHelper.getReportingPeriods(periodItems, metadata, queryMods?.periodOffset) if (programIndicator.expression() == null) { throw IllegalArgumentException("Program Indicator ${programIndicator.uid()} has empty expression.") @@ -90,9 +92,9 @@ internal class ProgramIndicatorSQLExecutor @Inject constructor( val contextWhereClause = when (programIndicator.analyticsType()) { AnalyticsType.EVENT -> - ProgramIndicatorEvaluatorHelper.getEventWhereClause(programIndicator, evaluationItem, metadata) + ProgramIndicatorEvaluatorHelper.getEventWhereClause(programIndicator, evaluationItem, metadata, queryMods) AnalyticsType.ENROLLMENT, null -> - ProgramIndicatorEvaluatorHelper.getEnrollmentWhereClause(programIndicator, evaluationItem, metadata) + ProgramIndicatorEvaluatorHelper.getEnrollmentWhereClause(programIndicator, evaluationItem, metadata, queryMods) } val context = ProgramIndicatorSQLContext( From 3ac30e8cd13356c1414dbeaf6d69643edcc369df Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 20 Dec 2022 16:23:09 +1100 Subject: [PATCH 022/201] [ANDROSDK-1569] Implement .aggregationType query modifier --- ...IndicatorEvaluatorIntegrationBaseShould.kt | 14 +++++ .../evaluator/DataElementSQLEvaluator.kt | 9 ++- .../evaluator/EventDataItemSQLEvaluator.kt | 9 ++- .../evaluator/ProgramIndicatorEvaluator.kt | 2 +- .../ProgramIndicatorEvaluatorHelper.kt | 7 ++- .../indicatorengine/IndicatorParserUtils.kt | 2 + .../function/FunctionAggregationType.kt | 60 +++++++++++++++++++ .../internal/ProgramIndicatorSQLExecutor.kt | 2 +- 8 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionAggregationType.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt index 2ecc473c50..828a660b92 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt @@ -201,6 +201,20 @@ internal abstract class IndicatorEvaluatorIntegrationBaseShould : BaseEvaluatorI assertThat(value).isEqualTo("23.0") } + @Test + fun should_evaluate_aggregation_type_function() { + createDataValue("2", dataElementUid = dataElement1.uid(), periodId = period201911.periodId()!!) + createDataValue("3", dataElementUid = dataElement1.uid(), periodId = period201912.periodId()!!) + + val sumIndicator = createIndicator(numerator = "${de(dataElement1.uid())}.aggregationType(SUM)") + val sumResult = evaluateForAbsolute(sumIndicator, periodId = period2019Q4.periodId()!!) + assertThat(sumResult).isEqualTo("5.0") + + val avgIndicator = createIndicator(numerator = "${de(dataElement1.uid())}.aggregationType(AVERAGE)") + val avgResult = evaluateForAbsolute(avgIndicator, periodId = period2019Q4.periodId()!!) + assertThat(avgResult).isEqualTo("2.5") + } + private fun evaluateForThisMonth( indicator: Indicator, aggregationType: AggregationType = AggregationType.DEFAULT diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt index 9279b6ee3a..42c0469364 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt @@ -67,7 +67,7 @@ internal class DataElementSQLEvaluator @Inject constructor( ): String { val items = AnalyticsDimensionHelper.getItemsByDimension(evaluationItem) - val aggregator = getAggregator(evaluationItem, metadata) + val aggregator = getAggregator(evaluationItem, metadata, queryMods) val whereClause = WhereClauseBuilder().apply { items.entries.forEach { entry -> @@ -222,11 +222,14 @@ internal class DataElementSQLEvaluator @Inject constructor( private fun getAggregator( evaluationItem: AnalyticsServiceEvaluationItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): AggregationType { val itemList: List = AnalyticsDimensionHelper.getSingleItemByDimension(evaluationItem) - return if (evaluationItem.aggregationType != AggregationType.DEFAULT) { + return if (queryMods?.aggregationType?.let { it != AggregationType.DEFAULT } == true) { + queryMods.aggregationType!! + } else if (evaluationItem.aggregationType != AggregationType.DEFAULT) { evaluationItem.aggregationType } else if (itemList.size > 1) { AggregationType.SUM diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt index 55087aa7be..1af35f12c9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt @@ -71,7 +71,7 @@ internal class EventDataItemSQLEvaluator @Inject constructor( val items = AnalyticsDimensionHelper.getItemsByDimension(evaluationItem) val eventDataItem = getEventDataItems(evaluationItem)[0] - val aggregator = getAggregator(evaluationItem, eventDataItem, metadata) + val aggregator = getAggregator(evaluationItem, eventDataItem, metadata, queryMods) val (valueColumn, fromClause) = getEventDataItemSQLItems(eventDataItem) val whereClause = WhereClauseBuilder().apply { @@ -288,9 +288,12 @@ internal class EventDataItemSQLEvaluator @Inject constructor( private fun getAggregator( evaluationItem: AnalyticsServiceEvaluationItem, item: DimensionItem.DataItem.EventDataItem, - metadata: Map + metadata: Map, + queryMods: QueryMods?, ): AggregationType { - return if (evaluationItem.aggregationType != AggregationType.DEFAULT) { + return if (queryMods?.aggregationType?.let { it != AggregationType.DEFAULT } == true) { + queryMods.aggregationType!! + } else if (evaluationItem.aggregationType != AggregationType.DEFAULT) { evaluationItem.aggregationType } else { val aggregationType = when (val metadataItem = metadata[item.id]) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluator.kt index a02def6f15..00f06f6ef5 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluator.kt @@ -58,7 +58,7 @@ internal class ProgramIndicatorEvaluator @Inject constructor( val programIndicator = ProgramIndicatorEvaluatorHelper.getProgramIndicator(evaluationItem, metadata) - val aggregationType = ProgramIndicatorEvaluatorHelper.getAggregator(evaluationItem, programIndicator) + val aggregationType = ProgramIndicatorEvaluatorHelper.getAggregator(evaluationItem, programIndicator, queryMods) val values: List = when (programIndicator.analyticsType()) { AnalyticsType.EVENT -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt index 8c2670c9f3..af1b241d6b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt @@ -387,10 +387,13 @@ internal object ProgramIndicatorEvaluatorHelper { fun getAggregator( evaluationItem: AnalyticsServiceEvaluationItem, - programIndicator: ProgramIndicator + programIndicator: ProgramIndicator, + queryMods: QueryMods?, ): AggregationType { val aggregationType = - if (evaluationItem.aggregationType != AggregationType.DEFAULT) { + if (queryMods?.aggregationType?.let { it != AggregationType.DEFAULT } == true) { + queryMods.aggregationType!! + } else if (evaluationItem.aggregationType != AggregationType.DEFAULT) { evaluationItem.aggregationType } else { programIndicator.aggregationType() diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt index 3c0c924871..6243201298 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt @@ -36,6 +36,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indica import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramDataElementItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramIndicatorItem import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionAggregationType import org.hisp.dhis.android.core.parser.internal.expression.function.PeriodOffset import org.hisp.dhis.android.core.period.Period import org.hisp.dhis.android.core.period.internal.PeriodHelper @@ -107,6 +108,7 @@ internal object IndicatorParserUtils { ExpressionParser.A_BRACE to ProgramAttributeItem(), // Query modifiers + ExpressionParser.AGGREGATION_TYPE to FunctionAggregationType(), ExpressionParser.PERIOD_OFFSET to PeriodOffset(), ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionAggregationType.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionAggregationType.kt new file mode 100644 index 0000000000..07168c8ee8 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionAggregationType.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression.function + +import org.hisp.dhis.android.core.common.AggregationType +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +/** + * Function periodOffset + * + * @author Jim Grace + */ +internal class FunctionAggregationType : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + val aggregationType = parseAggregationType(ctx.aggregationType.text) + val queryMods = (visitor.state.queryMods ?: QueryMods()).copy(aggregationType = aggregationType) + + return visitor.visitWithQueryMods(ctx.expr(0), queryMods) + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + return evaluate(ctx, visitor) + } + + private fun parseAggregationType(text: String): AggregationType? { + return try { + AggregationType.valueOf(text) + } catch (e: IllegalArgumentException) { + null + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt index a9a24f439b..89ad4761ff 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt @@ -109,7 +109,7 @@ internal class ProgramIndicatorSQLExecutor @Inject constructor( sqlVisitor.itemIds = collector.itemIds.toMutableSet() sqlVisitor.setExpressionLiteral(ProgramIndicatorSQLLiteral()) - val aggregator = ProgramIndicatorEvaluatorHelper.getAggregator(evaluationItem, programIndicator) + val aggregator = ProgramIndicatorEvaluatorHelper.getAggregator(evaluationItem, programIndicator, queryMods) val selectExpression = CommonParser.visit(programIndicator.expression(), sqlVisitor) // TODO Include more cases that are expected to be evaluated as "1" From b612023e0c8ed735cf77b0b0a4b62cfadf5fad0b Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 21 Dec 2022 12:38:17 +1100 Subject: [PATCH 023/201] [ANDROSDK-1569] Import kotlinx-datetime --- core/build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index 1502fa0003..440a29cad9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -68,6 +68,9 @@ ext { smsCompression : "0.2.0", expressionParser: "1.0.29", + // Kotlin + kotlinxDatetime : "0.4.0", + // test dependencies coreTesting : "2.1.0", jUnit : "4.13.2", @@ -189,6 +192,9 @@ dependencies { // Joda time api "joda-time:joda-time:${libraries.jodaTime}" + // Kotlinx datetime + implementation "org.jetbrains.kotlinx:kotlinx-datetime:${libraries.kotlinxDatetime}" + // sms compression library api "com.github.dhis2:sms-compression:${libraries.smsCompression}" From d05584c68911b3ee1442ef6a53a2f32f22c1d175 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 21 Dec 2022 12:38:40 +1100 Subject: [PATCH 024/201] [ANDROSDK-1569] Add .minDate and .maxDate modifiers --- .../BaseEvaluatorIntegrationShould.kt | 2 + .../evaluator/BaseEvaluatorSamples.kt | 7 +++ ...IndicatorEvaluatorIntegrationBaseShould.kt | 19 ++++++ .../evaluator/AnalyticsEvaluatorHelper.kt | 13 ++++ .../evaluator/DataElementSQLEvaluator.kt | 14 +++++ .../indicatorengine/IndicatorParserUtils.kt | 4 ++ .../parser/internal/expression/ParserUtils.kt | 13 ++++ .../parser/internal/expression/QueryMods.kt | 3 + .../expression/function/FunctionMaxDate.kt | 52 +++++++++++++++ .../expression/function/FunctionMinDate.kt | 52 +++++++++++++++ .../internal/expression/ParserUtilsShould.kt | 63 +++++++++++++++++++ 11 files changed, 242 insertions(+) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMaxDate.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMinDate.kt create mode 100644 core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtilsShould.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt index 831ac92321..9f0d24f09c 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt @@ -55,6 +55,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEv import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.orgunitChild1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.orgunitChild2 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.orgunitParent +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201910 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201911 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201912 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019Q4 @@ -207,6 +208,7 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt dataElementStore.insert(dataElement3) dataElementStore.insert(dataElement4) + periodStore.insert(period201910) periodStore.insert(period201911) periodStore.insert(period201912) periodStore.insert(period2019Q4) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorSamples.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorSamples.kt index 7c9981a26f..24ec220bb5 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorSamples.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorSamples.kt @@ -220,6 +220,13 @@ object BaseEvaluatorSamples { .valueType(ValueType.INTEGER) .build() + val period201910: Period = Period.builder() + .periodId("201910") + .periodType(PeriodType.Monthly) + .startDate(DateUtils.DATE_FORMAT.parse("2019-10-01T00:00:00.000")) + .endDate(DateUtils.DATE_FORMAT.parse("2019-10-31T23:59:59.999")) + .build() + val period201911: Period = Period.builder() .periodId("201911") .periodType(PeriodType.Monthly) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt index 828a660b92..3ebb347f0c 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt @@ -40,9 +40,11 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEv import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.generator import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.orgunitChild1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.orgunitParent +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201910 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201911 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201912 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019Q4 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period202001 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.program import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.programStage1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.trackedEntityType @@ -215,6 +217,23 @@ internal abstract class IndicatorEvaluatorIntegrationBaseShould : BaseEvaluatorI assertThat(avgResult).isEqualTo("2.5") } + @Test + fun should_evaluate_min_date_function() { + createDataValue("2", dataElementUid = dataElement1.uid(), periodId = period201910.periodId()!!) + createDataValue("4", dataElementUid = dataElement1.uid(), periodId = period201911.periodId()!!) + createDataValue("8", dataElementUid = dataElement1.uid(), periodId = period201912.periodId()!!) + + mapOf( + "${de(dataElement1.uid())}.minDate(2019-10-05)" to "12.0", + "${de(dataElement1.uid())}.maxDate(2019-12-01)" to "6.0", + "${de(dataElement1.uid())}.minDate(2019-10-05).maxDate(2019-12-01)" to "4.0", + ).forEach { (numerator, expected) -> + val indicator = createIndicator(numerator = numerator) + val result = evaluateForAbsolute(indicator, periodId = period2019Q4.periodId()!!) + assertThat(result).isEqualTo(expected) + } + } + private fun evaluateForThisMonth( indicator: Indicator, aggregationType: AggregationType = AggregationType.DEFAULT 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 index 05ca57402a..1b5f8621e5 100644 --- 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 @@ -175,6 +175,19 @@ internal object AnalyticsEvaluatorHelper { }" } + fun getPeriodsFromDate(startDate: String): String { + return "SELECT ${PeriodTableInfo.Columns.PERIOD_ID} " + + "FROM ${PeriodTableInfo.TABLE_INFO.name()} " + + "WHERE ${PeriodTableInfo.Columns.START_DATE} >= '$startDate'" + } + + + fun getPeriodsToDate(endDate: String): String { + return "SELECT ${PeriodTableInfo.Columns.PERIOD_ID} " + + "FROM ${PeriodTableInfo.TABLE_INFO.name()} " + + "WHERE ${PeriodTableInfo.Columns.END_DATE} <= '$endDate'" + } + fun getPeriodWhereClause(columnStart: String, columnEnd: String, period: Period): String { return "$columnStart >= '${DateUtils.DATE_FORMAT.format(period.startDate()!!)}' " + "AND " + diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt index 42c0469364..2b05855dce 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt @@ -78,6 +78,7 @@ internal class DataElementSQLEvaluator @Inject constructor( is Dimension.Category -> appendCategoryWhereClause(entry.value, this, metadata) } } + appendDateQueryMods(queryMods, this) appendKeyNumberValue(DataValueTableInfo.Columns.DELETED, 0) }.build() @@ -220,6 +221,19 @@ internal class DataElementSQLEvaluator @Inject constructor( ) } + private fun appendDateQueryMods(queryMods: QueryMods?, builder: WhereClauseBuilder): WhereClauseBuilder { + return builder.apply { + queryMods?.minDate?.let { + val date = it.toString() + appendInSubQuery(DataValueTableInfo.Columns.PERIOD, AnalyticsEvaluatorHelper.getPeriodsFromDate(date)) + } + queryMods?.maxDate?.let { + val date = it.toString() + appendInSubQuery(DataValueTableInfo.Columns.PERIOD, AnalyticsEvaluatorHelper.getPeriodsToDate(date)) + } + } + } + private fun getAggregator( evaluationItem: AnalyticsServiceEvaluationItem, metadata: Map, diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt index 6243201298..790132a291 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt @@ -37,6 +37,8 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indica import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramIndicatorItem import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionAggregationType +import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionMaxDate +import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionMinDate import org.hisp.dhis.android.core.parser.internal.expression.function.PeriodOffset import org.hisp.dhis.android.core.period.Period import org.hisp.dhis.android.core.period.internal.PeriodHelper @@ -109,6 +111,8 @@ internal object IndicatorParserUtils { // Query modifiers ExpressionParser.AGGREGATION_TYPE to FunctionAggregationType(), + ExpressionParser.MIN_DATE to FunctionMinDate(), + ExpressionParser.MAX_DATE to FunctionMaxDate(), ExpressionParser.PERIOD_OFFSET to PeriodOffset(), ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt index 5e0958ef79..8832e21f10 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt @@ -27,6 +27,7 @@ */ package org.hisp.dhis.android.core.parser.internal.expression +import kotlinx.datetime.LocalDate import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -37,8 +38,10 @@ import org.hisp.dhis.android.core.parser.internal.expression.operator.* import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemConstant import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemDays import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemOrgUnitGroup +import org.hisp.dhis.antlr.ParserExceptionWithoutContext import org.hisp.dhis.parser.expression.antlr.ExpressionParser import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import kotlin.math.exp internal object ParserUtils { const val DOUBLE_VALUE_IF_NULL = 0.0 @@ -137,4 +140,14 @@ internal object ParserUtils { format.format(date) } } + + fun parseExpressionDate(expression: String): LocalDate { + return try { + val tokens = expression.split('-') + val dateStr = tokens[0] + "-" + tokens[1].padStart(2, '0') + "-" + tokens[2].padStart(2, '0') + LocalDate.parse(dateStr) + } catch (e: Exception) { + throw ParserExceptionWithoutContext("Invalid date: $expression - ${e.message}") + } + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt index 33e3249cd4..1204ee3961 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt @@ -28,9 +28,12 @@ package org.hisp.dhis.android.core.parser.internal.expression +import kotlinx.datetime.LocalDate import org.hisp.dhis.android.core.common.AggregationType internal data class QueryMods( var aggregationType: AggregationType? = null, + var minDate: LocalDate? = null, + var maxDate: LocalDate? = null, var periodOffset: Int? = null ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMaxDate.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMaxDate.kt new file mode 100644 index 0000000000..00383f659e --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMaxDate.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression.function + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +/** + * Function periodOffset + * + * @author Jim Grace + */ +internal class FunctionMaxDate : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + val date = ParserUtils.parseExpressionDate(ctx.maxDate.text) + val queryMods = (visitor.state.queryMods ?: QueryMods()).copy(maxDate = date) + + return visitor.visitWithQueryMods(ctx.expr(0), queryMods) + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + return evaluate(ctx, visitor) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMinDate.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMinDate.kt new file mode 100644 index 0000000000..f811fce324 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMinDate.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression.function + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +/** + * Function periodOffset + * + * @author Jim Grace + */ +internal class FunctionMinDate : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + val date = ParserUtils.parseExpressionDate(ctx.minDate.text) + val queryMods = (visitor.state.queryMods ?: QueryMods()).copy(minDate = date) + + return visitor.visitWithQueryMods(ctx.expr(0), queryMods) + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + return evaluate(ctx, visitor) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtilsShould.kt b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtilsShould.kt new file mode 100644 index 0000000000..60821fad55 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtilsShould.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.junit.Assert.assertThrows +import org.junit.Test + +class ParserUtilsShould { + + @Test + fun parse_expression_date() { + mapOf( + "2022-12-10" to listOf(2022, 12, 10, "2022-12-10"), + "2022-05-08" to listOf(2022, 5, 8, "2022-05-08"), + "2022-5-8" to listOf(2022, 5, 8, "2022-05-08"), + ).forEach { (str, tokens) -> + val date = ParserUtils.parseExpressionDate(str) + assertThat(date.year).isEqualTo(tokens[0]) + assertThat(date.monthNumber).isEqualTo(tokens[1]) + assertThat(date.dayOfMonth).isEqualTo(tokens[2]) + assertThat(date.toString()).isEqualTo(tokens[3]) + } + } + + @Test + fun parse_invalid_date() { + listOf( + "", + "null", + "2022-08", + "2022-13-35", + ).forEach { + assertThrows(ParserExceptionWithoutContext::class.java) { ParserUtils.parseExpressionDate(it) } + } + } +} From 500a3d1a4f4ea8a7b22c7457d0a982d433fd504d Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 21 Dec 2022 15:06:05 +1100 Subject: [PATCH 025/201] [ANDROSDK-1569] Add [yearlyPeriodCount] and [periodInYear] items --- .../BaseEvaluatorIntegrationShould.kt | 4 ++ .../evaluator/BaseEvaluatorSamples.kt | 7 ++ ...IndicatorEvaluatorIntegrationBaseShould.kt | 30 +++++++- .../evaluator/AnalyticsEvaluatorHelper.kt | 2 +- .../evaluator/AnalyticsPeriodHelper.kt | 19 ++++- .../indicatorengine/IndicatorParserUtils.kt | 7 ++ .../parser/internal/expression/ParserUtils.kt | 6 ++ .../parser/internal/expression/QueryMods.kt | 3 +- .../function/FunctionAggregationType.kt | 2 +- .../expression/function/FunctionMaxDate.kt | 2 +- .../expression/function/FunctionMinDate.kt | 2 +- .../expression/function/FunctionYearToDate.kt | 50 +++++++++++++ .../service/dataitem/ItemPeriodBase.kt | 67 +++++++++++++++++ .../service/dataitem/ItemPeriodInYear.kt | 52 ++++++++++++++ .../service/dataitem/ItemYearlyPeriodCount.kt | 71 +++++++++++++++++++ .../internal/expression/ParserUtilsShould.kt | 14 ++++ 16 files changed, 330 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionYearToDate.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodBase.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodInYear.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemYearlyPeriodCount.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt index 9f0d24f09c..01beb19213 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt @@ -59,6 +59,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEv import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201911 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201912 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019Q4 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019SunW25 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period202001 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period202012 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.program @@ -149,6 +150,8 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt dataElement1.displayName()!!, categoryOptionCombo.displayName() ), + period2019SunW25.periodId()!! to MetadataItem.PeriodItem(period2019SunW25), + period201910.periodId()!! to MetadataItem.PeriodItem(period201910), period201911.periodId()!! to MetadataItem.PeriodItem(period201911), period201912.periodId()!! to MetadataItem.PeriodItem(period201912), period202001.periodId()!! to MetadataItem.PeriodItem(period202001), @@ -208,6 +211,7 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt dataElementStore.insert(dataElement3) dataElementStore.insert(dataElement4) + periodStore.insert(period2019SunW25) periodStore.insert(period201910) periodStore.insert(period201911) periodStore.insert(period201912) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorSamples.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorSamples.kt index 24ec220bb5..e8dba82ae7 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorSamples.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorSamples.kt @@ -220,6 +220,13 @@ object BaseEvaluatorSamples { .valueType(ValueType.INTEGER) .build() + val period2019SunW25: Period = Period.builder() + .periodId("2019SunW25") + .periodType(PeriodType.WeeklySunday) + .startDate(DateUtils.DATE_FORMAT.parse("2019-06-16T00:00:00.000")) + .endDate(DateUtils.DATE_FORMAT.parse("2019-06-22T23:59:59.999")) + .build() + val period201910: Period = Period.builder() .periodId("201910") .periodType(PeriodType.Monthly) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt index 3ebb347f0c..6c299e9586 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt @@ -44,7 +44,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEv import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201911 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201912 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019Q4 -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period202001 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019SunW25 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.program import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.programStage1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.trackedEntityType @@ -234,6 +234,34 @@ internal abstract class IndicatorEvaluatorIntegrationBaseShould : BaseEvaluatorI } } + @Test + fun should_evaluate_yearly_period_count_item() { + val indicator = createIndicator(numerator = "[yearlyPeriodCount]") + + mapOf( + period201910 to "12.0", + period2019Q4 to "4.0", + period2019SunW25 to "52.0" + ).forEach { (period, expected) -> + val result = evaluateForAbsolute(indicator, periodId = period.periodId()!!) + assertThat(result).isEqualTo(expected) + } + } + + @Test + fun should_evaluate_period_in_year() { + val indicator = createIndicator(numerator = "[periodInYear]") + + mapOf( + period201910 to "10.0", + period2019Q4 to "4.0", + period2019SunW25 to "25.0" + ).forEach { (period, expected) -> + val result = evaluateForAbsolute(indicator, periodId = period.periodId()!!) + assertThat(result).isEqualTo(expected) + } + } + private fun evaluateForThisMonth( indicator: Indicator, aggregationType: AggregationType = AggregationType.DEFAULT 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 index 1b5f8621e5..d7e48cff41 100644 --- 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 @@ -113,7 +113,7 @@ internal object AnalyticsEvaluatorHelper { } } return if (periodOffset != null && periodOffset != 0) { - AnalyticsPeriodHelper.shitPeriods(periods, periodOffset) + AnalyticsPeriodHelper.shiftPeriods(periods, periodOffset) } else { periods } diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt index 7e888a6c55..40cbed298a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt @@ -28,7 +28,10 @@ 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.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.PeriodType import org.hisp.dhis.android.core.period.internal.CalendarProviderFactory import org.hisp.dhis.android.core.period.internal.ParentPeriodGeneratorImpl @@ -36,7 +39,19 @@ object AnalyticsPeriodHelper { private val periodGenerator = ParentPeriodGeneratorImpl.create(CalendarProviderFactory.calendarProvider) - fun shitPeriods(periods: List, offset: Int): List { - return periods.mapNotNull { periodGenerator.generatePeriod(it.periodType()!!, it.startDate()!!, offset) } + fun shiftPeriod(period: Period, offset: Int): Period { + return periodGenerator.generatePeriod(period.periodType()!!, period.startDate()!!, offset)!! + } + + fun shiftPeriods(periods: List, offset: Int): List { + return periods.map { shiftPeriod(it, offset) } + } + + fun countWeeksOrBiWeeksInYear(periodType: PeriodType, year: Int): Int { + // The period containing this date is the last period in the year + val lastDate = DateUtils.SIMPLE_DATE_FORMAT.parse("$year-12-28") + val lastPeriod = periodGenerator.generatePeriod(periodType, lastDate, 0)!! + + return ParserUtils.getTrailingDigits(lastPeriod.periodId()!!)!! } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt index 790132a291..67ec867f19 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/indicatorengine/IndicatorParserUtils.kt @@ -36,10 +36,14 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indica import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramDataElementItem import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.indicatorengine.dataitem.ProgramIndicatorItem import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.parser.internal.expression.function.* import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionAggregationType import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionMaxDate import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionMinDate +import org.hisp.dhis.android.core.parser.internal.expression.function.FunctionYearToDate import org.hisp.dhis.android.core.parser.internal.expression.function.PeriodOffset +import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemPeriodInYear +import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemYearlyPeriodCount import org.hisp.dhis.android.core.period.Period import org.hisp.dhis.android.core.period.internal.PeriodHelper import org.hisp.dhis.android.core.program.programindicatorengine.internal.function.* @@ -108,12 +112,15 @@ internal object IndicatorParserUtils { ExpressionParser.I_BRACE to ProgramIndicatorItem(), ExpressionParser.D_BRACE to ProgramDataElementItem(), ExpressionParser.A_BRACE to ProgramAttributeItem(), + ExpressionParser.PERIOD_IN_YEAR to ItemPeriodInYear(), + ExpressionParser.YEARLY_PERIOD_COUNT to ItemYearlyPeriodCount(), // Query modifiers ExpressionParser.AGGREGATION_TYPE to FunctionAggregationType(), ExpressionParser.MIN_DATE to FunctionMinDate(), ExpressionParser.MAX_DATE to FunctionMaxDate(), ExpressionParser.PERIOD_OFFSET to PeriodOffset(), + ExpressionParser.YEAR_TO_DATE to FunctionYearToDate(), ) fun getDays( diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt index 8832e21f10..862db3b7e2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt @@ -28,6 +28,7 @@ package org.hisp.dhis.android.core.parser.internal.expression import kotlinx.datetime.LocalDate +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.AnalyticsPeriodHelper import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -48,6 +49,7 @@ internal object ParserUtils { private const val NUMERIC_REGEXP = "^(-?0|-?[1-9]\\d*)(\\.\\d+)?(E(-)?\\d+)?$" private val NUMERIC_PATTERN = Pattern.compile(NUMERIC_REGEXP) private const val DEFAULT_DATE_FORMAT = "yyyy-MM-dd" + private val trailingPeriodDigits = "\\d+$".toRegex() val ITEM_GET_DESCRIPTIONS = ExpressionItem::getDescription val ITEM_GET_IDS = ExpressionItem::getItemId @@ -150,4 +152,8 @@ internal object ParserUtils { throw ParserExceptionWithoutContext("Invalid date: $expression - ${e.message}") } } + + fun getTrailingDigits(periodId: String): Int? { + return trailingPeriodDigits.find(periodId)?.value?.toInt() + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt index 1204ee3961..99e0c1a4e2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/QueryMods.kt @@ -35,5 +35,6 @@ internal data class QueryMods( var aggregationType: AggregationType? = null, var minDate: LocalDate? = null, var maxDate: LocalDate? = null, - var periodOffset: Int? = null + var periodOffset: Int? = null, + var yearToDate: Boolean = false, ) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionAggregationType.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionAggregationType.kt index 07168c8ee8..657fc5bc29 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionAggregationType.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionAggregationType.kt @@ -34,7 +34,7 @@ import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext /** - * Function periodOffset + * Function aggregationType * * @author Jim Grace */ diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMaxDate.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMaxDate.kt index 00383f659e..efade24d2b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMaxDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMaxDate.kt @@ -34,7 +34,7 @@ import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext /** - * Function periodOffset + * Function maxDate * * @author Jim Grace */ diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMinDate.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMinDate.kt index f811fce324..956a8ec64a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMinDate.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionMinDate.kt @@ -34,7 +34,7 @@ import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext /** - * Function periodOffset + * Function minDate * * @author Jim Grace */ diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionYearToDate.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionYearToDate.kt new file mode 100644 index 0000000000..535c11e727 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/function/FunctionYearToDate.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.expression.function + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +/** + * Function periodOffset + * + * @author Jim Grace + */ +internal class FunctionYearToDate : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + val queryMods = (visitor.state.queryMods ?: QueryMods()).copy(yearToDate = true) + + return visitor.visitWithQueryMods(ctx.expr(0), queryMods) + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + return evaluate(ctx, visitor) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodBase.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodBase.kt new file mode 100644 index 0000000000..14e688353a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodBase.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.service.dataitem + +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.evaluator.AnalyticsPeriodHelper +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +internal abstract class ItemPeriodBase : ExpressionItem { + + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + val periodIds = visitor.indicatorContext?.evaluationItem?.allDimensionItems + ?.filterIsInstance()?.map { it.id } ?: emptyList() + + if (periodIds.size != 1) { + return ParserUtils.DOUBLE_VALUE_IF_NULL + } + + val periodItem = visitor.indicatorContext?.contextMetadata?.get(periodIds.first())!! as MetadataItem.PeriodItem + + val offset = visitor.state.queryMods?.periodOffset + val period = + if (offset != null && offset != 0) { + AnalyticsPeriodHelper.shiftPeriod(periodItem.item, offset) + } else { + periodItem.item + } + + return evaluate(period) + } + + override fun getSql(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + return evaluate(ctx, visitor) ?: ParserUtils.DOUBLE_VALUE_IF_NULL + } + + abstract fun evaluate(period: Period): Double +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodInYear.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodInYear.kt new file mode 100644 index 0000000000..6b9d7b5603 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodInYear.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.service.dataitem + +import kotlinx.datetime.LocalDate +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.PeriodType + +internal class ItemPeriodInYear : ItemPeriodBase() { + override fun evaluate(period: Period): Double { + val periodIsoDate = DateUtils.SIMPLE_DATE_FORMAT.format(period.startDate()!!) + + return when (period.periodType()!!) { + PeriodType.Daily -> LocalDate.parse(periodIsoDate).dayOfYear + PeriodType.Monthly, + PeriodType.BiMonthly -> period.periodId()!!.substring(4, 6).toInt() + PeriodType.Yearly, + PeriodType.FinancialApril, + PeriodType.FinancialJuly, + PeriodType.FinancialNov, + PeriodType.FinancialOct -> 1 + else -> ParserUtils.getTrailingDigits(period.periodId()!!) ?: 0 + }.toDouble() + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemYearlyPeriodCount.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemYearlyPeriodCount.kt new file mode 100644 index 0000000000..7f841b701a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemYearlyPeriodCount.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.parser.internal.service.dataitem + +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.AnalyticsPeriodHelper +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.PeriodType + +internal class ItemYearlyPeriodCount : ItemPeriodBase() { + override fun evaluate(period: Period): Double { + return when (period.periodType()!!) { + PeriodType.Daily -> evaluateDaily(period) + PeriodType.Weekly, + PeriodType.WeeklyWednesday, + PeriodType.WeeklyThursday, + PeriodType.WeeklySaturday, + PeriodType.WeeklySunday, + PeriodType.BiWeekly -> evaluateWeeklyOrBiWeekly(period) + PeriodType.Monthly -> 12 + PeriodType.BiMonthly -> 6 + PeriodType.Quarterly -> 4 + PeriodType.SixMonthly, + PeriodType.SixMonthlyApril, + PeriodType.SixMonthlyNov -> 2 + PeriodType.Yearly, + PeriodType.FinancialApril, + PeriodType.FinancialJuly, + PeriodType.FinancialOct, + PeriodType.FinancialNov -> 1 + }.toDouble() + } + + private fun evaluateDaily(period: Period): Int { + val year = getYear(period) + return if (year % 4 == 0) 366 else 365 + } + + private fun evaluateWeeklyOrBiWeekly(period: Period): Int { + val year = getYear(period) + return AnalyticsPeriodHelper.countWeeksOrBiWeeksInYear(period.periodType()!!, year) + } + + private fun getYear(period: Period): Int { + return period.periodId()!!.substring(0, 4).toInt() + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtilsShould.kt b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtilsShould.kt index 60821fad55..f988535b27 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtilsShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtilsShould.kt @@ -60,4 +60,18 @@ class ParserUtilsShould { assertThrows(ParserExceptionWithoutContext::class.java) { ParserUtils.parseExpressionDate(it) } } } + + @Test + fun should_parse_period_trailing_digits() { + mapOf( + "2022W1" to 1, + "2022WedW14" to 14, + "2022Q4" to 4, + "2022BiW13" to 13, + "2022S2" to 2, + "2022NovS1" to 1 + ).forEach { (periodId, trailingDigits) -> + assertThat(ParserUtils.getTrailingDigits(periodId)).isEqualTo(trailingDigits) + } + } } From 58f4db2aaf4db8133ef5bc5432cb13e720a314f2 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 22 Dec 2022 14:05:36 +1100 Subject: [PATCH 026/201] [ANDROSDK-1569] Implement yearToDate function --- .../BaseEvaluatorIntegrationShould.kt | 1 + ...IndicatorEvaluatorIntegrationBaseShould.kt | 21 ++++++++++++ .../evaluator/AnalyticsEvaluatorHelper.kt | 33 ++++++++++++------- .../evaluator/AnalyticsPeriodHelper.kt | 12 ++++++- .../evaluator/DataElementSQLEvaluator.kt | 4 +-- .../evaluator/EventDataItemSQLEvaluator.kt | 2 +- .../ProgramIndicatorEvaluatorHelper.kt | 7 ++-- .../parser/internal/expression/ParserUtils.kt | 26 ++++++++++++--- .../service/dataitem/ItemPeriodInYear.kt | 17 +--------- .../service/dataitem/ItemYearlyPeriodCount.kt | 1 + .../internal/ProgramIndicatorEngineImpl.kt | 1 + .../internal/ProgramIndicatorSQLExecutor.kt | 16 +++++++-- .../stock/StockUseCaseCollectionRepository.kt | 20 +++++------ .../internal/StockUseCaseEntityDIModule.kt | 8 ++--- .../StockUseCaseTransactionEntityDIModule.kt | 4 +-- 15 files changed, 116 insertions(+), 57 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt index 01beb19213..0e8b30b08e 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/BaseEvaluatorIntegrationShould.kt @@ -215,6 +215,7 @@ internal open class BaseEvaluatorIntegrationShould : BaseMockIntegrationTestEmpt periodStore.insert(period201910) periodStore.insert(period201911) periodStore.insert(period201912) + periodStore.insert(period202001) periodStore.insert(period2019Q4) trackedEntityTypeStore.insert(trackedEntityType) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt index 6c299e9586..48a3e92432 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/IndicatorEvaluatorIntegrationBaseShould.kt @@ -45,6 +45,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEv import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period201912 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019Q4 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period2019SunW25 +import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.period202001 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.program import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.programStage1 import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.BaseEvaluatorSamples.trackedEntityType @@ -262,6 +263,26 @@ internal abstract class IndicatorEvaluatorIntegrationBaseShould : BaseEvaluatorI } } + @Test + fun should_evaluate_year_to_date() { + createDataValue("2", dataElementUid = dataElement1.uid(), periodId = period201910.periodId()!!) + createDataValue("4", dataElementUid = dataElement1.uid(), periodId = period201911.periodId()!!) + createDataValue("8", dataElementUid = dataElement1.uid(), periodId = period201912.periodId()!!) + createDataValue("16", dataElementUid = dataElement1.uid(), periodId = period202001.periodId()!!) + + val indicator = createIndicator(numerator = "${de(dataElement1.uid())}.yearToDate()") + + mapOf( + period201910 to "2.0", + period201911 to "6.0", + period201912 to "14.0", + period202001 to "16.0", + ).forEach { (period, expected) -> + val result = evaluateForAbsolute(indicator, periodId = period.periodId()!!) + assertThat(result).isEqualTo(expected) + } + } + private fun evaluateForThisMonth( indicator: Indicator, aggregationType: AggregationType = AggregationType.DEFAULT 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 index d7e48cff41..364bc342d6 100644 --- 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 @@ -39,6 +39,7 @@ import org.hisp.dhis.android.core.category.CategoryOptionComboCategoryOptionLink 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.parser.internal.expression.QueryMods import org.hisp.dhis.android.core.period.Period import org.hisp.dhis.android.core.period.PeriodTableInfo import org.hisp.dhis.android.core.period.PeriodType @@ -96,7 +97,7 @@ internal object AnalyticsEvaluatorHelper { fun getReportingPeriods( items: List, metadata: Map, - periodOffset: Int? + queryMods: QueryMods? ): List { val periods = mutableListOf().apply { items.forEach { i -> @@ -112,11 +113,22 @@ internal object AnalyticsEvaluatorHelper { } } } - return if (periodOffset != null && periodOffset != 0) { - AnalyticsPeriodHelper.shiftPeriods(periods, periodOffset) - } else { - periods - } + + return periods + .run { + if (queryMods?.periodOffset?.let { it != 0 } == true) { + AnalyticsPeriodHelper.shiftPeriods(this, queryMods.periodOffset!!) + } else { + this + } + } + .run { + if (queryMods?.yearToDate == true) { + this.flatMap { AnalyticsPeriodHelper.getPeriodsToDate(it) } + } else { + this + } + }.distinctBy { it.periodId() } } fun getReportingPeriodsForAggregationType( @@ -177,15 +189,14 @@ internal object AnalyticsEvaluatorHelper { fun getPeriodsFromDate(startDate: String): String { return "SELECT ${PeriodTableInfo.Columns.PERIOD_ID} " + - "FROM ${PeriodTableInfo.TABLE_INFO.name()} " + - "WHERE ${PeriodTableInfo.Columns.START_DATE} >= '$startDate'" + "FROM ${PeriodTableInfo.TABLE_INFO.name()} " + + "WHERE ${PeriodTableInfo.Columns.START_DATE} >= '$startDate'" } - fun getPeriodsToDate(endDate: String): String { return "SELECT ${PeriodTableInfo.Columns.PERIOD_ID} " + - "FROM ${PeriodTableInfo.TABLE_INFO.name()} " + - "WHERE ${PeriodTableInfo.Columns.END_DATE} <= '$endDate'" + "FROM ${PeriodTableInfo.TABLE_INFO.name()} " + + "WHERE ${PeriodTableInfo.Columns.END_DATE} <= '$endDate'" } fun getPeriodWhereClause(columnStart: String, columnEnd: String, period: Period): String { diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt index 40cbed298a..fcdffed74c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/AnalyticsPeriodHelper.kt @@ -54,4 +54,14 @@ object AnalyticsPeriodHelper { return ParserUtils.getTrailingDigits(lastPeriod.periodId()!!)!! } -} \ No newline at end of file + + fun getPeriodsToDate(period: Period): List { + val periodInYear = ParserUtils.getPeriodInYear(period) + + val periodsExcludingEnd = (1 until periodInYear).map { + periodGenerator.generatePeriod(period.periodType()!!, period.startDate()!!, -it)!! + } + + return periodsExcludingEnd + period + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt index 2b05855dce..054c24feba 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/DataElementSQLEvaluator.kt @@ -38,8 +38,8 @@ 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 -import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.android.core.datavalue.DataValueTableInfo.Columns as dvColumns +import org.hisp.dhis.android.core.parser.internal.expression.QueryMods import org.hisp.dhis.android.core.period.PeriodTableInfo import org.hisp.dhis.android.core.period.PeriodTableInfo.Columns as peColumns @@ -185,7 +185,7 @@ internal class DataElementSQLEvaluator @Inject constructor( aggregationType: AggregationType, queryMods: QueryMods?, ): WhereClauseBuilder { - val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(items, metadata, queryMods?.periodOffset) + val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(items, metadata, queryMods) val periods = AnalyticsEvaluatorHelper.getReportingPeriodsForAggregationType(reportingPeriods, aggregationType) return builder.appendInSubQuery( diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt index 1af35f12c9..cef002f384 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/EventDataItemSQLEvaluator.kt @@ -230,7 +230,7 @@ internal class EventDataItemSQLEvaluator @Inject constructor( aggregation: AggregationType, queryMods: QueryMods?, ): WhereClauseBuilder { - val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(items, metadata, queryMods?.periodOffset) + val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(items, metadata, queryMods) return if (reportingPeriods.isEmpty()) { builder diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt index af1b241d6b..b33024dcf9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/internal/evaluator/ProgramIndicatorEvaluatorHelper.kt @@ -206,6 +206,7 @@ internal object ProgramIndicatorEvaluatorHelper { }.build() } + @Suppress("LongParameterList") private fun appendProgramIndicatorPeriodClauses( dimensions: List, metadata: Map, @@ -220,7 +221,7 @@ internal object ProgramIndicatorEvaluatorHelper { column = defaultColumn, dimensions = dimensions, metadata = metadata, - periodOffset = queryMods?.periodOffset + queryMods = queryMods ) ) } else { @@ -234,9 +235,9 @@ internal object ProgramIndicatorEvaluatorHelper { column: String, dimensions: List, metadata: Map, - periodOffset: Int? + queryMods: QueryMods? ): String { - val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(dimensions, metadata, periodOffset) + val reportingPeriods = AnalyticsEvaluatorHelper.getReportingPeriods(dimensions, metadata, queryMods) return WhereClauseBuilder().apply { reportingPeriods.forEach { period -> diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt index 862db3b7e2..dcc048c73b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/expression/ParserUtils.kt @@ -27,22 +27,22 @@ */ package org.hisp.dhis.android.core.parser.internal.expression -import kotlinx.datetime.LocalDate -import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.AnalyticsPeriodHelper import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern import kotlin.math.pow import kotlin.math.roundToInt +import kotlinx.datetime.LocalDate +import org.hisp.dhis.android.core.arch.helpers.DateUtils import org.hisp.dhis.android.core.parser.internal.expression.function.* import org.hisp.dhis.android.core.parser.internal.expression.operator.* import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemConstant import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemDays import org.hisp.dhis.android.core.parser.internal.service.dataitem.ItemOrgUnitGroup +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.PeriodType import org.hisp.dhis.antlr.ParserExceptionWithoutContext import org.hisp.dhis.parser.expression.antlr.ExpressionParser -import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext -import kotlin.math.exp internal object ParserUtils { const val DOUBLE_VALUE_IF_NULL = 0.0 @@ -143,6 +143,7 @@ internal object ParserUtils { } } + @Suppress("TooGenericExceptionCaught") fun parseExpressionDate(expression: String): LocalDate { return try { val tokens = expression.split('-') @@ -156,4 +157,21 @@ internal object ParserUtils { fun getTrailingDigits(periodId: String): Int? { return trailingPeriodDigits.find(periodId)?.value?.toInt() } + + @Suppress("MagicNumber") + fun getPeriodInYear(period: Period): Int { + val periodIsoDate = DateUtils.SIMPLE_DATE_FORMAT.format(period.startDate()!!) + + return when (period.periodType()!!) { + PeriodType.Daily -> LocalDate.parse(periodIsoDate).dayOfYear + PeriodType.Monthly, + PeriodType.BiMonthly -> period.periodId()!!.substring(4, 6).toInt() + PeriodType.Yearly, + PeriodType.FinancialApril, + PeriodType.FinancialJuly, + PeriodType.FinancialNov, + PeriodType.FinancialOct -> 1 + else -> getTrailingDigits(period.periodId()!!) ?: 0 + } + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodInYear.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodInYear.kt index 6b9d7b5603..f60bc6deb2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodInYear.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemPeriodInYear.kt @@ -27,26 +27,11 @@ */ package org.hisp.dhis.android.core.parser.internal.service.dataitem -import kotlinx.datetime.LocalDate -import org.hisp.dhis.android.core.arch.helpers.DateUtils import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.period.Period -import org.hisp.dhis.android.core.period.PeriodType internal class ItemPeriodInYear : ItemPeriodBase() { override fun evaluate(period: Period): Double { - val periodIsoDate = DateUtils.SIMPLE_DATE_FORMAT.format(period.startDate()!!) - - return when (period.periodType()!!) { - PeriodType.Daily -> LocalDate.parse(periodIsoDate).dayOfYear - PeriodType.Monthly, - PeriodType.BiMonthly -> period.periodId()!!.substring(4, 6).toInt() - PeriodType.Yearly, - PeriodType.FinancialApril, - PeriodType.FinancialJuly, - PeriodType.FinancialNov, - PeriodType.FinancialOct -> 1 - else -> ParserUtils.getTrailingDigits(period.periodId()!!) ?: 0 - }.toDouble() + return ParserUtils.getPeriodInYear(period).toDouble() } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemYearlyPeriodCount.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemYearlyPeriodCount.kt index 7f841b701a..cf449b08ca 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemYearlyPeriodCount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/dataitem/ItemYearlyPeriodCount.kt @@ -31,6 +31,7 @@ import org.hisp.dhis.android.core.analytics.aggregated.internal.evaluator.Analyt import org.hisp.dhis.android.core.period.Period import org.hisp.dhis.android.core.period.PeriodType +@Suppress("MagicNumber") internal class ItemYearlyPeriodCount : ItemPeriodBase() { override fun evaluate(period: Period): Double { return when (period.periodType()!!) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorEngineImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorEngineImpl.kt index 08ae2f726d..60e91d5330 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorEngineImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorEngineImpl.kt @@ -58,6 +58,7 @@ internal class ProgramIndicatorEngineImpl @Inject constructor( private val programStageStore: IdentifiableObjectStore ) : ProgramIndicatorEngine { + @Deprecated("Deprecated in Java") override fun getProgramIndicatorValue( enrollmentUid: String?, eventUid: String?, diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt index 89ad4761ff..ba72eaeec2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorSQLExecutor.kt @@ -77,7 +77,7 @@ internal class ProgramIndicatorSQLExecutor @Inject constructor( ): String { val programIndicator = ProgramIndicatorEvaluatorHelper.getProgramIndicator(evaluationItem, metadata) val periodItems = evaluationItem.allDimensionItems.filterIsInstance() - val periods = AnalyticsEvaluatorHelper.getReportingPeriods(periodItems, metadata, queryMods?.periodOffset) + val periods = AnalyticsEvaluatorHelper.getReportingPeriods(periodItems, metadata, queryMods) if (programIndicator.expression() == null) { throw IllegalArgumentException("Program Indicator ${programIndicator.uid()} has empty expression.") @@ -92,9 +92,19 @@ internal class ProgramIndicatorSQLExecutor @Inject constructor( val contextWhereClause = when (programIndicator.analyticsType()) { AnalyticsType.EVENT -> - ProgramIndicatorEvaluatorHelper.getEventWhereClause(programIndicator, evaluationItem, metadata, queryMods) + ProgramIndicatorEvaluatorHelper.getEventWhereClause( + programIndicator, + evaluationItem, + metadata, + queryMods + ) AnalyticsType.ENROLLMENT, null -> - ProgramIndicatorEvaluatorHelper.getEnrollmentWhereClause(programIndicator, evaluationItem, metadata, queryMods) + ProgramIndicatorEvaluatorHelper.getEnrollmentWhereClause( + programIndicator, + evaluationItem, + metadata, + queryMods + ) } val context = ProgramIndicatorSQLContext( diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt index a940033c6b..b7ce2a031b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseCollectionRepository.kt @@ -44,16 +44,16 @@ class StockUseCaseCollectionRepository @Inject internal constructor( scope: RepositoryScope, transformer: TwoWayTransformer, ) : ReadOnlyWithUidCollectionRepository by - ReadOnlyWithUidAndTransformerCollectionRepositoryImpl( - store, - childrenAppenders, - scope, - FilterConnectorFactory(scope) { s: RepositoryScope -> - StockUseCaseCollectionRepository(store, childrenAppenders, s, transformer) - }, - transformer - ) { +ReadOnlyWithUidAndTransformerCollectionRepositoryImpl( + store, + childrenAppenders, + scope, + FilterConnectorFactory(scope) { s: RepositoryScope -> + StockUseCaseCollectionRepository(store, childrenAppenders, s, transformer) + }, + transformer +) { private val cf: FilterConnectorFactory = FilterConnectorFactory(scope) { s: RepositoryScope -> StockUseCaseCollectionRepository(store, childrenAppenders, s, transformer) diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt index 23b3621f20..4025f8df4c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseEntityDIModule.kt @@ -68,8 +68,8 @@ internal class StockUseCaseEntityDIModule { @Reusable fun childrenAppenders(linkStore: LinkStore): Map> { - val childrenAppender: ChildrenAppender = - StockUseCaseTransactionChildrenAppender(linkStore) - return mapOf(Pair(InternalStockUseCase.TRANSACTIONS, childrenAppender)) - } + val childrenAppender: ChildrenAppender = + StockUseCaseTransactionChildrenAppender(linkStore) + return mapOf(Pair(InternalStockUseCase.TRANSACTIONS, childrenAppender)) + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionEntityDIModule.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionEntityDIModule.kt index 1399d2929f..356a9014be 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionEntityDIModule.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/internal/StockUseCaseTransactionEntityDIModule.kt @@ -48,6 +48,6 @@ internal class StockUseCaseTransactionEntityDIModule { @Reusable fun handler(store: LinkStore): LinkHandler { - return LinkHandlerImpl(store) - } + return LinkHandlerImpl(store) + } } From 508174a27e773ea7c8afb9c8b47f042b69a462fb Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 22 Dec 2022 14:06:05 +1100 Subject: [PATCH 027/201] [ANDROSDK-1569] Upgrade kotlin version 1.7.21; gradle plugin --- build.gradle | 4 +-- core/build.gradle | 31 +++++++------------ core/src/androidTest/AndroidManifest.xml | 3 +- core/src/main/AndroidManifest.xml | 3 +- core/src/test/AndroidManifest.xml | 3 +- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- instrumented-test-app/build.gradle | 1 + .../src/main/AndroidManifest.xml | 3 +- 9 files changed, 20 insertions(+), 32 deletions(-) diff --git a/build.gradle b/build.gradle index d8bddd0e79..97ab501253 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.21' repositories { google() mavenLocal() @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath "com.android.tools.build:gradle:7.0.4" + classpath 'com.android.tools.build:gradle:7.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jlleitschuh.gradle:ktlint-gradle:11.0.0" classpath "org.jacoco:org.jacoco.core:0.8.8" diff --git a/core/build.gradle b/core/build.gradle index 440a29cad9..750628dbb4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -94,43 +94,28 @@ def libraries = project.ext.libraries android { compileSdkVersion configuration.targetSdkVersion - buildToolsVersion configuration.buildToolsVersion defaultConfig { minSdkVersion configuration.minSdkVersion targetSdkVersion configuration.targetSdkVersion - versionCode configuration.versionCode - versionName configuration.versionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true vectorDrawables.useSupportLibrary = true - buildConfigField("long", "VERSION_CODE", "${defaultConfig.versionCode}") - buildConfigField("String","VERSION_NAME","\"${defaultConfig.versionName}\"") + buildConfigField("long", "VERSION_CODE", "${configuration.versionCode}") + buildConfigField("String","VERSION_NAME","\"${configuration.versionName}\"") } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } - packagingOptions { - // excluding duplicate license files - // from jackson modules - exclude "META-INF/LICENSE" - - // should be removed with interoperability library - exclude "META-INF/rxjava.properties" + resources { + excludes += ['META-INF/LICENSE', 'META-INF/rxjava.properties'] + } } - lintOptions { - // casting 'InvalidPackage' error to warning - warning "InvalidPackage" - disable 'MissingTranslation' - - // Fail early. - abortOnError true - } buildTypes { debug { @@ -153,6 +138,12 @@ android { testOptions { unitTests.returnDefaultValues = true } + lint { + abortOnError true + disable 'MissingTranslation' + warning 'InvalidPackage' + } + namespace 'org.hisp.dhis.android' } diff --git a/core/src/androidTest/AndroidManifest.xml b/core/src/androidTest/AndroidManifest.xml index bd6e29a3c7..a43c2ca9af 100644 --- a/core/src/androidTest/AndroidManifest.xml +++ b/core/src/androidTest/AndroidManifest.xml @@ -27,8 +27,7 @@ --> + xmlns:tools="http://schemas.android.com/tools"> diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml index 8f2bcf4453..adb13dbea3 100644 --- a/core/src/main/AndroidManifest.xml +++ b/core/src/main/AndroidManifest.xml @@ -27,8 +27,7 @@ --> + xmlns:tools="http://schemas.android.com/tools"> - + diff --git a/gradle.properties b/gradle.properties index 0308d37ab5..87c1fb1833 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx8g -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # Enable AndroidX android.useAndroidX=true # Enable Gradle Daemon diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 05679dc3c1..41dfb87909 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/instrumented-test-app/build.gradle b/instrumented-test-app/build.gradle index 2980882997..bec58ee0f9 100644 --- a/instrumented-test-app/build.gradle +++ b/instrumented-test-app/build.gradle @@ -50,6 +50,7 @@ android { testCoverageEnabled = true } } + namespace 'org.hisp.dhis.android.instrumentedTestApp' } dependencies { diff --git a/instrumented-test-app/src/main/AndroidManifest.xml b/instrumented-test-app/src/main/AndroidManifest.xml index 754141da05..9676a8f342 100644 --- a/instrumented-test-app/src/main/AndroidManifest.xml +++ b/instrumented-test-app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + From c1669a50adde5451c57a2a1960cd5eb4ad546ff2 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Thu, 22 Dec 2022 16:12:31 +1100 Subject: [PATCH 028/201] [ANDROSDK-1569] Ktlint --- Jenkinsfile | 2 +- build.gradle | 2 +- core/build.gradle | 2 +- .../core/EventWithLimitCallMockIntegrationShould.kt | 6 +++--- ...mIndicatorBoundariesSQLExecutorIntegrationShould.kt | 2 +- .../ProgramIndicatorEngineIntegrationShould.kt | 2 +- .../ProgramIndicatorSQLExecutorIntegrationShould.kt | 2 +- .../dbgeneration/LocalAnalyticsDataGenerator.kt | 10 +++++----- .../dbgeneration/LocalAnalyticsDatabaseFiller.kt | 6 +++--- .../dbgeneration/LocalAnalyticsMetadataGenerator.kt | 6 +++--- .../dbgeneration/LocalAnalyticsParams.kt | 6 +++--- ...eLocalAnalyticsDatabaseSizeMockIntegrationShould.kt | 6 +++--- ...AnalyticsSuperLargeDatabaseMockIntegrationShould.kt | 6 +++--- ...aseLocalAnalyticsAggregatedMockIntegrationShould.kt | 6 +++--- .../localanalytics/tests/BaseLocalAnalyticsTest.kt | 6 +++--- .../BaseLocalAnalyticsTrackerMockIntegrationShould.kt | 6 +++--- ...lyticsAggregatedDefaultDataMockIntegrationShould.kt | 6 +++--- ...nalyticsAggregatedLargeDataMockIntegrationShould.kt | 6 +++--- ...icsAggregatedSuperLargeDataMockIntegrationShould.kt | 6 +++--- ...ocalAnalyticsTrackerDefaultMockIntegrationShould.kt | 6 +++--- .../LocalAnalyticsTrackerLargeMockIntegrationShould.kt | 6 +++--- ...lAnalyticsTrackerSuperLargeMockIntegrationShould.kt | 6 +++--- .../testapp/arch/helpers/FileResizerHelperShould.kt | 6 +++--- ...nstanceCollectionRepositoryMockIntegrationShould.kt | 6 +++--- ...SummaryCollectionRepositoryMockIntegrationShould.kt | 6 +++--- ...taValueCollectionRepositoryMockIntegrationShould.kt | 6 +++--- .../DataValueObjectRepositoryMockIntegrationShould.kt | 6 +++--- .../FileResourceAddMockIntegrationShould.kt | 6 +++--- ...esourceCollectionRepositoryMockIntegrationShould.kt | 6 +++--- ...llectionRepositoryMockEnqueableIntegrationShould.kt | 6 +++--- .../BaseTrackerConflictMockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1000MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1006MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1007MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1008MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1009MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1063MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1064MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1069MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1081MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1084MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1100MockIntegrationShould.kt | 6 +++--- .../tracker/importer/E1103MockIntegrationShould.kt | 6 +++--- .../TrackerConflictHelperMockIntegrationShould.kt | 6 +++--- .../analytics/aggregated/mock/AggregatedSamples.kt | 6 +++--- .../ReadOnlyWithTransformerCollectionRepositoryImpl.kt | 4 ++-- ...nlyWithUidAndTransformerCollectionRepositoryImpl.kt | 4 ++-- .../ReadOnlyWithTransformerObjectRepositoryImpl.kt | 2 +- .../core/settings/internal/AnalyticsTeiSettingStore.kt | 4 ++-- .../core/settings/internal/GeneralSettingStore.kt | 4 ++-- .../settings/internal/SynchronizationSettingStore.kt | 4 ++-- .../core/user/openid/OpenIDConnectTokenRefresher.kt | 2 +- .../core/tracker/importer/JobReportSuccessShould.kt | 6 +++++- 53 files changed, 142 insertions(+), 138 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7471ee1e66..e17687f5c6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -63,7 +63,7 @@ pipeline { expression { env.CHANGE_ID == null } anyOf { expression { env.GIT_BRANCH == "master" } - expression { env.GIT_BRANCH == "1.7.1-rc" } + expression { env.GIT_BRANCH == "develop" } } } } diff --git a/build.gradle b/build.gradle index 97ab501253..a1b37e8d56 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,7 @@ subprojects { project -> apply plugin: "org.jlleitschuh.gradle.ktlint" ktlint { - version = "0.41.0" + version = "0.45.2" android = true outputColorName = "RED" reporters { diff --git a/core/build.gradle b/core/build.gradle index 750628dbb4..bf8db5e0ff 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -59,7 +59,7 @@ ext { autoValueCursor : "2.0.1", retrofit : "2.9.0", okHttp : "3.12.0", - dagger : "2.39.1", + dagger : "2.44.2", rxJava : "2.2.12", rxAndroid : "2.1.1", sqlCipher : "4.4.3", 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 f0b133e5fd..f61726b948 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorBoundariesSQLExecutorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorBoundariesSQLExecutorIntegrationShould.kt index b2327013ac..d6c5f2fb7d 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorBoundariesSQLExecutorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorBoundariesSQLExecutorIntegrationShould.kt @@ -52,10 +52,10 @@ import org.hisp.dhis.android.core.common.AnalyticsType import org.hisp.dhis.android.core.period.PeriodType import org.hisp.dhis.android.core.program.AnalyticsPeriodBoundary import org.hisp.dhis.android.core.program.AnalyticsPeriodBoundaryType -import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.`var` import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.att import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.de import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.psEventDate +import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.`var` import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.Test import org.junit.runner.RunWith diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorEngineIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorEngineIntegrationShould.kt index 2efea4235f..3b0fc3b566 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorEngineIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorEngineIntegrationShould.kt @@ -45,11 +45,11 @@ import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.program.ProgramStage import org.hisp.dhis.android.core.program.internal.ProgramStageStore import org.hisp.dhis.android.core.program.internal.ProgramStore -import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.`var` import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.att import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.de import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.today import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.twoDaysBefore +import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.`var` import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute import org.hisp.dhis.android.core.trackedentity.TrackedEntityType import org.hisp.dhis.android.core.trackedentity.internal.TrackedEntityAttributeStore diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt index 3da3aaeac4..68992472b6 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/program/programindicatorengine/ProgramIndicatorSQLExecutorIntegrationShould.kt @@ -53,10 +53,10 @@ import org.hisp.dhis.android.core.common.AggregationType import org.hisp.dhis.android.core.common.AnalyticsType import org.hisp.dhis.android.core.enrollment.EnrollmentStatus import org.hisp.dhis.android.core.event.EventStatus -import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.`var` import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.att import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.cons import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.de +import org.hisp.dhis.android.core.program.programindicatorengine.BaseTrackerDataIntegrationHelper.Companion.`var` import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.Test import org.junit.runner.RunWith 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 593238125f..9031930b93 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -61,14 +61,14 @@ internal class LocalAnalyticsDataGenerator(private val params: LocalAnalyticsDat .groupBy { de -> de.categoryCombo() } val periodOrgUnits = metadata.periods.flatMap { - period -> + period -> orgUnits.map { ou -> Pair(period, ou) } } val iterations = params.dataValues / 100 return categoryOptionCombosByCategoryCombo.flatMap { - (categoryCombo, categoryOptionCombos) -> + (categoryCombo, categoryOptionCombos) -> categoryOptionCombos.flatMap { categoryOptionCombo -> dataElementsByCategoryCombo[categoryCombo]!!.flatMap { dataElement -> (0 until iterations).map { 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 4756de3e64..f495eb5ce8 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsMetadataGenerator.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsMetadataGenerator.kt index 8a462edef6..171f2b7c1a 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsParams.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbgeneration/LocalAnalyticsParams.kt index 7407d5c946..c07903f52f 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/BaseLocalAnalyticsDatabaseSizeMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/BaseLocalAnalyticsDatabaseSizeMockIntegrationShould.kt index 95727aa60c..b8348d17f5 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/LocalAnalyticsSuperLargeDatabaseMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/dbtests/LocalAnalyticsSuperLargeDatabaseMockIntegrationShould.kt index a6b291a885..31aa96aa51 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsAggregatedMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsAggregatedMockIntegrationShould.kt index 584db4591d..05ef156849 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTest.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTest.kt index 462dd8dc3e..4845e7cfb8 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTrackerMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/BaseLocalAnalyticsTrackerMockIntegrationShould.kt index 64c7dd2dd9..abbbf1b60d 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedDefaultDataMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedDefaultDataMockIntegrationShould.kt index 5aaba825fb..d6d4dfcc80 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedLargeDataMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedLargeDataMockIntegrationShould.kt index 27cc1fe877..bca63931e1 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedSuperLargeDataMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsAggregatedSuperLargeDataMockIntegrationShould.kt index 3a17357ba2..1d345629ec 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerDefaultMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerDefaultMockIntegrationShould.kt index da29a429a5..2ad9dbb197 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerLargeMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerLargeMockIntegrationShould.kt index 5495b7b2c8..c208faebf3 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerSuperLargeMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/localanalytics/tests/LocalAnalyticsTrackerSuperLargeMockIntegrationShould.kt index 9bf40625d5..95742f8ff1 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/arch/helpers/FileResizerHelperShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/arch/helpers/FileResizerHelperShould.kt index d440a875c7..60c7c4b3c2 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/arch/helpers/FileResizerHelperShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/arch/helpers/FileResizerHelperShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceCollectionRepositoryMockIntegrationShould.kt index 3a8eb26ca3..bddfceadfd 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceCollectionRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceCollectionRepositoryMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceSummaryCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceSummaryCollectionRepositoryMockIntegrationShould.kt index 140c79fa6b..058fc7430c 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceSummaryCollectionRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/dataset/DataSetInstanceSummaryCollectionRepositoryMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueCollectionRepositoryMockIntegrationShould.kt index 2eac25a447..64032de61e 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueCollectionRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueCollectionRepositoryMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueObjectRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueObjectRepositoryMockIntegrationShould.kt index 3aa80dcd61..8407dad71c 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueObjectRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueObjectRepositoryMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceAddMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceAddMockIntegrationShould.kt index 78575721c2..201f0fd85b 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceAddMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceAddMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.kt index 459581b052..2b5231f08a 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/fileresource/FileResourceCollectionRepositoryMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockEnqueableIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockEnqueableIntegrationShould.kt index 08e223894a..d92467d4de 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockEnqueableIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockEnqueableIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/BaseTrackerConflictMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/BaseTrackerConflictMockIntegrationShould.kt index c83383055c..b6b06fa5a4 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/BaseTrackerConflictMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/BaseTrackerConflictMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1000MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1000MockIntegrationShould.kt index 36f78db97a..474855bfcf 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1000MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1000MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1006MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1006MockIntegrationShould.kt index cfaa83eab5..9208e0ac07 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1006MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1006MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1007MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1007MockIntegrationShould.kt index 60b103cd67..2c5c731880 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1007MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1007MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1008MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1008MockIntegrationShould.kt index 88e46cd645..e0c7f202f9 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1008MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1008MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1009MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1009MockIntegrationShould.kt index 5e3bc84870..ace54e38bb 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1009MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1009MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1063MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1063MockIntegrationShould.kt index 779f9b669d..143267cf2b 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1063MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1063MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1064MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1064MockIntegrationShould.kt index 413d048495..83c56e4028 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1064MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1064MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1069MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1069MockIntegrationShould.kt index 0f14dde191..40408498f8 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1069MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1069MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1081MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1081MockIntegrationShould.kt index d9f748dfc5..98e4be27d1 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1081MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1081MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1084MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1084MockIntegrationShould.kt index eb35b34742..a82549501c 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1084MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1084MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1100MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1100MockIntegrationShould.kt index 32352d2953..fd410682e0 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1100MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1100MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1103MockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1103MockIntegrationShould.kt index c770e7fde5..223a104a60 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1103MockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/E1103MockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/TrackerConflictHelperMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/TrackerConflictHelperMockIntegrationShould.kt index 6ea33abbf1..d1598c2a72 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/TrackerConflictHelperMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/tracker/importer/TrackerConflictHelperMockIntegrationShould.kt @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/AggregatedSamples.kt b/core/src/main/java/org/hisp/dhis/android/core/analytics/aggregated/mock/AggregatedSamples.kt index 6f6da13047..41dc2db938 100644 --- 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 @@ -1,19 +1,19 @@ /* * Copyright (c) 2004-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithTransformerCollectionRepositoryImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithTransformerCollectionRepositoryImpl.kt index 9ec5b5e6d2..3090e27f4d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithTransformerCollectionRepositoryImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithTransformerCollectionRepositoryImpl.kt @@ -36,12 +36,12 @@ import org.hisp.dhis.android.core.arch.db.querybuilders.internal.OrderByClauseBu import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder import org.hisp.dhis.android.core.arch.db.stores.internal.ReadableStore import org.hisp.dhis.android.core.arch.handlers.internal.TwoWayTransformer -import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyObjectRepository -import org.hisp.dhis.android.core.arch.repositories.`object`.internal.ReadOnlyWithTransformerObjectRepositoryImpl import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppenderExecutor import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyCollectionRepository import org.hisp.dhis.android.core.arch.repositories.filters.internal.FilterConnectorFactory +import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyObjectRepository +import org.hisp.dhis.android.core.arch.repositories.`object`.internal.ReadOnlyWithTransformerObjectRepositoryImpl import org.hisp.dhis.android.core.arch.repositories.paging.internal.RepositoryDataSourceWithTransformer import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope import org.hisp.dhis.android.core.arch.repositories.scope.internal.WhereClauseFromScopeBuilder diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithUidAndTransformerCollectionRepositoryImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithUidAndTransformerCollectionRepositoryImpl.kt index 59bf22fe3f..1f66b6b0ce 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithUidAndTransformerCollectionRepositoryImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/collection/internal/ReadOnlyWithUidAndTransformerCollectionRepositoryImpl.kt @@ -31,12 +31,12 @@ import io.reactivex.Single import org.hisp.dhis.android.core.arch.db.querybuilders.internal.OrderByClauseBuilder import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.handlers.internal.TwoWayTransformer -import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyObjectRepository -import org.hisp.dhis.android.core.arch.repositories.`object`.internal.ReadOnlyWithTransformerObjectRepositoryImpl import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyCollectionRepository import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithUidCollectionRepository import org.hisp.dhis.android.core.arch.repositories.filters.internal.FilterConnectorFactory +import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyObjectRepository +import org.hisp.dhis.android.core.arch.repositories.`object`.internal.ReadOnlyWithTransformerObjectRepositoryImpl import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope import org.hisp.dhis.android.core.arch.repositories.scope.internal.RepositoryScopeHelper import org.hisp.dhis.android.core.common.CoreObject diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt index 3d32b15037..cb2e18039b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt @@ -31,9 +31,9 @@ import io.reactivex.Single import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder import org.hisp.dhis.android.core.arch.db.stores.internal.ReadableStore import org.hisp.dhis.android.core.arch.handlers.internal.TwoWayTransformer -import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyObjectRepository import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppenderExecutor +import org.hisp.dhis.android.core.arch.repositories.`object`.ReadOnlyObjectRepository import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope import org.hisp.dhis.android.core.arch.repositories.scope.internal.WhereClauseFromScopeBuilder diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsTeiSettingStore.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsTeiSettingStore.kt index e092bcfcff..bbb19b1d56 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsTeiSettingStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/AnalyticsTeiSettingStore.kt @@ -51,11 +51,11 @@ internal object AnalyticsTeiSettingStore { } private val WHERE_UPDATE_BINDER = WhereStatementBinder { - _: AnalyticsTeiSetting, _: StatementWrapper -> + _: AnalyticsTeiSetting, _: StatementWrapper -> } private val WHERE_DELETE_BINDER = WhereStatementBinder { - _: AnalyticsTeiSetting, _: StatementWrapper -> + _: AnalyticsTeiSetting, _: StatementWrapper -> } fun create(databaseAdapter: DatabaseAdapter): ObjectWithoutUidStore { diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/GeneralSettingStore.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/GeneralSettingStore.kt index 7b47545539..0df399a984 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/GeneralSettingStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/GeneralSettingStore.kt @@ -52,11 +52,11 @@ internal object GeneralSettingStore { } private val WHERE_UPDATE_BINDER = WhereStatementBinder { - _: GeneralSettings, _: StatementWrapper -> + _: GeneralSettings, _: StatementWrapper -> } private val WHERE_DELETE_BINDER = WhereStatementBinder { - _: GeneralSettings, _: StatementWrapper -> + _: GeneralSettings, _: StatementWrapper -> } fun create(databaseAdapter: DatabaseAdapter): ObjectWithoutUidStore { diff --git a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SynchronizationSettingStore.kt b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SynchronizationSettingStore.kt index 6f3381cd17..a27c4b31ea 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SynchronizationSettingStore.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/settings/internal/SynchronizationSettingStore.kt @@ -46,11 +46,11 @@ internal object SynchronizationSettingStore { } private val WHERE_UPDATE_BINDER = WhereStatementBinder { - _: SynchronizationSettings, _: StatementWrapper -> + _: SynchronizationSettings, _: StatementWrapper -> } private val WHERE_DELETE_BINDER = WhereStatementBinder { - _: SynchronizationSettings, _: StatementWrapper -> + _: SynchronizationSettings, _: StatementWrapper -> } fun create(databaseAdapter: DatabaseAdapter?): ObjectWithoutUidStore { diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/openid/OpenIDConnectTokenRefresher.kt b/core/src/main/java/org/hisp/dhis/android/core/user/openid/OpenIDConnectTokenRefresher.kt index 082c89e6dc..28a182f94c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/openid/OpenIDConnectTokenRefresher.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/openid/OpenIDConnectTokenRefresher.kt @@ -46,7 +46,7 @@ internal class OpenIDConnectTokenRefresher @Inject constructor( val service = AuthorizationService(context) return Single.create { authState.performActionWithFreshTokens(service) { - _: String?, idToken: String?, ex: AuthorizationException? -> + _: String?, idToken: String?, ex: AuthorizationException? -> service.dispose() if (idToken != null) { it.onSuccess(idToken) 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 15fd961b48..81c9c0c77b 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,7 +33,11 @@ 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.* +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.TrackerImporterObjectType import org.junit.Test class JobReportSuccessShould : BaseObjectShould("tracker/importer/jobreport-success.json"), ObjectShould { From 0ace62105bdc05d3b98a0ad01b949022dbee26d4 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Fri, 23 Dec 2022 10:10:28 +1100 Subject: [PATCH 029/201] [ANDROSDK-1569] Migrate validation executor --- .../DataSetIndicatorEngineImpl.kt | 16 +- .../DataSetIndicatorEvaluator.kt | 15 +- .../internal/service/ExpressionService.kt | 84 ++++----- .../service/ExpressionServiceContext.kt | 39 ++++ ...idationEngine.java => ValidationEngine.kt} | 29 +-- ...java => ValidationEngineEntityDIModule.kt} | 21 +-- .../engine/internal/ValidationEngineImpl.java | 168 ------------------ .../engine/internal/ValidationEngineImpl.kt | 145 +++++++++++++++ .../engine/internal/ValidationExecutor.java | 150 ---------------- .../engine/internal/ValidationExecutor.kt | 129 ++++++++++++++ .../DataSetIndicatorEvaluatorShould.kt | 66 ++++--- .../expression/ExpressionServiceShould.kt | 62 +++---- docs/content/developer/analytics.md | 88 ++++----- 13 files changed, 501 insertions(+), 511 deletions(-) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionServiceContext.kt rename core/src/main/java/org/hisp/dhis/android/core/validation/engine/{ValidationEngine.java => ValidationEngine.kt} (84%) rename core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/{ValidationEngineEntityDIModule.java => ValidationEngineEntityDIModule.kt} (85%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineImpl.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineImpl.kt delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationExecutor.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationExecutor.kt diff --git a/core/src/main/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEngineImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEngineImpl.kt index 1eac370488..8e8a94251a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEngineImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEngineImpl.kt @@ -40,6 +40,7 @@ import org.hisp.dhis.android.core.indicator.IndicatorCollectionRepository import org.hisp.dhis.android.core.indicator.IndicatorTypeCollectionRepository import org.hisp.dhis.android.core.organisationunit.OrganisationUnitOrganisationUnitGroupLink import org.hisp.dhis.android.core.organisationunit.OrganisationUnitOrganisationUnitGroupLinkTableInfo +import org.hisp.dhis.android.core.parser.internal.service.ExpressionServiceContext import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject import org.hisp.dhis.android.core.parser.internal.service.utils.ExpressionHelper import org.hisp.dhis.android.core.period.Period @@ -78,18 +79,17 @@ internal class DataSetIndicatorEngineImpl @Inject constructor( val indicator = indicatorRepository.uid(indicatorUid).blockingGet() val indicatorType = indicatorTypeRepository.uid(indicator.indicatorType()?.uid()).blockingGet() - val valueMap = getValueMap(dataSetUid, attributeOptionComboUid, orgUnitUid, periodId) - val constantMap = getConstantMap() - val orgunitGroupCountMap = getOrgunitGroupMap() - val period = getPeriod(periodId) + val context = ExpressionServiceContext( + valueMap = getValueMap(dataSetUid, attributeOptionComboUid, orgUnitUid, periodId), + constantMap = getConstantMap(), + orgUnitCountMap = getOrgunitGroupMap(), + days = PeriodHelper.getDays(getPeriod(periodId)) + ) return dataSetIndicatorEvaluator.evaluate( indicator = indicator, indicatorType = indicatorType, - valueMap = valueMap, - constantMap = constantMap, - orgUnitCountMap = orgunitGroupCountMap, - days = PeriodHelper.getDays(period) + context = context ) } diff --git a/core/src/main/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEvaluator.kt b/core/src/main/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEvaluator.kt index e441bf4995..88a0e55719 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEvaluator.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEvaluator.kt @@ -29,34 +29,27 @@ package org.hisp.dhis.android.core.indicator.datasetindicatorengine import dagger.Reusable import javax.inject.Inject -import org.hisp.dhis.android.core.constant.Constant import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils import org.hisp.dhis.android.core.parser.internal.service.ExpressionService -import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject +import org.hisp.dhis.android.core.parser.internal.service.ExpressionServiceContext import org.hisp.dhis.android.core.validation.MissingValueStrategy @Reusable internal class DataSetIndicatorEvaluator @Inject constructor(private val expressionService: ExpressionService) { - @Suppress("LongParameterList") fun evaluate( indicator: Indicator, indicatorType: IndicatorType, - valueMap: Map, - constantMap: Map, - orgUnitCountMap: Map, - days: Int + context: ExpressionServiceContext ): Double { val numerator = expressionService.getExpressionValue( - indicator.numerator(), valueMap, constantMap, - orgUnitCountMap, days, MissingValueStrategy.NEVER_SKIP + indicator.numerator(), context, MissingValueStrategy.NEVER_SKIP ) as Double val denominator = expressionService.getExpressionValue( - indicator.denominator(), valueMap, constantMap, - orgUnitCountMap, days, MissingValueStrategy.NEVER_SKIP + indicator.denominator(), context, MissingValueStrategy.NEVER_SKIP ) as Double val formula = "$numerator * ${indicatorType.factor() ?: 1} / $denominator" diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt index e58a9bd4ee..6a63990529 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt @@ -48,7 +48,6 @@ import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils.ITEM_RE import org.hisp.dhis.android.core.parser.internal.expression.literal.RegenerateLiteral import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimItemDataElementAndOperand import org.hisp.dhis.android.core.parser.internal.service.dataitem.DimensionalItemId -import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject import org.hisp.dhis.android.core.program.ProgramStage import org.hisp.dhis.android.core.validation.MissingValueStrategy import org.hisp.dhis.parser.expression.antlr.ExpressionParser @@ -71,12 +70,9 @@ internal class ExpressionService @Inject constructor( return if (expression == null) { emptySet() } else { - // TODO REVIEW - val itemIds: MutableSet = HashSet() val visitor = newVisitor(ITEM_GET_IDS, emptyMap()) - visitor.itemIds = itemIds visit(expression, visitor) - itemIds + visitor.itemIds } } @@ -100,102 +96,84 @@ internal class ExpressionService @Inject constructor( } else { val visitor = newVisitor(ITEM_GET_DESCRIPTIONS, constantMap) visit(expression, visitor) - val itemDescriptions = visitor.itemDescriptions - var description: String = expression - for ((key, value) in itemDescriptions) { - description = description.replace(key, value) + visitor.itemDescriptions.entries.fold(expression) { acc, (key, value) -> + acc.replace(key, value) } - description } } fun getExpressionValue(expression: String?): Any? { return getExpressionValue( - expression, emptyMap(), emptyMap(), emptyMap(), 0, MissingValueStrategy.NEVER_SKIP + expression, ExpressionServiceContext(), MissingValueStrategy.NEVER_SKIP ) } - @Suppress("LongParameterList", "ComplexMethod", "ReturnCount") fun getExpressionValue( expression: String?, - valueMap: Map, - constantMap: Map, - orgUnitCountMap: Map, - days: Int?, + context: ExpressionServiceContext, missingValueStrategy: MissingValueStrategy ): Any? { return expression?.let { val visitor = newVisitor( ITEM_EVALUATE, - constantMap + context.constantMap ) - val itemValueMap = valueMap.map { it.key.dimensionItem to it.value }.toMap() + val itemValueMap = context.valueMap.map { it.key.dimensionItem to it.value }.toMap() visitor.itemValueMap = itemValueMap - visitor.orgUnitCountMap = orgUnitCountMap - if (days != null) { - visitor.days = days.toDouble() - } + visitor.orgUnitCountMap = context.orgUnitCountMap + visitor.days = context.days?.toDouble() + val value = visit(expression, visitor) val itemsFound = visitor.state.itemsFound val itemValuesFound = visitor.state.itemValuesFound + val handledValue = + if (value == null) { + 0.0 + } else if (value is Double && value.isNaN()) { + null + } else { + value + } + when (missingValueStrategy) { MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING -> { if (itemValuesFound < itemsFound) { - return null - } - if (itemsFound != 0 && itemValuesFound == 0) { - return null - } - if (value == null) { - // TODO Handle other ParseType - return 0.0 + null + } else { + handledValue } } MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING -> { if (itemsFound != 0 && itemValuesFound == 0) { - return null - } - if (value == null) { - return 0.0 + null + } else { + handledValue } } - MissingValueStrategy.NEVER_SKIP -> if (value == null) { - return 0.0 - } - } - - if (value is Double && value.isNaN()) { - null - } else { - value + MissingValueStrategy.NEVER_SKIP -> handledValue } } } fun regenerateExpression( expression: String?, - valueMap: Map, - constantMap: Map, - orgUnitCountMap: Map, - days: Int? + context: ExpressionServiceContext ): String { return if (expression == null) { "" } else { val visitor = newVisitor( ITEM_REGENERATE, - constantMap + context.constantMap ) - val itemValueMap = valueMap.map { it.key.dimensionItem to it.value }.toMap() + val itemValueMap = context.valueMap.map { it.key.dimensionItem to it.value }.toMap() visitor.itemValueMap = itemValueMap - visitor.orgUnitCountMap = orgUnitCountMap + visitor.orgUnitCountMap = context.orgUnitCountMap visitor.setExpressionLiteral(RegenerateLiteral()) - if (days != null) { - visitor.days = days.toDouble() - } + visitor.days = context.days?.toDouble() visit(expression, visitor) as String } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionServiceContext.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionServiceContext.kt new file mode 100644 index 0000000000..1d10035acf --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionServiceContext.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.hisp.dhis.android.core.parser.internal.service + +import org.hisp.dhis.android.core.constant.Constant +import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject + +internal data class ExpressionServiceContext( + val valueMap: Map = emptyMap(), + val constantMap: Map = emptyMap(), + val orgUnitCountMap: Map = emptyMap(), + val days: Int? = 0, +) diff --git a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/ValidationEngine.java b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/ValidationEngine.kt similarity index 84% rename from core/src/main/java/org/hisp/dhis/android/core/validation/engine/ValidationEngine.java rename to core/src/main/java/org/hisp/dhis/android/core/validation/engine/ValidationEngine.kt index 38d3594211..d4b33c5612 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/ValidationEngine.java +++ b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/ValidationEngine.kt @@ -25,13 +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.validation.engine -package org.hisp.dhis.android.core.validation.engine; - -import io.reactivex.Single; - -public interface ValidationEngine { +import io.reactivex.Single +interface ValidationEngine { /** * Run the validation associated to a particular dataSets returning a ValidationResult. This result contains the * list of validation conflicts. @@ -42,14 +40,18 @@ public interface ValidationEngine { * @param attributeOptionComboUid AttributeOptionCombo uid to run the validation rules * @return Validation result */ - Single validate(String dataSetUid, String periodId, - String orgUnitUid, String attributeOptionComboUid); + fun validate( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String + ): Single /** * Run the validation associated to a particular dataSets returning a ValidationResult. This result contains the * list of validation conflicts. Important: this is a blocking method and it should not be * executed in the main thread. Consider the asynchronous version - * {@link #validate(String, String, String, String)}. + * [.validate]. * * @param dataSetUid DataSet uid to run the validation rules * @param periodId Validation period @@ -57,7 +59,10 @@ Single validate(String dataSetUid, String periodId, * @param attributeOptionComboUid AttributeOptionCombo uid to run the validation rules * @return Validation result */ - ValidationResult blockingValidate(String dataSetUid, String periodId, - String orgUnitUid, String attributeOptionComboUid); - -} \ No newline at end of file + fun blockingValidate( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String + ): ValidationResult +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineEntityDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineEntityDIModule.kt similarity index 85% rename from core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineEntityDIModule.java rename to core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineEntityDIModule.kt index f0eb9cc206..9255d5def9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineEntityDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineEntityDIModule.kt @@ -25,21 +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.validation.engine.internal -package org.hisp.dhis.android.core.validation.engine.internal; - -import org.hisp.dhis.android.core.validation.engine.ValidationEngine; - -import dagger.Module; -import dagger.Provides; -import dagger.Reusable; +import dagger.Module +import dagger.Provides +import dagger.Reusable +import org.hisp.dhis.android.core.validation.engine.ValidationEngine @Module -public final class ValidationEngineEntityDIModule { - +internal class ValidationEngineEntityDIModule { @Provides @Reusable - public ValidationEngine store(ValidationEngineImpl impl) { - return impl; + fun store(impl: ValidationEngineImpl): ValidationEngine { + return impl } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineImpl.java b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineImpl.java deleted file mode 100644 index 4caa99b46a..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineImpl.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.validation.engine.internal; - -import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore; -import org.hisp.dhis.android.core.arch.helpers.UidsHelper; -import org.hisp.dhis.android.core.constant.Constant; -import org.hisp.dhis.android.core.constant.ConstantCollectionRepository; -import org.hisp.dhis.android.core.datavalue.DataValue; -import org.hisp.dhis.android.core.datavalue.DataValueCollectionRepository; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitCollectionRepository; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitOrganisationUnitGroupLink; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitOrganisationUnitGroupLinkTableInfo; -import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject; -import org.hisp.dhis.android.core.parser.internal.service.utils.ExpressionHelper; -import org.hisp.dhis.android.core.period.Period; -import org.hisp.dhis.android.core.period.internal.PeriodHelper; -import org.hisp.dhis.android.core.validation.ValidationRule; -import org.hisp.dhis.android.core.validation.ValidationRuleCollectionRepository; -import org.hisp.dhis.android.core.validation.engine.ValidationEngine; -import org.hisp.dhis.android.core.validation.engine.ValidationResult; -import org.hisp.dhis.android.core.validation.engine.ValidationResultViolation; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; - -import io.reactivex.Single; - -class ValidationEngineImpl implements ValidationEngine { - - private final ValidationExecutor validationExecutor; - - private final ValidationRuleCollectionRepository validationRuleRepository; - - private final DataValueCollectionRepository dataValueRepository; - - private final ConstantCollectionRepository constantRepository; - - private final OrganisationUnitCollectionRepository organisationUnitRepository; - - private final PeriodHelper periodHelper; - - private final LinkStore orgunitGroupLinkStore; - - @Inject - ValidationEngineImpl(ValidationExecutor validationExecutor, - ValidationRuleCollectionRepository validationRuleRepository, - DataValueCollectionRepository dataValueRepository, - ConstantCollectionRepository constantRepository, - OrganisationUnitCollectionRepository organisationUnitRepository, - PeriodHelper periodHelper, - LinkStore orgunitGroupLinkStore) { - this.validationExecutor = validationExecutor; - this.validationRuleRepository = validationRuleRepository; - this.dataValueRepository = dataValueRepository; - this.constantRepository = constantRepository; - this.organisationUnitRepository = organisationUnitRepository; - this.periodHelper = periodHelper; - this.orgunitGroupLinkStore = orgunitGroupLinkStore; - } - - @Override - public Single validate(String dataSetUid, String periodId, - String orgUnitUid, String attributeOptionComboUid) { - return Single.fromCallable(() -> - blockingValidate(dataSetUid, periodId, orgUnitUid, attributeOptionComboUid)); - } - - @Override - public ValidationResult blockingValidate(String dataSetUid, String periodId, - String orgUnitUid, String attributeOptionComboUid) { - - List rules = getValidationRulesForDataSetValidation(dataSetUid); - List violations = new ArrayList<>(); - - if (!rules.isEmpty()) { - Map constantMap = getConstantMap(); - Map valueMap = getValueMap(dataSetUid, attributeOptionComboUid, - orgUnitUid, periodId); - Map orgunitGroupMap = getOrgunitGroupMap(); - OrganisationUnit organisationUnit = getOrganisationUnit(orgUnitUid); - Period period = getPeriod(periodId); - - for (ValidationRule rule : rules) { - violations.addAll(validationExecutor.evaluateRule(rule, organisationUnit, valueMap, constantMap, - orgunitGroupMap, period, attributeOptionComboUid)); - } - } - - ValidationResult.ValidationResultStatus status = violations.isEmpty() ? - ValidationResult.ValidationResultStatus.OK : - ValidationResult.ValidationResultStatus.ERROR; - - return ValidationResult.builder() - .status(status) - .violations(violations) - .build(); - } - - private List getValidationRulesForDataSetValidation(String datasetUid) { - return validationRuleRepository - .byDataSetUids(Collections.singletonList(datasetUid)) - .bySkipFormValidation().isFalse() - .blockingGet(); - } - - private Map getValueMap(String dataSetUid, String attributeOptionComboUid, - String orgUnitUid, String periodId) { - List dataValues = dataValueRepository - .byDataSetUid(dataSetUid) - .byAttributeOptionComboUid().eq(attributeOptionComboUid) - .byOrganisationUnitUid().eq(orgUnitUid) - .byPeriod().eq(periodId) - .byDeleted().isFalse() - .blockingGet(); - - return ExpressionHelper.getValueMap(dataValues); - } - - private Map getConstantMap() { - List constants = constantRepository.blockingGet(); - return UidsHelper.mapByUid(constants); - } - - private OrganisationUnit getOrganisationUnit(String orgunitId) { - return organisationUnitRepository.uid(orgunitId).blockingGet(); - } - - private Map getOrgunitGroupMap() { - return orgunitGroupLinkStore.groupAndGetCountBy( - OrganisationUnitOrganisationUnitGroupLinkTableInfo.Columns.ORGANISATION_UNIT_GROUP); - } - - private Period getPeriod(String periodId) { - return periodHelper.blockingGetPeriodForPeriodId(periodId); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineImpl.kt new file mode 100644 index 0000000000..e5db989f68 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationEngineImpl.kt @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.validation.engine.internal + +import io.reactivex.Single +import javax.inject.Inject +import org.hisp.dhis.android.core.arch.db.stores.internal.LinkStore +import org.hisp.dhis.android.core.arch.helpers.UidsHelper.mapByUid +import org.hisp.dhis.android.core.constant.Constant +import org.hisp.dhis.android.core.constant.ConstantCollectionRepository +import org.hisp.dhis.android.core.datavalue.DataValueCollectionRepository +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitCollectionRepository +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitOrganisationUnitGroupLink +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitOrganisationUnitGroupLinkTableInfo +import org.hisp.dhis.android.core.parser.internal.service.ExpressionServiceContext +import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject +import org.hisp.dhis.android.core.parser.internal.service.utils.ExpressionHelper.getValueMap +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.internal.PeriodHelper +import org.hisp.dhis.android.core.validation.ValidationRule +import org.hisp.dhis.android.core.validation.ValidationRuleCollectionRepository +import org.hisp.dhis.android.core.validation.engine.ValidationEngine +import org.hisp.dhis.android.core.validation.engine.ValidationResult +import org.hisp.dhis.android.core.validation.engine.ValidationResult.ValidationResultStatus + +internal class ValidationEngineImpl @Inject constructor( + private val validationExecutor: ValidationExecutor, + private val validationRuleRepository: ValidationRuleCollectionRepository, + private val dataValueRepository: DataValueCollectionRepository, + private val constantRepository: ConstantCollectionRepository, + private val organisationUnitRepository: OrganisationUnitCollectionRepository, + private val periodHelper: PeriodHelper, + private val orgunitGroupLinkStore: LinkStore +) : ValidationEngine { + override fun validate( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String + ): Single { + return Single.fromCallable { blockingValidate(dataSetUid, periodId, orgUnitUid, attributeOptionComboUid) } + } + + override fun blockingValidate( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String + ): ValidationResult { + val rules = getValidationRulesForDataSetValidation(dataSetUid) + + val violations = if (rules.isNotEmpty()) { + val constantMap = constantMap + val valueMap = getValueMap( + dataSetUid, attributeOptionComboUid, + orgUnitUid, periodId + ) + val orgunitGroupMap = orgunitGroupMap + val organisationUnit = getOrganisationUnit(orgUnitUid) + val period = getPeriod(periodId) + val context = ExpressionServiceContext(valueMap, constantMap, orgunitGroupMap, PeriodHelper.getDays(period)) + + rules.mapNotNull { + validationExecutor.evaluateRule(it, organisationUnit, context, period, attributeOptionComboUid) + } + } else { + emptyList() + } + + val status = if (violations.isEmpty()) ValidationResultStatus.OK else ValidationResultStatus.ERROR + + return ValidationResult.builder() + .status(status) + .violations(violations) + .build() + } + + private fun getValidationRulesForDataSetValidation(datasetUid: String): List { + return validationRuleRepository + .byDataSetUids(listOf(datasetUid)) + .bySkipFormValidation().isFalse + .blockingGet() + } + + private fun getValueMap( + dataSetUid: String, + attributeOptionComboUid: String, + orgUnitUid: String, + periodId: String + ): Map { + val dataValues = dataValueRepository + .byDataSetUid(dataSetUid) + .byAttributeOptionComboUid().eq(attributeOptionComboUid) + .byOrganisationUnitUid().eq(orgUnitUid) + .byPeriod().eq(periodId) + .byDeleted().isFalse + .blockingGet() + return getValueMap(dataValues) + } + + private val constantMap: Map + get() { + val constants = constantRepository.blockingGet() + return mapByUid(constants) + } + + private fun getOrganisationUnit(orgunitId: String): OrganisationUnit? { + return organisationUnitRepository.uid(orgunitId).blockingGet() + } + + private val orgunitGroupMap: Map + get() = orgunitGroupLinkStore.groupAndGetCountBy( + OrganisationUnitOrganisationUnitGroupLinkTableInfo.Columns.ORGANISATION_UNIT_GROUP + ) + + private fun getPeriod(periodId: String): Period { + return periodHelper.blockingGetPeriodForPeriodId(periodId) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationExecutor.java b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationExecutor.java deleted file mode 100644 index e2d1447775..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationExecutor.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.validation.engine.internal; - -import org.hisp.dhis.android.core.constant.Constant; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; -import org.hisp.dhis.android.core.parser.internal.service.ExpressionService; -import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject; -import org.hisp.dhis.android.core.period.Period; -import org.hisp.dhis.android.core.period.internal.PeriodHelper; -import org.hisp.dhis.android.core.validation.MissingValueStrategy; -import org.hisp.dhis.android.core.validation.ValidationRule; -import org.hisp.dhis.android.core.validation.ValidationRuleExpression; -import org.hisp.dhis.android.core.validation.ValidationRuleOperator; -import org.hisp.dhis.android.core.validation.engine.ValidationResultSideEvaluation; -import org.hisp.dhis.android.core.validation.engine.ValidationResultViolation; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -class ValidationExecutor { - - private final ExpressionService expressionService; - - @Inject - ValidationExecutor(ExpressionService expressionService) { - this.expressionService = expressionService; - } - - List evaluateRule(ValidationRule rule, - OrganisationUnit organisationUnit, - Map valueMap, - Map constantMap, - Map orgunitGroupMap, - Period period, - String attributeOptionComboId) { - - List violations = new ArrayList<>(); - - if (!rule.organisationUnitLevels().isEmpty() && - organisationUnit != null && - !rule.organisationUnitLevels().contains(organisationUnit.level())) { - return violations; - } - - Integer days = PeriodHelper.getDays(period); - - Double leftSideValue = (Double) expressionService.getExpressionValue(rule.leftSide().expression(), valueMap, - constantMap, orgunitGroupMap, days, rule.leftSide().missingValueStrategy()); - Double rightSideValue = (Double) expressionService.getExpressionValue(rule.rightSide().expression(), valueMap, - constantMap, orgunitGroupMap, days, rule.rightSide().missingValueStrategy()); - - if (isViolation(rule, leftSideValue, rightSideValue)) { - ValidationResultSideEvaluation leftSide = buildSideResult(leftSideValue, rule.leftSide(), valueMap, - constantMap, orgunitGroupMap, days); - ValidationResultSideEvaluation rightSide = buildSideResult(rightSideValue, rule.rightSide(), valueMap, - constantMap, orgunitGroupMap, days); - - violations.add(ValidationResultViolation.builder() - .period(period.periodId()) - .organisationUnitUid(organisationUnit.uid()) - .attributeOptionComboUid(attributeOptionComboId) - .validationRule(rule) - .leftSideEvaluation(leftSide) - .rightSideEvaluation(rightSide) - .build()); - } - return violations; - } - - private boolean isViolation(ValidationRule rule, Double leftSide, Double rightSide) { - if (ValidationRuleOperator.compulsory_pair.equals(rule.operator())) { - return (leftSide == null) != (rightSide == null); - } - - if (ValidationRuleOperator.exclusive_pair.equals(rule.operator())) { - return leftSide != null && rightSide != null; - } - - Double leftSideValue = leftSide; - if (leftSide == null) { - if (rule.leftSide().missingValueStrategy() == MissingValueStrategy.NEVER_SKIP) { - leftSideValue = 0d; - } else { - return false; - } - } - - Double rightSideValue = rightSide; - if (rightSide == null) { - if (rule.rightSide().missingValueStrategy() == MissingValueStrategy.NEVER_SKIP) { - rightSideValue = 0d; - } else { - return false; - } - } - - String test = leftSideValue - + rule.operator().getMathematicalOperator() - + rightSideValue; - return !(Boolean) expressionService.getExpressionValue(test); - } - - private ValidationResultSideEvaluation buildSideResult(Double value, - ValidationRuleExpression side, - Map valueMap, - Map constantMap, - Map orgunitGroupMap, - Integer days) { - return ValidationResultSideEvaluation.builder() - .value(value) - .dataElementUids(expressionService.getDataElementOperands(side.expression())) - .displayExpression(expressionService.getExpressionDescription(side.expression(), constantMap)) - .regeneratedExpression( - expressionService.regenerateExpression(side.expression(), valueMap, constantMap, - orgunitGroupMap, days)) - .build(); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationExecutor.kt b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationExecutor.kt new file mode 100644 index 0000000000..655fa5b445 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/validation/engine/internal/ValidationExecutor.kt @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.validation.engine.internal + +import javax.inject.Inject +import javax.inject.Singleton +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.parser.internal.service.ExpressionService +import org.hisp.dhis.android.core.parser.internal.service.ExpressionServiceContext +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.validation.MissingValueStrategy +import org.hisp.dhis.android.core.validation.ValidationRule +import org.hisp.dhis.android.core.validation.ValidationRuleExpression +import org.hisp.dhis.android.core.validation.ValidationRuleOperator +import org.hisp.dhis.android.core.validation.engine.ValidationResultSideEvaluation +import org.hisp.dhis.android.core.validation.engine.ValidationResultViolation + +@Singleton +internal class ValidationExecutor @Inject constructor(private val expressionService: ExpressionService) { + fun evaluateRule( + rule: ValidationRule, + organisationUnit: OrganisationUnit?, + context: ExpressionServiceContext, + period: Period, + attributeOptionComboId: String? + ): ValidationResultViolation? { + return if (shouldSkipOrgunitLevel(rule, organisationUnit)) { + null + } else { + val leftSideValue = expressionService.getExpressionValue( + rule.leftSide().expression(), context, rule.leftSide().missingValueStrategy() + ) as Double? + val rightSideValue = expressionService.getExpressionValue( + rule.rightSide().expression(), context, rule.rightSide().missingValueStrategy() + ) as Double? + + if (isViolation(rule, leftSideValue, rightSideValue)) { + val leftSide = buildSideResult(leftSideValue, rule.leftSide(), context) + val rightSide = buildSideResult(rightSideValue, rule.rightSide(), context) + ValidationResultViolation.builder() + .period(period.periodId()) + .organisationUnitUid(organisationUnit!!.uid()) + .attributeOptionComboUid(attributeOptionComboId) + .validationRule(rule) + .leftSideEvaluation(leftSide) + .rightSideEvaluation(rightSide) + .build() + } else { + null + } + } + } + + private fun isViolation(rule: ValidationRule, leftSide: Double?, rightSide: Double?): Boolean { + return when (rule.operator()) { + ValidationRuleOperator.compulsory_pair -> leftSide == null != (rightSide == null) + ValidationRuleOperator.exclusive_pair -> leftSide != null && rightSide != null + else -> { + val leftSideValue = leftSide + ?: if (rule.leftSide().missingValueStrategy() == MissingValueStrategy.NEVER_SKIP) { + 0.0 + } else { + null + } + + val rightSideValue: Double? = rightSide + ?: if (rule.rightSide().missingValueStrategy() == MissingValueStrategy.NEVER_SKIP) { + 0.0 + } else { + null + } + + if (leftSideValue == null || rightSideValue == null) { + false + } else { + val test = "$leftSideValue ${rule.operator().mathematicalOperator} $rightSideValue" + !(expressionService.getExpressionValue(test) as Boolean) + } + } + } + } + + private fun buildSideResult( + value: Double?, + side: ValidationRuleExpression, + context: ExpressionServiceContext + ): ValidationResultSideEvaluation { + return ValidationResultSideEvaluation.builder() + .value(value) + .dataElementUids(expressionService.getDataElementOperands(side.expression())) + .displayExpression(expressionService.getExpressionDescription(side.expression(), context.constantMap)) + .regeneratedExpression(expressionService.regenerateExpression(side.expression(), context)) + .build() + } + + private fun shouldSkipOrgunitLevel( + rule: ValidationRule, + organisationUnit: OrganisationUnit? + ): Boolean { + return rule.organisationUnitLevels().isNotEmpty() && + organisationUnit != null && + !rule.organisationUnitLevels().contains(organisationUnit.level()) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEvaluatorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEvaluatorShould.kt index 8fdf97967d..b009497335 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEvaluatorShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/indicator/datasetindicatorengine/DataSetIndicatorEvaluatorShould.kt @@ -35,6 +35,7 @@ import org.hisp.dhis.android.core.constant.Constant import org.hisp.dhis.android.core.indicator.Indicator import org.hisp.dhis.android.core.indicator.IndicatorType import org.hisp.dhis.android.core.parser.internal.service.ExpressionService +import org.hisp.dhis.android.core.parser.internal.service.ExpressionServiceContext import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject import org.junit.Before import org.junit.Test @@ -54,7 +55,6 @@ class DataSetIndicatorEvaluatorShould { private val diObject1: DimensionalItemObject = mock() private val diObject2: DimensionalItemObject = mock() - private lateinit var valueMap: Map private lateinit var orgunitGroupMap: Map private lateinit var expressionService: ExpressionService @@ -78,36 +78,40 @@ class DataSetIndicatorEvaluatorShould { @Test fun evaluate_indicator_factor() { - valueMap = mapOf( - diObject1 to 5.0, - diObject2 to 10.0 + val context = getContextForValueMap( + mapOf( + diObject1 to 5.0, + diObject2 to 10.0 + ) ) whenever(indicatorType.factor()) doReturn 1 assertThat( - evaluator.evaluate(indicator, indicatorType, valueMap, constantMap, orgunitGroupMap, days) + evaluator.evaluate(indicator, indicatorType, context) ).isEqualTo(0.5) whenever(indicatorType.factor()) doReturn 100 assertThat( - evaluator.evaluate(indicator, indicatorType, valueMap, constantMap, orgunitGroupMap, days) + evaluator.evaluate(indicator, indicatorType, context) ).isEqualTo(50) whenever(indicatorType.factor()) doReturn -10 assertThat( - evaluator.evaluate(indicator, indicatorType, valueMap, constantMap, orgunitGroupMap, days) + evaluator.evaluate(indicator, indicatorType, context) ).isEqualTo(-5) } @Test fun evaluate_indicator_decimals_default() { - valueMap = mapOf( - diObject1 to 10.0, - diObject2 to 3.0 + val context = getContextForValueMap( + mapOf( + diObject1 to 10.0, + diObject2 to 3.0 + ) ) assertThat( - evaluator.evaluate(indicator, indicatorType, valueMap, constantMap, orgunitGroupMap, days) + evaluator.evaluate(indicator, indicatorType, context) ).isEqualTo(3.33) } @@ -115,47 +119,59 @@ class DataSetIndicatorEvaluatorShould { fun evaluate_indicator_decimals_configurable() { whenever(indicator.decimals()) doReturn 3 - valueMap = mapOf( - diObject1 to 10.0, - diObject2 to 3.0 + val context = getContextForValueMap( + mapOf( + diObject1 to 10.0, + diObject2 to 3.0 + ) ) assertThat( - evaluator.evaluate(indicator, indicatorType, valueMap, constantMap, orgunitGroupMap, days) + evaluator.evaluate(indicator, indicatorType, context) ).isEqualTo(3.333) } @Test fun evaluate_null_numerator() { - valueMap = mapOf( - diObject2 to 10.0 + val context = getContextForValueMap( + mapOf( + diObject2 to 10.0 + ) ) assertThat( - evaluator.evaluate(indicator, indicatorType, valueMap, constantMap, orgunitGroupMap, days) + evaluator.evaluate(indicator, indicatorType, context) ).isEqualTo(0.0) } @Test fun evaluate_null_denominator() { - valueMap = mapOf( - diObject1 to 10.0 + val context = getContextForValueMap( + mapOf( + diObject1 to 10.0 + ) ) assertThat( - evaluator.evaluate(indicator, indicatorType, valueMap, constantMap, orgunitGroupMap, days) + evaluator.evaluate(indicator, indicatorType, context) ).isEqualTo(0.0) } @Test fun evaluate_zero_denominator() { - valueMap = mapOf( - diObject1 to 10.0, - diObject2 to 0.0 + val context = getContextForValueMap( + mapOf( + diObject1 to 10.0, + diObject2 to 0.0 + ) ) assertThat( - evaluator.evaluate(indicator, indicatorType, valueMap, constantMap, orgunitGroupMap, days) + evaluator.evaluate(indicator, indicatorType, context) ).isEqualTo(0.0) } + + private fun getContextForValueMap(valueMap: Map): ExpressionServiceContext { + return ExpressionServiceContext(valueMap, constantMap, orgunitGroupMap, days) + } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt index 5e3f048fe2..d65d47c095 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/parser/internal/expression/ExpressionServiceShould.kt @@ -35,9 +35,9 @@ import org.hisp.dhis.android.core.category.CategoryOptionCombo import org.hisp.dhis.android.core.category.internal.CategoryOptionComboStore import org.hisp.dhis.android.core.constant.Constant import org.hisp.dhis.android.core.dataelement.DataElement -import org.hisp.dhis.android.core.dataelement.DataElementOperand import org.hisp.dhis.android.core.organisationunit.OrganisationUnitGroup import org.hisp.dhis.android.core.parser.internal.service.ExpressionService +import org.hisp.dhis.android.core.parser.internal.service.ExpressionServiceContext import org.hisp.dhis.android.core.parser.internal.service.dataobject.DataElementObject import org.hisp.dhis.android.core.parser.internal.service.dataobject.DataElementOperandObject import org.hisp.dhis.android.core.parser.internal.service.dataobject.DimensionalItemObject @@ -104,10 +104,7 @@ class ExpressionServiceShould { val result = service.getExpressionValue( expression, - valueMap, - constantMap, - emptyMap(), - 10, + ExpressionServiceContext(valueMap, constantMap, emptyMap(), 10), MissingValueStrategy.NEVER_SKIP ) as Double? @@ -123,8 +120,9 @@ class ExpressionServiceShould { whenever(constant.value()).thenReturn(4.0) val result = service.getExpressionValue( - expression, valueMap, constantMap, emptyMap(), - 10, MissingValueStrategy.NEVER_SKIP + expression, + ExpressionServiceContext(valueMap, constantMap, emptyMap(), 10), + MissingValueStrategy.NEVER_SKIP ) as Double? assertThat(result).isEqualTo(9.0) @@ -139,10 +137,7 @@ class ExpressionServiceShould { ) val result = service.getExpressionValue( expression, - valueMap, - constantMap, - emptyMap(), - 10, + ExpressionServiceContext(valueMap, constantMap, emptyMap(), 10), MissingValueStrategy.NEVER_SKIP ) as Double? @@ -157,10 +152,7 @@ class ExpressionServiceShould { ) val result = service.getExpressionValue( expression, - valueMap, - constantMap, - emptyMap(), - 10, + ExpressionServiceContext(valueMap, constantMap, emptyMap(), 10), MissingValueStrategy.NEVER_SKIP ) as Double? @@ -178,10 +170,7 @@ class ExpressionServiceShould { ) val result = service.getExpressionValue( expression, - valueMap, - constantMap, - orgunitMap, - 10, + ExpressionServiceContext(valueMap, constantMap, orgunitMap, 10), MissingValueStrategy.NEVER_SKIP ) as Double? @@ -201,8 +190,9 @@ class ExpressionServiceShould { MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING to 5.0 ).forEach { (strategy, expected) -> val result = service.getExpressionValue( - expression, valueMap, - constantMap, emptyMap(), 10, strategy + expression, + ExpressionServiceContext(valueMap, constantMap, emptyMap(), 10), + strategy ) as Double? assertThat(result).isEqualTo(expected) @@ -220,8 +210,9 @@ class ExpressionServiceShould { MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING to null ).forEach { (strategy, expected) -> val result = service.getExpressionValue( - expression, valueMap, - constantMap, emptyMap(), 10, strategy + expression, + ExpressionServiceContext(valueMap, constantMap, emptyMap(), 10), + strategy ) as Double? assertThat(result).isEqualTo(expected) @@ -230,10 +221,11 @@ class ExpressionServiceShould { @Test fun evaluate_null_expression() { + val context = ExpressionServiceContext(emptyMap(), constantMap, emptyMap(), 10) assertThat(service.getExpressionValue(null)).isNull() assertThat(service.getExpressionDescription(null, emptyMap())).isEqualTo("") assertThat(service.getDataElementOperands(null)).isEmpty() - assertThat(service.regenerateExpression(null, emptyMap(), constantMap, emptyMap(), 10)).isEqualTo("") + assertThat(service.regenerateExpression(null, context)).isEqualTo("") } @Test @@ -316,10 +308,21 @@ class ExpressionServiceShould { } } + @Test + fun get_dataelement_ids_in_nested_expressions() { + val expression = "if(" + + "${de(dataElementId1)} > 0, " + + "greatest(${de(dataElementId1)}, ${de(dataElementId2)})," + + "${deOperand(dataElementId1, categoryOptionComboId1)})" + + val dataElementOperands = service.getDataElementOperands(expression) + assertThat(dataElementOperands.size).isEqualTo(3) + } + @Test fun get_dataelement_ids_in_empty_expression() { val expression = days + " + " + constant(constantId) + " + " + oug(constantId) - val dataElementOperands: Set = service.getDataElementOperands(expression) + val dataElementOperands = service.getDataElementOperands(expression) assertThat(dataElementOperands).isEmpty() } @@ -364,12 +367,10 @@ class ExpressionServiceShould { val orgunitMap: Map = mapOf( orgunitGroupId to 20 ) + val context = ExpressionServiceContext(valueMap, constantMap, orgunitMap, 10) whenever(constant.value()).thenReturn(3.14) - val regeneratedExpression: Any = service.regenerateExpression( - expression, valueMap, constantMap, - orgunitMap, 10 - ) + val regeneratedExpression: Any = service.regenerateExpression(expression, context) assertThat(regeneratedExpression).isEqualTo("5.0 + 3.0 / 3.14 * 20 - 10.0") } @@ -380,9 +381,10 @@ class ExpressionServiceShould { val valueMap: Map = mapOf( DataElementOperandObject(dataElementId1, categoryOptionComboId1) to 5.0 ) + val context = ExpressionServiceContext(valueMap, constantMap, emptyMap(), 10) val regeneratedExpression: Any = - service.regenerateExpression(expression, valueMap, constantMap, emptyMap(), 10) + service.regenerateExpression(expression, context) assertThat(regeneratedExpression).isEqualTo("5.0 + " + de(dataElementId2)) } diff --git a/docs/content/developer/analytics.md b/docs/content/developer/analytics.md index 4d8ccd0ed3..cd79c695b7 100644 --- a/docs/content/developer/analytics.md +++ b/docs/content/developer/analytics.md @@ -326,48 +326,52 @@ Properties included in the `GridDimensionalResponse` object: This table shows the functionality supported by the Indicator dimension item compared to the backend analytics. -| Type | Element | Backend | Android SDK | -|---------- |---------------------- |-----------|---------------| -|**Mathematical:** |Parenthesis | Yes | Yes | -| |Plus (+) | Yes | Yes | -| |Minus (-) | Yes | Yes | -| |Power (^) | Yes | No | -| |Multiply (*) | Yes | Yes | -| |Divide (/) | Yes | Yes | -| |Modulus (%) | Yes | Yes | -|**Logical:** |NOT | Yes | Yes | -| |! | Yes | Yes | -| |AND | Yes | Yes | -| |&& | Yes | Yes | -| |OR | Yes | Yes | -| ||| | Yes | Yes | -|**Comparison:** |Equal (==) | Yes | Yes | -| |NotEqual (!=) | Yes | Yes | -| |GT (>) | Yes | Yes | -| |LT (<) | Yes | Yes | -| |GE (>=) | Yes | Yes | -| |LE (<=) | Yes | Yes | -|**Functions:** |FirstNonNull | Yes | Yes | -| |Greatest | Yes | Yes | -| |If | Yes | Yes | -| |IsNotNull | Yes | Yes | -| |IsNull | Yes | Yes | -| |Least | Yes | Yes | -| |Log | Yes | No | -| |Log10 | Yes | No | -| |.aggregationType | Yes | No | -| |.maxDate | Yes | No | -| |.minDate | Yes | No | -| |.periodOffset | Yes | No | -|**Dimensions:** |Constant | Yes | Yes | -| |DataElement | Yes | Yes | -| |ProgramAttribute | Yes | Yes | -| |ProgramDataElement | Yes | Yes | -| |ProgramIndicator | Yes | Yes | -| |OrgUnitGroup | Yes | No | -| |ReportingRate | Yes | No | -| |Days | Yes | Yes | -| |N_Brace (indicators) | Yes | No | +| Type | Element | Backend | Android SDK | +|---------- |----------------------|-----------|-------------| +|**Mathematical:** | Parenthesis | Yes | Yes | +| | Plus (+) | Yes | Yes | +| | Minus (-) | Yes | Yes | +| | Power (^) | Yes | No | +| | Multiply (*) | Yes | Yes | +| | Divide (/) | Yes | Yes | +| | Modulus (%) | Yes | Yes | +|**Logical:** | NOT | Yes | Yes | +| | ! | Yes | Yes | +| | AND | Yes | Yes | +| | && | Yes | Yes | +| | OR | Yes | Yes | +| | || | Yes | Yes | +|**Comparison:** | Equal (==) | Yes | Yes | +| | NotEqual (!=) | Yes | Yes | +| | GT (>) | Yes | Yes | +| | LT (<) | Yes | Yes | +| | GE (>=) | Yes | Yes | +| | LE (<=) | Yes | Yes | +|**Functions:** | FirstNonNull | Yes | Yes | +| | Greatest | Yes | Yes | +| | If | Yes | Yes | +| | IsNotNull | Yes | Yes | +| | IsNull | Yes | Yes | +| | Least | Yes | Yes | +| | Log | Yes | No | +| | Log10 | Yes | No | +| | Subexpression | Yes | No | +| | .aggregationType | Yes | Yes | +| | .maxDate | Yes | Yes | +| | .minDate | Yes | Yes | +| | .periodOffset | Yes | Yes | +| | .yearToDate | Yes | Yes | +|**Dimensions:** | Constant | Yes | Yes | +| | DataElement | Yes | Yes | +| | ProgramAttribute | Yes | Yes | +| | ProgramDataElement | Yes | Yes | +| | ProgramIndicator | Yes | Yes | +| | OrgUnitGroup | Yes | No | +| | ReportingRate | Yes | No | +| | Days | Yes | Yes | +| | PeriodInYear | Yes | Yes | +| | YearlyPeriodCount | Yes | Yes | +| | N_Brace (indicators) | Yes | No | ### Program indicator support { #android_sdk_analytics_program_indicator_support } From 695c405d42187a4fbceac4d174be96d4cb3f4a69 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Fri, 23 Dec 2022 10:22:01 +0100 Subject: [PATCH 030/201] [ANDROSDK-1613] Add methods to filter by dataset and program --- ...VisualizationsSettingObjectRepository.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 index e6d7c72870..6ba05a0e62 100644 --- 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 @@ -35,9 +35,12 @@ import org.hisp.dhis.android.core.arch.repositories.object.internal.ReadOnlyAnyObjectWithDownloadRepositoryImpl; import org.hisp.dhis.android.core.settings.internal.AnalyticsSettingCall; +import java.util.List; + import javax.inject.Inject; import dagger.Reusable; +import io.reactivex.Single; @Reusable public class AnalyticsDhisVisualizationsSettingObjectRepository @@ -54,6 +57,22 @@ public AnalyticsDhisVisualizationsSettingObjectRepository( this.analyticsDhisVisualizationStore = analyticsDhisVisualizationStore; } + public Single> getByProgram(String program) { + return Single.just(blockingGetByProgram(program)); + } + + public List blockingGetByProgram(String program) { + return generateGroups(analyticsDhisVisualizationStore.selectAll()).program().get(program); + } + + public Single> getByDataSet(String dataSet) { + return Single.just(blockingGetByProgram(dataSet)); + } + + public List blockingByDataSet(String dataSet) { + return generateGroups(analyticsDhisVisualizationStore.selectAll()).dataSet().get(dataSet); + } + @Override public AnalyticsDhisVisualizationsSetting blockingGet() { return generateGroups(analyticsDhisVisualizationStore.selectAll()); From 3ebc087c1c08fd0239f96af2bac239aa335bc4c1 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Fri, 23 Dec 2022 10:22:17 +0100 Subject: [PATCH 031/201] [ANDROSDK-1613] Add tests for new methods --- ...ngObjectRepositoryMockIntegrationShould.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould.kt index 7612c88499..dd5c7d61b7 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/settings/AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould.kt @@ -51,4 +51,30 @@ class AnalyticsDhisVisualizationsSettingObjectRepositoryMockIntegrationShould : assertThat(analyticsDhisVisualizationsSetting.program().size).isEqualTo(1) assertThat(analyticsDhisVisualizationsSetting.dataSet().size).isEqualTo(1) } + + @Test + fun find_analytics_settings_by_program() { + val programSettings = d2 + .settingModule() + .analyticsSetting() + .visualizationsSettings() + .blockingGetByProgram("IpHINAT79UW") + + assertThat(programSettings.size).isEqualTo(1) + assertThat(programSettings.first().visualizations().size).isEqualTo(2) + assertThat(programSettings.first().visualizations().first().uid()).isEqualTo("PYBH8ZaAQnC") + } + + @Test + fun find_analytics_settings_by_data_set() { + val programSettings = d2 + .settingModule() + .analyticsSetting() + .visualizationsSettings() + .blockingByDataSet("BfMAe6Itzgt") + + assertThat(programSettings.size).isEqualTo(1) + assertThat(programSettings.first().visualizations().size).isEqualTo(1) + assertThat(programSettings.first().visualizations().first().uid()).isEqualTo("FAFa11yFeFe") + } } From ec26ea945e74bf1e0ac2dac10dede56cf03b1e93 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Fri, 23 Dec 2022 10:41:10 +0100 Subject: [PATCH 032/201] [ANDROSDK-1596] Fix nullPointer on blockingExists method --- .../internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt index 3d32b15037..1fafa409ee 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/object/internal/ReadOnlyWithTransformerObjectRepositoryImpl.kt @@ -45,9 +45,9 @@ internal constructor( private val transformer: TwoWayTransformer ) : ReadOnlyObjectRepository { - fun blockingGetWithoutChildren(): M { + fun blockingGetWithoutChildren(): M? { val whereClauseBuilder = WhereClauseFromScopeBuilder(WhereClauseBuilder()) - return store.selectOneWhere(whereClauseBuilder.getWhereClause(scope))!! + return store.selectOneWhere(whereClauseBuilder.getWhereClause(scope)) } /** From 1eeeadb5d0c9933a4343a0e77ccd4468e8a2e279 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Fri, 23 Dec 2022 10:41:45 +0100 Subject: [PATCH 033/201] [ANDROSDK-1596] Add test to ensure no more nullPointers will appear --- ...aseCollectionRepositoryMockIntegrationShould.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/usecase/stock/StockUseCaseCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/usecase/stock/StockUseCaseCollectionRepositoryMockIntegrationShould.java index 7fa50e4123..372e87bda8 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/usecase/stock/StockUseCaseCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/usecase/stock/StockUseCaseCollectionRepositoryMockIntegrationShould.java @@ -66,4 +66,16 @@ public void filter_by_number() { assertThat(stockUseCases.get(0).getTransactions().size()).isEqualTo(3); } + @Test + public void return_false_when_use_case_does_not_exist() { + boolean stockUseCaseExists = d2.useCaseModule().stockUseCases() + .uid("IpHINAT79UW") + .blockingExists(); + + boolean stockUseCaseDoesNotExist = d2.useCaseModule().stockUseCases() + .uid("false_uid") + .blockingExists(); + assertThat(stockUseCaseExists).isEqualTo(true); + assertThat(stockUseCaseDoesNotExist).isEqualTo(false); + } } \ No newline at end of file From c14f7640c47f4f81d1333a98117bbec98324ee76 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 26 Dec 2022 09:53:31 +1100 Subject: [PATCH 034/201] [ANDROSDK-1569] Reduce method complexity --- .../internal/service/ExpressionService.kt | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt index 6a63990529..b216f07722 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/ExpressionService.kt @@ -128,31 +128,22 @@ internal class ExpressionService @Inject constructor( val itemsFound = visitor.state.itemsFound val itemValuesFound = visitor.state.itemValuesFound - val handledValue = - if (value == null) { - 0.0 - } else if (value is Double && value.isNaN()) { - null - } else { - value - } - when (missingValueStrategy) { MissingValueStrategy.SKIP_IF_ANY_VALUE_MISSING -> { if (itemValuesFound < itemsFound) { null } else { - handledValue + getHandledValue(value) } } MissingValueStrategy.SKIP_IF_ALL_VALUES_MISSING -> { if (itemsFound != 0 && itemValuesFound == 0) { null } else { - handledValue + getHandledValue(value) } } - MissingValueStrategy.NEVER_SKIP -> handledValue + MissingValueStrategy.NEVER_SKIP -> getHandledValue(value) } } } @@ -180,6 +171,17 @@ internal class ExpressionService @Inject constructor( // ------------------------------------------------------------------------- // Supportive methods // ------------------------------------------------------------------------- + + private fun getHandledValue(value: Any?): Any? { + return if (value == null) { + 0.0 + } else if (value is Double && value.isNaN()) { + null + } else { + value + } + } + /** * Creates a new ExpressionItemsVisitor object. */ From 79a08d23823e6949b1aa23019c4f6fe8ca5a72d3 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 26 Dec 2022 12:45:12 +1100 Subject: [PATCH 035/201] [ANDROSDK-1616] Remove Apache dependencies --- core/build.gradle | 8 -- .../android/core/arch/helpers/DateUtils.kt | 8 ++ .../mockwebserver/ResponseController.java | 6 +- .../service/utils/ExpressionHelper.kt | 11 +- .../function/{D2Ceil.java => D2Ceil.kt} | 25 ++-- .../internal/function/D2DaysBetween.kt | 4 - .../function/{D2Floor.java => D2Floor.kt} | 25 ++-- .../internal/function/D2Left.kt | 46 +++++++ .../function/{D2Modulus.java => D2Modulus.kt} | 29 ++-- .../internal/function/D2Right.java | 54 -------- .../internal/function/D2Right.kt | 46 +++++++ .../function/{D2Round.java => D2Round.kt} | 25 ++-- .../internal/function/D2Split.java | 64 --------- .../function/{D2Left.java => D2Split.kt} | 39 +++--- .../internal/function/D2Substring.java | 49 ------- .../internal/function/D2Substring.kt | 44 ++++++ .../TrackerQueryFactoryCommonHelper.kt | 4 +- .../internal/TrackerSyncLastUpdatedManager.kt | 2 +- .../dhis/android/core/util/StringUtils.kt | 59 ++++++++ .../DataDimensionItemProgramAttribute.java | 3 +- .../DataDimensionItemProgramDataElement.java | 3 +- .../MonthlyPeriodGeneratorShould.java | 1 - .../{D2CeilShould.java => D2CeilShould.kt} | 88 +++++------- .../{D2FloorShould.java => D2FloorShould.kt} | 88 +++++------- .../internal/function/D2LeftShould.java | 114 ---------------- .../internal/function/D2LeftShould.kt | 99 ++++++++++++++ ...2ModulusShould.java => D2ModulusShould.kt} | 93 ++++++------- .../internal/function/D2RightShould.java | 110 --------------- .../internal/function/D2RightShould.kt | 96 +++++++++++++ .../{D2RoundShould.java => D2RoundShould.kt} | 87 +++++------- .../internal/function/D2SplitShould.java | 100 -------------- .../internal/function/D2SplitShould.kt | 89 +++++++++++++ .../internal/function/D2SubStringShould.java | 126 ------------------ .../internal/function/D2SubStringShould.kt | 106 +++++++++++++++ 34 files changed, 820 insertions(+), 931 deletions(-) rename core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2Ceil.java => D2Ceil.kt} (77%) rename core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2Floor.java => D2Floor.kt} (77%) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Left.kt rename core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2Modulus.java => D2Modulus.kt} (73%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Right.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Right.kt rename core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2Round.java => D2Round.kt} (77%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Split.java rename core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2Left.java => D2Split.kt} (67%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Substring.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Substring.kt create mode 100644 core/src/main/java/org/hisp/dhis/android/core/util/StringUtils.kt rename core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2CeilShould.java => D2CeilShould.kt} (61%) rename core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2FloorShould.java => D2FloorShould.kt} (61%) delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2LeftShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2LeftShould.kt rename core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2ModulusShould.java => D2ModulusShould.kt} (52%) delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RightShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RightShould.kt rename core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/{D2RoundShould.java => D2RoundShould.kt} (60%) delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SplitShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SplitShould.kt delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SubStringShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SubStringShould.kt diff --git a/core/build.gradle b/core/build.gradle index bf8db5e0ff..1ddc0e68d8 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -53,7 +53,6 @@ ext { paging : '2.1.2', // java - apacheCommons : "3.8.1", jackson : "2.11.2", autoValue : "1.7.4", autoValueCursor : "2.0.1", @@ -63,7 +62,6 @@ ext { rxJava : "2.2.12", rxAndroid : "2.1.1", sqlCipher : "4.4.3", - jexl : "2.1.1", jodaTime : "2.10.6", smsCompression : "0.2.0", expressionParser: "1.0.29", @@ -174,12 +172,6 @@ dependencies { api "com.squareup.retrofit2:converter-jackson:${libraries.retrofit}" api "com.squareup.retrofit2:adapter-rxjava2:${libraries.retrofit}" - // Apache libraries - api "org.apache.commons:commons-lang3:${libraries.apacheCommons}" - api("org.apache.commons:commons-jexl:${libraries.jexl}") { - exclude group: 'commons-logging', module: 'commons-logging' - } - // Joda time api "joda-time:joda-time:${libraries.jodaTime}" diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/DateUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/DateUtils.kt index 73c3918b75..176fc4d928 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/DateUtils.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/helpers/DateUtils.kt @@ -84,4 +84,12 @@ object DateUtils { fun getEndDate(periods: List): Date? { return periods.mapNotNull { it.endDate() }.maxByOrNull { it.time } } + + @JvmStatic + fun addMonths(date: Date, amount: Int): Date { + val c = CalendarProviderFactory.calendarProvider.calendar.clone() as Calendar + c.time = date + c.add(Calendar.MONTH, amount) + return c.time + } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/ResponseController.java b/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/ResponseController.java index 94e6f0ae83..34ddae0907 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/ResponseController.java +++ b/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/ResponseController.java @@ -37,8 +37,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.apache.commons.lang3.StringUtils.EMPTY; - public class ResponseController { public static final String GET = "GET"; @@ -77,7 +75,7 @@ void addResponse(String method, String path, String responseName, Integer respon String getBody(String method, String currentPath){ Map resourcesMap = methodsMap.get(method); - String filename = EMPTY; + String filename = ""; List paths = new ArrayList<>(resourcesMap.keySet()); Collections.reverse(paths); @@ -91,7 +89,7 @@ String getBody(String method, String currentPath){ } private String findResponse(Map resourcesMap, String path, String currentPath) { - String filename = EMPTY; + String filename = ""; Pattern pattern = Pattern.compile(path); Matcher matcher = pattern.matcher(currentPath); diff --git a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelper.kt index 19c69acad4..a8c8c51c2c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/parser/internal/service/utils/ExpressionHelper.kt @@ -29,7 +29,6 @@ package org.hisp.dhis.android.core.parser.internal.service.utils import java.lang.NumberFormatException import java.util.* -import org.apache.commons.lang3.math.NumberUtils import org.hisp.dhis.android.core.datavalue.DataValue import org.hisp.dhis.android.core.parser.internal.service.dataobject.DataElementObject import org.hisp.dhis.android.core.parser.internal.service.dataobject.DataElementOperandObject @@ -60,11 +59,11 @@ internal object ExpressionHelper { valueMap: MutableMap ) { try { - val newValue = NumberUtils.createDouble(value) - - val existingValue = valueMap[item] - val result = (existingValue ?: 0.0) + newValue - valueMap[item] = result + value?.toDouble()?.let { newValue -> + val existingValue = valueMap[item] + val result = (existingValue ?: 0.0) + newValue + valueMap[item] = result + } } catch (e: NumberFormatException) { // Ignore non-numeric values } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Ceil.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Ceil.kt similarity index 77% rename from core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Ceil.java rename to core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Ceil.kt index 3b2db8385f..ddf560441e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Ceil.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Ceil.kt @@ -25,20 +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.program.programindicatorengine.internal.function -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; +import kotlin.math.ceil +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; - -import static org.apache.commons.lang3.math.NumberUtils.toDouble; - -public class D2Ceil - implements ExpressionItem { - - @Override - public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - return String.valueOf((long) Math.ceil(toDouble(visitor.castStringVisit(ctx.expr(0)), 0.0))); +internal class D2Ceil : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val value = visitor.castStringVisit(ctx.expr(0))?.toDoubleOrNull() ?: ParserUtils.DOUBLE_VALUE_IF_NULL + return ceil(value).toLong().toString() } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2DaysBetween.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2DaysBetween.kt index 15e290bda8..887a86e44e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2DaysBetween.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2DaysBetween.kt @@ -39,8 +39,4 @@ internal class D2DaysBetween : ProgramBetweenDatesFunction() { override fun getSql(startExpression: String, endExpression: String): Any { return "CAST((julianday($endExpression) - julianday($startExpression)) AS INTEGER)" } - - private companion object { - const val MillisInADay = 24 * 60 * 60 * 1000 - } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Floor.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Floor.kt similarity index 77% rename from core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Floor.java rename to core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Floor.kt index 442551d817..e7612f2e4f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Floor.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Floor.kt @@ -25,20 +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.program.programindicatorengine.internal.function -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; +import kotlin.math.floor +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; - -import static org.apache.commons.lang3.math.NumberUtils.toDouble; - -public class D2Floor - implements ExpressionItem { - - @Override - public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - return String.valueOf((long) Math.floor(toDouble(visitor.castStringVisit(ctx.expr(0)), 0.0))); +internal class D2Floor : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val value = visitor.castStringVisit(ctx.expr(0))?.toDoubleOrNull() ?: ParserUtils.DOUBLE_VALUE_IF_NULL + return floor(value).toLong().toString() } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Left.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Left.kt new file mode 100644 index 0000000000..b4b6c4cb76 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Left.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorParserUtils.wrap +import org.hisp.dhis.android.core.util.StringUtils +import org.hisp.dhis.antlr.AntlrParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +internal class D2Left : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val doubleValue = AntlrParserUtils.castDouble(visitor.castStringVisit(ctx.expr(1))) + require(doubleValue % 1 == 0.0) { "Number has to be an integer" } + val chars = doubleValue.toInt() + val str = visitor.castStringVisit(ctx.expr(0)) + + return wrap(StringUtils.substring(str, 0, chars)) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.kt similarity index 73% rename from core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.java rename to core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.kt index d1fc530c35..cc8c8c5c26 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Modulus.kt @@ -25,24 +25,19 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; +import java.util.* +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; +internal class D2Modulus : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val firstValue = visitor.castStringVisit(ctx.expr(0))?.toDoubleOrNull() ?: ParserUtils.DOUBLE_VALUE_IF_NULL + val secondValue = visitor.castStringVisit(ctx.expr(1))?.toDoubleOrNull() ?: ParserUtils.DOUBLE_VALUE_IF_NULL -import static org.apache.commons.lang3.math.NumberUtils.toDouble; - -import java.util.Locale; - -public class D2Modulus - implements ExpressionItem { - - @Override - public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - return String.format(Locale.US, "%.1f", - toDouble(visitor.castStringVisit(ctx.expr(0)), 0.0) % - toDouble(visitor.castStringVisit(ctx.expr(1)), 0.0)); + return String.format(Locale.US, "%.1f", firstValue % secondValue) } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Right.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Right.java deleted file mode 100644 index f5f5476350..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Right.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.apache.commons.lang3.StringUtils; -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; - -import static org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorParserUtils.wrap; -import static org.hisp.dhis.antlr.AntlrParserUtils.castDouble; - -public class D2Right - implements ExpressionItem { - - @Override - public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - Double doubleValue = castDouble(visitor.castStringVisit(ctx.expr(1))); - - if (doubleValue % 1 != 0) { - throw new IllegalArgumentException("Number has to be an integer"); - } - - int chars = doubleValue.intValue(); - return wrap(StringUtils.reverse( - StringUtils.substring(StringUtils.reverse(visitor.castStringVisit(ctx.expr(0))), 0, chars))); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Right.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Right.kt new file mode 100644 index 0000000000..e88bc38546 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Right.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorParserUtils.wrap +import org.hisp.dhis.android.core.util.StringUtils +import org.hisp.dhis.antlr.AntlrParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +internal class D2Right : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val doubleValue = AntlrParserUtils.castDouble(visitor.castStringVisit(ctx.expr(1))) + require(doubleValue % 1 == 0.0) { "Number has to be an integer" } + val chars = doubleValue.toInt() + val str = visitor.castStringVisit(ctx.expr(0)) + + return wrap(StringUtils.substring(str?.reversed(), 0, chars)).reversed() + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Round.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Round.kt similarity index 77% rename from core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Round.java rename to core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Round.kt index f06bc5f502..5b6ab0c88f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Round.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Round.kt @@ -25,20 +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.program.programindicatorengine.internal.function -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; +import kotlin.math.roundToInt +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.parser.internal.expression.ParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; - -import static org.apache.commons.lang3.math.NumberUtils.toDouble; - -public class D2Round - implements ExpressionItem { - - @Override - public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - return String.valueOf(Math.round(toDouble(visitor.castStringVisit(ctx.expr(0)), 0.0))); +internal class D2Round : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val value = visitor.castStringVisit(ctx.expr(0))?.toDoubleOrNull() ?: ParserUtils.DOUBLE_VALUE_IF_NULL + return value.roundToInt().toString() } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Split.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Split.java deleted file mode 100644 index 504542da76..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Split.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.apache.commons.lang3.StringUtils; -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; - -import java.util.Arrays; -import java.util.List; - -import static org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorParserUtils.wrap; -import static org.hisp.dhis.antlr.AntlrParserUtils.castDouble; - -public class D2Split - implements ExpressionItem { - - @Override - public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - String input = visitor.castStringVisit(ctx.expr(0)); - String delimiter = visitor.castStringVisit(ctx.expr(1)); - - if (input == null || delimiter == null) { - return ""; - } - - int index = castDouble(visitor.castStringVisit(ctx.expr(2))).intValue(); - - List tokens = Arrays.asList(StringUtils.split(input, delimiter)); - - if (tokens.size() > index && index >= 0) { - return wrap(tokens.get(index)); - } - - return ""; - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Left.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Split.kt similarity index 67% rename from core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Left.java rename to core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Split.kt index c534bc512d..2966956b97 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Left.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Split.kt @@ -25,29 +25,28 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorParserUtils.wrap +import org.hisp.dhis.antlr.AntlrParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext -import org.apache.commons.lang3.StringUtils; -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; +internal class D2Split : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any { + val input = visitor.castStringVisit(ctx.expr(0)) + val delimiter = visitor.castStringVisit(ctx.expr(1)) -import static org.hisp.dhis.android.core.program.programindicatorengine.internal.ProgramIndicatorParserUtils.wrap; -import static org.hisp.dhis.antlr.AntlrParserUtils.castDouble; + return if (input == null || delimiter == null) { + return "" + } else { + val index = AntlrParserUtils.castDouble(visitor.castStringVisit(ctx.expr(2))).toInt() + val tokens = input.split(delimiter) -public class D2Left - implements ExpressionItem { - - @Override - public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - Double doubleValue = castDouble(visitor.castStringVisit(ctx.expr(1))); - - if (doubleValue % 1 != 0) { - throw new IllegalArgumentException("Number has to be an integer"); + if (tokens.size > index && index >= 0) { + wrap(tokens[index]) + } else "" } - - int chars = doubleValue.intValue(); - return wrap(StringUtils.substring(visitor.castStringVisit(ctx.expr(0)), 0, chars)); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Substring.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Substring.java deleted file mode 100644 index 6a1bbb5172..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Substring.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.apache.commons.lang3.StringUtils; -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; - -import static org.hisp.dhis.antlr.AntlrParserUtils.castDouble; - -public class D2Substring - implements ExpressionItem { - - @Override - public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { - String originalString = visitor.castStringVisit(ctx.expr(0)); - return StringUtils.substring( - originalString == null ? "" : originalString, - castDouble(visitor.castStringVisit(ctx.expr(1))).intValue(), - castDouble(visitor.castStringVisit(ctx.expr(2))).intValue()); - } -} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Substring.kt b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Substring.kt new file mode 100644 index 0000000000..d89e0e079f --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2Substring.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.android.core.parser.internal.expression.ExpressionItem +import org.hisp.dhis.android.core.util.StringUtils +import org.hisp.dhis.antlr.AntlrParserUtils +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext + +internal class D2Substring : ExpressionItem { + override fun evaluate(ctx: ExprContext, visitor: CommonExpressionVisitor): Any? { + val originalString = visitor.castStringVisit(ctx.expr(0)) ?: "" + val start = AntlrParserUtils.castDouble(visitor.castStringVisit(ctx.expr(1))).toInt() + val end = AntlrParserUtils.castDouble(visitor.castStringVisit(ctx.expr(2))).toInt() + + return StringUtils.substring(originalString, start, end) + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerQueryFactoryCommonHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerQueryFactoryCommonHelper.kt index 6ccda0c6cd..738a530ce2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerQueryFactoryCommonHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerQueryFactoryCommonHelper.kt @@ -30,9 +30,9 @@ package org.hisp.dhis.android.core.trackedentity.internal import dagger.Reusable import java.util.* import javax.inject.Inject -import org.apache.commons.lang3.time.DateUtils 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.helpers.DateUtils import org.hisp.dhis.android.core.organisationunit.OrganisationUnit import org.hisp.dhis.android.core.organisationunit.OrganisationUnitMode import org.hisp.dhis.android.core.organisationunit.OrganisationUnitProgramLink @@ -234,7 +234,7 @@ internal class TrackerQueryFactoryCommonHelper @Inject constructor( null } else { val startDate = DateUtils.addMonths(Date(), -period.months) - org.hisp.dhis.android.core.arch.helpers.DateUtils.DATE_FORMAT.format(startDate) + DateUtils.DATE_FORMAT.format(startDate) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerSyncLastUpdatedManager.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerSyncLastUpdatedManager.kt index 412582da96..9a9729dae1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerSyncLastUpdatedManager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/internal/TrackerSyncLastUpdatedManager.kt @@ -28,9 +28,9 @@ package org.hisp.dhis.android.core.trackedentity.internal import java.util.Date -import org.apache.commons.lang3.time.DateUtils import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder import org.hisp.dhis.android.core.arch.db.stores.internal.ObjectWithoutUidStore +import org.hisp.dhis.android.core.arch.helpers.DateUtils import org.hisp.dhis.android.core.common.BaseIdentifiableObject import org.hisp.dhis.android.core.program.internal.ProgramDataDownloadParams import org.hisp.dhis.android.core.settings.DownloadPeriod diff --git a/core/src/main/java/org/hisp/dhis/android/core/util/StringUtils.kt b/core/src/main/java/org/hisp/dhis/android/core/util/StringUtils.kt new file mode 100644 index 0000000000..d7f3951f50 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/util/StringUtils.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.hisp.dhis.android.core.util + +import kotlin.math.max + +object StringUtils { + fun substring(str: String?, start: Int, end: Int): String? { + return str?.let { + val positiveEnd = end + .run { + if (this < 0) this + str.length + else this + } + .run { + if (this > str.length) str.length + else this + } + + val positiveStart = start + .run { + if (this < 0) this + str.length + else this + } + + if (positiveStart > positiveEnd) { + "" + } else { + str.substring(max(positiveStart, 0), max(positiveEnd, 0)) + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramAttribute.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramAttribute.java index 2f233ee43d..30df39f3cd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramAttribute.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramAttribute.java @@ -37,7 +37,6 @@ import com.gabrielittner.auto.value.cursor.ColumnAdapter; import com.google.auto.value.AutoValue; -import org.apache.commons.lang3.ArrayUtils; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreObjectWithUidColumnAdapter; import org.hisp.dhis.android.core.common.ObjectWithUid; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; @@ -72,7 +71,7 @@ public static Builder builder() { public abstract Builder toBuilder(); private ObjectWithUid getTokenAt(int position) { - String[] tokens = uid() == null ? ArrayUtils.EMPTY_STRING_ARRAY : uid().split("\\."); + String[] tokens = uid() == null ? new String[] {} : uid().split("\\."); String uid = tokens.length > position ? tokens[position] : null; return uid == null ? null : ObjectWithUid.create(uid); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramDataElement.java b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramDataElement.java index 7b55ff2f08..887e9ac68d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramDataElement.java +++ b/core/src/main/java/org/hisp/dhis/android/core/visualization/DataDimensionItemProgramDataElement.java @@ -37,7 +37,6 @@ import com.gabrielittner.auto.value.cursor.ColumnAdapter; import com.google.auto.value.AutoValue; -import org.apache.commons.lang3.ArrayUtils; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreObjectWithUidColumnAdapter; import org.hisp.dhis.android.core.common.ObjectWithUid; import org.hisp.dhis.android.core.common.ObjectWithUidInterface; @@ -72,7 +71,7 @@ public static Builder builder() { public abstract Builder toBuilder(); private ObjectWithUid getTokenAt(int position) { - String[] tokens = uid() == null ? ArrayUtils.EMPTY_STRING_ARRAY : uid().split("\\."); + String[] tokens = uid() == null ? new String[] {} : uid().split("\\."); String uid = tokens.length > position ? tokens[position] : null; return uid == null ? null : ObjectWithUid.create(uid); } diff --git a/core/src/test/java/org/hisp/dhis/android/core/period/internal/MonthlyPeriodGeneratorShould.java b/core/src/test/java/org/hisp/dhis/android/core/period/internal/MonthlyPeriodGeneratorShould.java index 8cbd7bd47d..3ecbc1e20f 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/period/internal/MonthlyPeriodGeneratorShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/period/internal/MonthlyPeriodGeneratorShould.java @@ -29,7 +29,6 @@ import com.google.common.collect.Lists; -import org.apache.commons.lang3.builder.ToStringExclude; import org.hisp.dhis.android.core.period.Period; import org.hisp.dhis.android.core.period.PeriodType; import org.junit.Test; diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CeilShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CeilShould.kt similarity index 61% rename from core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CeilShould.java rename to core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CeilShould.kt index dca2c15e80..1ddb4412e7 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CeilShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2CeilShould.kt @@ -25,64 +25,50 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class D2CeilShould { - - @Mock - private ExpressionParser.ExprContext context; - - @Mock - private CommonExpressionVisitor visitor; - - @Mock - private ExpressionParser.ExprContext mockedFirstExpr; - - private D2Ceil ceil = new D2Ceil(); +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class D2CeilShould { + private val context: ExprContext = mock() + private val visitor: CommonExpressionVisitor = mock() + private val mockedFirstExpr: ExprContext = mock() + + private val ceil = D2Ceil() @Before - public void setUp() { - when(context.expr(0)).thenReturn(mockedFirstExpr); + fun setUp() { + whenever(context.expr(0)).thenReturn(mockedFirstExpr) } @Test - public void evaluateMustReturnCeiledValue() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("4.1"); - assertThat(ceil.evaluate(context, visitor)).isEqualTo("5"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.8"); - assertThat(ceil.evaluate(context, visitor)).isEqualTo("1"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("5.1"); - assertThat(ceil.evaluate(context, visitor)).isEqualTo("6"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("1"); - assertThat(ceil.evaluate(context, visitor)).isEqualTo("1"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-9.3"); - assertThat(ceil.evaluate(context, visitor)).isEqualTo("-9"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-5.9"); - assertThat(ceil.evaluate(context, visitor)).isEqualTo("-5"); + fun evaluateMustReturnCeiledValue() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("4.1") + assertThat(ceil.evaluate(context, visitor)).isEqualTo("5") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.8") + assertThat(ceil.evaluate(context, visitor)).isEqualTo("1") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("5.1") + assertThat(ceil.evaluate(context, visitor)).isEqualTo("6") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("1") + assertThat(ceil.evaluate(context, visitor)).isEqualTo("1") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-9.3") + assertThat(ceil.evaluate(context, visitor)).isEqualTo("-9") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-5.9") + assertThat(ceil.evaluate(context, visitor)).isEqualTo("-5") } @Test - public void return_zero_when_number_is_invalid() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("not a number"); - - assertThat(ceil.evaluate(context, visitor)).isEqualTo("0"); + fun return_zero_when_number_is_invalid() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("not a number") + assertThat(ceil.evaluate(context, visitor)).isEqualTo("0") } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2FloorShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2FloorShould.kt similarity index 61% rename from core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2FloorShould.java rename to core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2FloorShould.kt index 4fc63cd932..54ee3b25df 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2FloorShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2FloorShould.kt @@ -25,64 +25,50 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class D2FloorShould { - - @Mock - private ExpressionParser.ExprContext context; - - @Mock - private CommonExpressionVisitor visitor; - - @Mock - private ExpressionParser.ExprContext mockedFirstExpr; - - private D2Floor floor = new D2Floor(); +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class D2FloorShould { + private val context: ExprContext = mock() + private val visitor: CommonExpressionVisitor = mock() + private val mockedFirstExpr: ExprContext = mock() + + private val floor = D2Floor() @Before - public void setUp() { - when(context.expr(0)).thenReturn(mockedFirstExpr); + fun setUp() { + whenever(context.expr(0)).thenReturn(mockedFirstExpr) } @Test - public void evaluateMustReturnFlooredValue() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("4.1"); - assertThat(floor.evaluate(context, visitor)).isEqualTo("4"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.8"); - assertThat(floor.evaluate(context, visitor)).isEqualTo("0"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("5.1"); - assertThat(floor.evaluate(context, visitor)).isEqualTo("5"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("1.0"); - assertThat(floor.evaluate(context, visitor)).isEqualTo("1"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-9.3"); - assertThat(floor.evaluate(context, visitor)).isEqualTo("-10"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-5.9"); - assertThat(floor.evaluate(context, visitor)).isEqualTo("-6"); + fun evaluateMustReturnFlooredValue() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("4.1") + assertThat(floor.evaluate(context, visitor)).isEqualTo("4") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.8") + assertThat(floor.evaluate(context, visitor)).isEqualTo("0") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("5.1") + assertThat(floor.evaluate(context, visitor)).isEqualTo("5") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("1.0") + assertThat(floor.evaluate(context, visitor)).isEqualTo("1") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-9.3") + assertThat(floor.evaluate(context, visitor)).isEqualTo("-10") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-5.9") + assertThat(floor.evaluate(context, visitor)).isEqualTo("-6") } @Test - public void return_zero_when_number_is_invalid() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("not a number"); - - assertThat(floor.evaluate(context, visitor)).isEqualTo("0"); + fun return_zero_when_number_is_invalid() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("not a number") + assertThat(floor.evaluate(context, visitor)).isEqualTo("0") } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2LeftShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2LeftShould.java deleted file mode 100644 index fd9170c476..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2LeftShould.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.antlr.ParserExceptionWithoutContext; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class D2LeftShould { - @Mock - private ExpressionParser.ExprContext context; - - @Mock - private CommonExpressionVisitor visitor; - - @Mock - private ExpressionParser.ExprContext mockedFirstExpr; - - @Mock - private ExpressionParser.ExprContext mockedSecondExpr; - - private D2Left left = new D2Left(); - - @Before - public void setUp() { - when(context.expr(0)).thenReturn(mockedFirstExpr); - when(context.expr(1)).thenReturn(mockedSecondExpr); - } - - @Test - public void return_empty_string_for_null_input() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0"); - assertThat(left.evaluate(context, visitor)).isEqualTo(""); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("10"); - assertThat(left.evaluate(context, visitor)).isEqualTo(""); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-10"); - assertThat(left.evaluate(context, visitor)).isEqualTo(""); - } - - @Test - public void return_substring_of_first_argument_from_the_beginning() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("000"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2"); - assertThat(left.evaluate(context, visitor)).isEqualTo("00"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0"); - assertThat(left.evaluate(context, visitor)).isEqualTo(""); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-5"); - assertThat(left.evaluate(context, visitor)).isEqualTo("a"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2"); - assertThat(left.evaluate(context, visitor)).isEqualTo("ab"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("30"); - assertThat(left.evaluate(context, visitor)).isEqualTo("abcdef"); - } - - @Test(expected = ParserExceptionWithoutContext.class) - public void throw_parser_exception_without_context_if_position_is_a_text() { - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("text"); - left.evaluate(context, visitor); - } - - @Test(expected = IllegalArgumentException.class) - public void throw_illegal_argument_when_number_not_an_integer() { - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("6.8"); - left.evaluate(context, visitor); - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2LeftShould.kt b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2LeftShould.kt new file mode 100644 index 0000000000..e3687da8c6 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2LeftShould.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class D2LeftShould { + private val context: ExprContext = mock() + private val visitor: CommonExpressionVisitor = mock() + private val mockedFirstExpr: ExprContext = mock() + private val mockedSecondExpr: ExprContext = mock() + + private val left = D2Left() + + @Before + fun setUp() { + whenever(context.expr(0)).thenReturn(mockedFirstExpr) + whenever(context.expr(1)).thenReturn(mockedSecondExpr) + } + + @Test + fun return_empty_string_for_null_input() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0") + assertThat(left.evaluate(context, visitor)).isEqualTo("") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("10") + assertThat(left.evaluate(context, visitor)).isEqualTo("") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-10") + assertThat(left.evaluate(context, visitor)).isEqualTo("") + } + + @Test + fun return_substring_of_first_argument_from_the_beginning() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("000") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2") + assertThat(left.evaluate(context, visitor)).isEqualTo("00") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0") + assertThat(left.evaluate(context, visitor)).isEqualTo("") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-5") + assertThat(left.evaluate(context, visitor)).isEqualTo("a") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2") + assertThat(left.evaluate(context, visitor)).isEqualTo("ab") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("30") + assertThat(left.evaluate(context, visitor)).isEqualTo("abcdef") + } + + @Test(expected = ParserExceptionWithoutContext::class) + fun throw_parser_exception_without_context_if_position_is_a_text() { + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("text") + left.evaluate(context, visitor) + } + + @Test(expected = IllegalArgumentException::class) + fun throw_illegal_argument_when_number_not_an_integer() { + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("6.8") + left.evaluate(context, visitor) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2ModulusShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2ModulusShould.kt similarity index 52% rename from core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2ModulusShould.java rename to core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2ModulusShould.kt index 5b2c0ccc32..67d8411cfc 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2ModulusShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2ModulusShould.kt @@ -25,69 +25,56 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +@RunWith(MockitoJUnitRunner::class) +class D2ModulusShould { + private val context: ExprContext = mock() + private val visitor: CommonExpressionVisitor = mock() + private val mockedFirstExpr: ExprContext = mock() + private val mockedSecondExpr: ExprContext = mock() -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class D2ModulusShould { - @Mock - private ExpressionParser.ExprContext context; - - @Mock - private CommonExpressionVisitor visitor; - - @Mock - private ExpressionParser.ExprContext mockedFirstExpr; - - @Mock - private ExpressionParser.ExprContext mockedSecondExpr; - - private D2Modulus modulus = new D2Modulus(); + private val modulus = D2Modulus() @Before - public void setUp() { - when(context.expr(0)).thenReturn(mockedFirstExpr); - when(context.expr(1)).thenReturn(mockedSecondExpr); + fun setUp() { + whenever(context.expr(0)).thenReturn(mockedFirstExpr) + whenever(context.expr(1)).thenReturn(mockedSecondExpr) } @Test - public void return_argument_rounded_down_to_nearest_whole_number() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2"); - assertThat(modulus.evaluate(context, visitor)).isEqualTo("0.0"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("11"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("3"); - assertThat(modulus.evaluate(context, visitor)).isEqualTo("2.0"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-11"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("3"); - assertThat(modulus.evaluate(context, visitor)).isEqualTo("-2.0"); + fun return_argument_rounded_down_to_nearest_whole_number() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2") + assertThat(modulus.evaluate(context, visitor)).isEqualTo("0.0") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("11") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("3") + assertThat(modulus.evaluate(context, visitor)).isEqualTo("2.0") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-11") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("3") + assertThat(modulus.evaluate(context, visitor)).isEqualTo("-2.0") } @Test - public void return_NaN_when_invalid_operations() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("2"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0"); - assertThat(modulus.evaluate(context, visitor)).isEqualTo("NaN"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("bad number"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("bad number"); - assertThat(modulus.evaluate(context, visitor)).isEqualTo("NaN"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn(null); - assertThat(modulus.evaluate(context, visitor)).isEqualTo("NaN"); + fun return_NaN_when_invalid_operations() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("2") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0") + assertThat(modulus.evaluate(context, visitor)).isEqualTo("NaN") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("bad number") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("bad number") + assertThat(modulus.evaluate(context, visitor)).isEqualTo("NaN") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn(null) + assertThat(modulus.evaluate(context, visitor)).isEqualTo("NaN") } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RightShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RightShould.java deleted file mode 100644 index 3cbdfdf74d..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RightShould.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.antlr.ParserExceptionWithoutContext; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class D2RightShould { - @Mock - private ExpressionParser.ExprContext context; - - @Mock - private CommonExpressionVisitor visitor; - - @Mock - private ExpressionParser.ExprContext mockedFirstExpr; - - @Mock - private ExpressionParser.ExprContext mockedSecondExpr; - - private D2Right right = new D2Right(); - - @Before - public void setUp() { - when(context.expr(0)).thenReturn(mockedFirstExpr); - when(context.expr(1)).thenReturn(mockedSecondExpr); - } - - @Test - public void return_empty_string_for_null_input() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0"); - assertThat(right.evaluate(context, visitor)).isEqualTo(""); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("10"); - assertThat(right.evaluate(context, visitor)).isEqualTo(""); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-10"); - assertThat(right.evaluate(context, visitor)).isEqualTo(""); - } - - @Test - public void return_substring_of_first_argument_from_the_beginning() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0"); - assertThat(right.evaluate(context, visitor)).isEqualTo(""); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-5"); - assertThat(right.evaluate(context, visitor)).isEqualTo("f"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2"); - assertThat(right.evaluate(context, visitor)).isEqualTo("ef"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("30"); - assertThat(right.evaluate(context, visitor)).isEqualTo("abcdef"); - } - - @Test(expected = ParserExceptionWithoutContext.class) - public void throw_parser_exception_without_context_if_position_is_a_text() { - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("text"); - right.evaluate(context, visitor); - } - - @Test(expected = IllegalArgumentException.class) - public void throw_illegal_argument_when_number_not_an_integer() { - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("6.8"); - right.evaluate(context, visitor); - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RightShould.kt b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RightShould.kt new file mode 100644 index 0000000000..eda0d576ba --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RightShould.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class D2RightShould { + private val context: ExprContext = mock() + private val visitor: CommonExpressionVisitor = mock() + private val mockedFirstExpr: ExprContext = mock() + private val mockedSecondExpr: ExprContext = mock() + + private val right = D2Right() + + @Before + fun setUp() { + whenever(context.expr(0)).thenReturn(mockedFirstExpr) + whenever(context.expr(1)).thenReturn(mockedSecondExpr) + } + + @Test + fun return_empty_string_for_null_input() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0") + assertThat(right.evaluate(context, visitor)).isEqualTo("") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("10") + assertThat(right.evaluate(context, visitor)).isEqualTo("") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-10") + assertThat(right.evaluate(context, visitor)).isEqualTo("") + } + + @Test + fun return_substring_of_first_argument_from_the_beginning() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0") + assertThat(right.evaluate(context, visitor)).isEqualTo("") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-5") + assertThat(right.evaluate(context, visitor)).isEqualTo("f") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2") + assertThat(right.evaluate(context, visitor)).isEqualTo("ef") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("30") + assertThat(right.evaluate(context, visitor)).isEqualTo("abcdef") + } + + @Test(expected = ParserExceptionWithoutContext::class) + fun throw_parser_exception_without_context_if_position_is_a_text() { + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("text") + right.evaluate(context, visitor) + } + + @Test(expected = IllegalArgumentException::class) + fun throw_illegal_argument_when_number_not_an_integer() { + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("6.8") + right.evaluate(context, visitor) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RoundShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RoundShould.kt similarity index 60% rename from core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RoundShould.java rename to core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RoundShould.kt index cfeff19892..dca4b39e66 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RoundShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2RoundShould.kt @@ -25,63 +25,50 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class D2RoundShould { - @Mock - private ExpressionParser.ExprContext context; - - @Mock - private CommonExpressionVisitor visitor; - - @Mock - private ExpressionParser.ExprContext mockedFirstExpr; - - private D2Round round = new D2Round(); +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class D2RoundShould { + private val context: ExprContext = mock() + private val visitor: CommonExpressionVisitor = mock() + private val mockedFirstExpr: ExprContext = mock() + + private val round = D2Round() @Before - public void setUp() { - when(context.expr(0)).thenReturn(mockedFirstExpr); + fun setUp() { + whenever(context.expr(0)).thenReturn(mockedFirstExpr) } @Test - public void return_argument_rounded_up_to_nearest_whole_number() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0"); - assertThat(round.evaluate(context, visitor)).isEqualTo("0"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.8"); - assertThat(round.evaluate(context, visitor)).isEqualTo("1"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.4999"); - assertThat(round.evaluate(context, visitor)).isEqualTo("0"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.5001"); - assertThat(round.evaluate(context, visitor)).isEqualTo("1"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-9.3"); - assertThat(round.evaluate(context, visitor)).isEqualTo("-9"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-9.8"); - assertThat(round.evaluate(context, visitor)).isEqualTo("-10"); + fun return_argument_rounded_up_to_nearest_whole_number() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0") + assertThat(round.evaluate(context, visitor)).isEqualTo("0") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.8") + assertThat(round.evaluate(context, visitor)).isEqualTo("1") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.4999") + assertThat(round.evaluate(context, visitor)).isEqualTo("0") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("0.5001") + assertThat(round.evaluate(context, visitor)).isEqualTo("1") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-9.3") + assertThat(round.evaluate(context, visitor)).isEqualTo("-9") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("-9.8") + assertThat(round.evaluate(context, visitor)).isEqualTo("-10") } @Test - public void return_zero_when_number_is_invalid() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("not a number"); - - assertThat(round.evaluate(context, visitor)).isEqualTo("0"); + fun return_zero_when_number_is_invalid() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("not a number") + assertThat(round.evaluate(context, visitor)).isEqualTo("0") } } diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SplitShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SplitShould.java deleted file mode 100644 index e8fd65a856..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SplitShould.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.antlr.ParserExceptionWithoutContext; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class D2SplitShould { - @Mock - private ExpressionParser.ExprContext context; - - @Mock - private CommonExpressionVisitor visitor; - - @Mock - private ExpressionParser.ExprContext mockedFirstExpr; - - @Mock - private ExpressionParser.ExprContext mockedSecondExpr; - - @Mock - private ExpressionParser.ExprContext mockedThirdExpr; - - private D2Split functionToTest = new D2Split(); - - @Before - public void setUp() { - when(context.expr(0)).thenReturn(mockedFirstExpr); - when(context.expr(1)).thenReturn(mockedSecondExpr); - when(context.expr(2)).thenReturn(mockedThirdExpr); - } - - @Test - public void return_empty_string_for_null_inputs() { - assertSplit(null, null, "0", ""); - assertSplit("", null, "0", ""); - assertSplit(null, "", "0", ""); - } - - @Test - public void return_the_nth_field_of_the_splited_first_argument() { - assertSplit("a,b,c", ",", "0", "a"); - assertSplit("a,b,c", ",", "2", "c"); - assertSplit("a,;b,;c", ",;", "1", "b"); - } - - @Test - public void return_empty_string_if_field_index_is_out_of_bounds() { - assertSplit("a,b,c", ",", "10", ""); - assertSplit("a,b,c", ",", "-1", ""); - } - - @Test(expected = ParserExceptionWithoutContext.class) - public void throw_parser_exception_without_context_if_position_is_a_text() { - assertSplit("test_variable_one", "variable", "text", null); - } - - private void assertSplit(String input, String delimiter, String index, String zScore) { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(input); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn(delimiter); - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn(index); - assertThat(functionToTest.evaluate(context, visitor)).isEqualTo((zScore)); - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SplitShould.kt b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SplitShould.kt new file mode 100644 index 0000000000..7371cea477 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SplitShould.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class D2SplitShould { + private val context: ExprContext = mock() + private val visitor: CommonExpressionVisitor = mock() + private val mockedFirstExpr: ExprContext = mock() + private val mockedSecondExpr: ExprContext = mock() + private val mockedThirdExpr: ExprContext = mock() + + private val functionToTest = D2Split() + + @Before + fun setUp() { + whenever(context.expr(0)).thenReturn(mockedFirstExpr) + whenever(context.expr(1)).thenReturn(mockedSecondExpr) + whenever(context.expr(2)).thenReturn(mockedThirdExpr) + } + + @Test + fun return_empty_string_for_null_inputs() { + assertSplit(null, null, "0", "") + assertSplit("", null, "0", "") + assertSplit(null, "", "0", "") + } + + @Test + fun return_the_nth_field_of_the_splited_first_argument() { + assertSplit("a,b,c", ",", "0", "a") + assertSplit("a,b,c", ",", "2", "c") + assertSplit("a,;b,;c", ",;", "1", "b") + } + + @Test + fun return_empty_string_if_field_index_is_out_of_bounds() { + assertSplit("a,b,c", ",", "10", "") + assertSplit("a,b,c", ",", "-1", "") + } + + @Test(expected = ParserExceptionWithoutContext::class) + fun throw_parser_exception_without_context_if_position_is_a_text() { + assertSplit("test_variable_one", "variable", "text", null) + } + + private fun assertSplit(input: String?, delimiter: String?, index: String, zScore: String?) { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(input) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn(delimiter) + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn(index) + assertThat(functionToTest.evaluate(context, visitor)).isEqualTo(zScore) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SubStringShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SubStringShould.java deleted file mode 100644 index b1562f4a58..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SubStringShould.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.program.programindicatorengine.internal.function; - -import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor; -import org.hisp.dhis.antlr.ParserExceptionWithoutContext; -import org.hisp.dhis.parser.expression.antlr.ExpressionParser; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class D2SubStringShould { - @Mock - private ExpressionParser.ExprContext context; - - @Mock - private CommonExpressionVisitor visitor; - - @Mock - private ExpressionParser.ExprContext mockedFirstExpr; - - @Mock - private ExpressionParser.ExprContext mockedSecondExpr; - - @Mock - private ExpressionParser.ExprContext mockedThirdExpr; - - private D2Substring functionToTest = new D2Substring(); - - @Before - public void setUp() { - when(context.expr(0)).thenReturn(mockedFirstExpr); - when(context.expr(1)).thenReturn(mockedSecondExpr); - when(context.expr(2)).thenReturn(mockedThirdExpr); - } - - @Test - public void return_empty_string_for_null_inputs() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0"); - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn("0"); - - assertThat(functionToTest.evaluate(context, visitor)).isEqualTo(""); - - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn("10"); - assertThat(functionToTest.evaluate(context, visitor)).isEqualTo(""); - } - - @Test - public void return_substring_from_start_index_to_end_index_of_input_string() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0"); - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn("0"); - - assertThat(functionToTest.evaluate(context, visitor)).isEqualTo(""); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0"); - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn("1"); - assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("a"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-10"); - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn("1"); - assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("a"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2"); - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn("4"); - assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("cd"); - - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2"); - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn("10"); - assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("cdef"); - } - - @Test(expected = ParserExceptionWithoutContext.class) - public void throw_parser_exception_without_context_if_start_index_is_a_text() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("test_variable_one"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("variable"); - - functionToTest.evaluate(context, visitor); - } - - @Test(expected = ParserExceptionWithoutContext.class) - public void throw_parser_exception_without_context_if_end_index_is_a_text() { - when(visitor.castStringVisit(mockedFirstExpr)).thenReturn("test_variable_one"); - when(visitor.castStringVisit(mockedSecondExpr)).thenReturn("3"); - when(visitor.castStringVisit(mockedThirdExpr)).thenReturn("ede"); - - functionToTest.evaluate(context, visitor); - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SubStringShould.kt b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SubStringShould.kt new file mode 100644 index 0000000000..1415f556a4 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/function/D2SubStringShould.kt @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.program.programindicatorengine.internal.function + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.parser.internal.expression.CommonExpressionVisitor +import org.hisp.dhis.antlr.ParserExceptionWithoutContext +import org.hisp.dhis.parser.expression.antlr.ExpressionParser.ExprContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +class D2SubStringShould { + private val context: ExprContext = mock() + private val visitor: CommonExpressionVisitor = mock() + private val mockedFirstExpr: ExprContext = mock() + private val mockedSecondExpr: ExprContext = mock() + private val mockedThirdExpr: ExprContext = mock() + + private val functionToTest = D2Substring() + + @Before + fun setUp() { + whenever(context.expr(0)).thenReturn(mockedFirstExpr) + whenever(context.expr(1)).thenReturn(mockedSecondExpr) + whenever(context.expr(2)).thenReturn(mockedThirdExpr) + } + + @Test + fun return_empty_string_for_null_inputs() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn(null) + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0") + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn("0") + assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("") + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn("10") + assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("") + } + + @Test + fun return_substring_from_start_index_to_end_index_of_input_string() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0") + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn("0") + assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("0") + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn("1") + assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("a") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("-10") + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn("1") + assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("a") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2") + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn("4") + assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("cd") + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("abcdef") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("2") + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn("10") + assertThat(functionToTest.evaluate(context, visitor)).isEqualTo("cdef") + } + + @Test(expected = ParserExceptionWithoutContext::class) + fun throw_parser_exception_without_context_if_start_index_is_a_text() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("test_variable_one") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("variable") + functionToTest.evaluate(context, visitor) + } + + @Test(expected = ParserExceptionWithoutContext::class) + fun throw_parser_exception_without_context_if_end_index_is_a_text() { + whenever(visitor.castStringVisit(mockedFirstExpr)).thenReturn("test_variable_one") + whenever(visitor.castStringVisit(mockedSecondExpr)).thenReturn("3") + whenever(visitor.castStringVisit(mockedThirdExpr)).thenReturn("ede") + functionToTest.evaluate(context, visitor) + } +} From e43c3ea600f64590055daf73b82452dac5279e2f Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Tue, 27 Dec 2022 14:58:45 +0100 Subject: [PATCH 036/201] [ANDROSDK-1614] Remove CategoryOptionComboChildrenAppender --- .../internal/CategoryComboEntityDIModule.java | 2 - .../CategoryOptionComboChildrenAppender.java | 57 ------------------- 2 files changed, 59 deletions(-) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboChildrenAppender.java diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboEntityDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboEntityDIModule.java index a93b939a05..f5e21ccb58 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboEntityDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryComboEntityDIModule.java @@ -75,8 +75,6 @@ OrphanCleaner orphanCleaner(DatabaseAdapter Map> childrenAppenders(DatabaseAdapter databaseAdapter) { return new HashMap>() {{ put(CategoryComboFields.CATEGORIES, CategoryCategoryComboChildrenAppender.create(databaseAdapter)); - put(CategoryComboFields.CATEGORY_OPTION_COMBOS, - CategoryOptionComboChildrenAppender.create(databaseAdapter)); }}; } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboChildrenAppender.java b/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboChildrenAppender.java deleted file mode 100644 index 96976c5ef3..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/category/internal/CategoryOptionComboChildrenAppender.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.android.core.category.internal; - -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; -import org.hisp.dhis.android.core.category.CategoryCombo; -import org.hisp.dhis.android.core.category.CategoryOptionCombo; - -import java.util.List; - -final class CategoryOptionComboChildrenAppender extends ChildrenAppender { - - private final CategoryOptionComboStore store; - - private CategoryOptionComboChildrenAppender(CategoryOptionComboStore store) { - this.store = store; - } - - @Override - public CategoryCombo appendChildren(CategoryCombo categoryCombo) { - CategoryCombo.Builder builder = categoryCombo.toBuilder(); - List optionCombos = store.getForCategoryCombo(categoryCombo.uid()); - return builder.categoryOptionCombos(optionCombos).build(); - } - - static ChildrenAppender create(DatabaseAdapter databaseAdapter) { - return new CategoryOptionComboChildrenAppender( - CategoryOptionComboStoreImpl.create(databaseAdapter) - ); - } -} \ No newline at end of file From d996599282f81749ee52568de91c008a955f7e8e Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Tue, 27 Dec 2022 14:59:04 +0100 Subject: [PATCH 037/201] [ANDROSDK-1614] Remove withCategoryOptionCombos method in CategoryComboCollectionRepository --- .../core/category/CategoryComboCollectionRepository.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryComboCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryComboCollectionRepository.java index e549c4d8f1..efaf0daf11 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/category/CategoryComboCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/category/CategoryComboCollectionRepository.java @@ -27,6 +27,8 @@ */ package org.hisp.dhis.android.core.category; +import static org.hisp.dhis.android.core.category.CategoryComboTableInfo.Columns; + 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; @@ -41,8 +43,6 @@ import dagger.Reusable; -import static org.hisp.dhis.android.core.category.CategoryComboTableInfo.Columns; - @Reusable public final class CategoryComboCollectionRepository extends ReadOnlyIdentifiableCollectionRepositoryImpl { @@ -62,8 +62,4 @@ public BooleanFilterConnector byIsDefault() { public CategoryComboCollectionRepository withCategories() { return cf.withChild(CategoryComboFields.CATEGORIES); } - - public CategoryComboCollectionRepository withCategoryOptionCombos() { - return cf.withChild(CategoryComboFields.CATEGORY_OPTION_COMBOS); - } } \ No newline at end of file From 25cea3d00c4c6abfa94d8df766707b01f2ce59d1 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Tue, 27 Dec 2022 14:59:22 +0100 Subject: [PATCH 038/201] [ANDROSDK-1614] Update tests --- ...ositoryOneMethodMockIntegrationShould.java | 3 +-- ...ectionRepositoryMockIntegrationShould.java | 26 ++++++------------- .../CategoryModuleMockIntegrationShould.java | 6 ++--- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/arch/repositories/collection/CollectionRepositoryOneMethodMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/arch/repositories/collection/CollectionRepositoryOneMethodMockIntegrationShould.java index 016e9eb7c2..ec71dd4ba6 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/arch/repositories/collection/CollectionRepositoryOneMethodMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/arch/repositories/collection/CollectionRepositoryOneMethodMockIntegrationShould.java @@ -77,9 +77,8 @@ public void get_first_when_filter_limits_to_no_objects() { @Test public void get_with_all_children_returns_object_children() { CategoryCombo combo = d2.categoryModule().categoryCombos() - .withCategories().withCategoryOptionCombos().one().blockingGet(); + .withCategories().one().blockingGet(); assertThat(combo.uid()).isEqualTo(BIRTH_UID); assertThat(combo.categories().size()).isEqualTo(2); - assertThat(CategoryComboInternalAccessor.accessCategoryOptionCombos(combo).size()).isEqualTo(2); } } \ No newline at end of file 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 b0d6cd023f..63d2077035 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-2022, University of Oslo * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * Neither the name of the HISP project nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -28,9 +28,10 @@ package org.hisp.dhis.android.testapp.category; +import static com.google.common.truth.Truth.assertThat; + import org.hisp.dhis.android.core.category.CategoryCombo; import org.hisp.dhis.android.core.category.CategoryComboCollectionRepository; -import org.hisp.dhis.android.core.category.CategoryComboInternalAccessor; import org.hisp.dhis.android.core.common.BaseIdentifiableObject; import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; import org.hisp.dhis.android.core.utils.runner.D2JunitRunner; @@ -41,17 +42,15 @@ import java.util.Date; import java.util.List; -import static com.google.common.truth.Truth.assertThat; - @RunWith(D2JunitRunner.class) public class CategoryComboCollectionRepositoryMockIntegrationShould extends BaseMockIntegrationTestFullDispatcher { private final String BEFORE_DATE = "2007-12-24T12:24:25.203"; private final String IN_BETWEEN_DATE = "2016-04-16T18:04:34.745"; - private final String AFTER_DATE = "2017-12-24T12:24:25.203"; + private final String AFTER_DATE = "2017-12-24T12:24:25.203"; - private final String BIRTH_UID = "m2jTvAj5kkm"; - private final String DEFAULT_UID = "p0KPaWEg3cf"; + private final String BIRTH_UID = "m2jTvAj5kkm"; + private final String DEFAULT_UID = "p0KPaWEg3cf"; @Test public void find_objects_with_equal_name() { @@ -244,13 +243,4 @@ public void include_categories_as_children() { .blockingGet(); assertThat(categoryCombo.categories().size()).isEqualTo(2); } - - @Test - public void include_category_option_combos_as_children() { - CategoryCombo categoryCombo = d2.categoryModule().categoryCombos() - .withCategoryOptionCombos() - .uid("m2jTvAj5kkm") - .blockingGet(); - assertThat(CategoryComboInternalAccessor.accessCategoryOptionCombos(categoryCombo).size()).isEqualTo(2); - } } \ No newline at end of file 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 4e595e3216..f2d1285a96 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 @@ -59,7 +59,7 @@ public void allow_access_to_combos_without_children() { @Test public void allow_access_to_combos_with_category_option_combos() { - List combos = d2.categoryModule().categoryCombos().withCategoryOptionCombos().blockingGet(); + List combos = d2.categoryModule().categoryCombos().blockingGet(); assertThat(combos.size()).isEqualTo(2); for (CategoryCombo combo : combos) { assertThat(accessCategoryOptionCombos(combo) == null).isFalse(); @@ -87,7 +87,7 @@ public void allow_access_to_combo_by_uid_without_children() { @Test public void allow_access_to_combo_by_uid_with_category_option_combos() { - CategoryCombo combo = d2.categoryModule().categoryCombos().withCategoryOptionCombos().uid("m2jTvAj5kkm").blockingGet(); + CategoryCombo combo = d2.categoryModule().categoryCombos().uid("m2jTvAj5kkm").blockingGet(); assertThat(combo.uid()).isEqualTo("m2jTvAj5kkm"); assertThat(combo.code()).isEqualTo("BIRTHS"); assertThat(combo.name()).isEqualTo("Births"); @@ -105,7 +105,7 @@ public void dont_fail_when_asking_for_combos_without_children_when_not_in_databa @Test public void dont_fail_when_asking_for_combos_with_children_when_not_in_database() { - CategoryCombo combo = d2.categoryModule().categoryCombos().withCategories().withCategoryOptionCombos() + CategoryCombo combo = d2.categoryModule().categoryCombos().withCategories() .uid("nonExistentId").blockingGet(); assertThat(combo == null).isTrue(); } From 2706400bc9cb58649bf00e926417676ca3aa583e Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Tue, 27 Dec 2022 15:23:53 +0100 Subject: [PATCH 039/201] [ANDROSDK-1614] Update CategoryModuleMockIntegrationShould --- .../CategoryModuleMockIntegrationShould.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) 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 f2d1285a96..45b2b11ee8 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 @@ -57,15 +57,6 @@ public void allow_access_to_combos_without_children() { } } - @Test - public void allow_access_to_combos_with_category_option_combos() { - List combos = d2.categoryModule().categoryCombos().blockingGet(); - assertThat(combos.size()).isEqualTo(2); - for (CategoryCombo combo : combos) { - assertThat(accessCategoryOptionCombos(combo) == null).isFalse(); - } - } - @Test public void allow_access_to_combos_with_categories() { List combos = d2.categoryModule().categoryCombos().withCategories().blockingGet(); @@ -92,9 +83,7 @@ public void allow_access_to_combo_by_uid_with_category_option_combos() { assertThat(combo.code()).isEqualTo("BIRTHS"); assertThat(combo.name()).isEqualTo("Births"); List optionCombos = accessCategoryOptionCombos(combo); - assertThat(optionCombos == null).isFalse(); - assertThat(optionCombos.size()).isEqualTo(2); - assertThat(optionCombos.iterator().next().name()).isEqualTo("Trained TBA, At PHU"); + assertThat(optionCombos == null).isTrue(); } @Test From b1d339eff5037e60d4eb5234b9cbc734122c0ecd Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 28 Dec 2022 11:53:41 +0100 Subject: [PATCH 040/201] [ANDROSDK-1612] Add children Appender for DataValueConflict module --- .../core/datavalue/internal/DataValueConflictDIModule.kt | 7 +++++++ 1 file changed, 7 insertions(+) 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 index 8d5a5bf77f..484d4ef96c 100644 --- 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 @@ -33,6 +33,7 @@ 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.arch.repositories.children.internal.ChildrenAppender import org.hisp.dhis.android.core.datavalue.DataValueConflict @Module @@ -43,4 +44,10 @@ internal class DataValueConflictDIModule { fun store(databaseAdapter: DatabaseAdapter): ObjectStore { return DataValueConflictStore.create(databaseAdapter) } + + @Provides + @Reusable + fun childrenAppenders(): Map> { + return emptyMap() + } } From 7d38b609f71cbc8da9e1c2d75eb0ef3a1592c877 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 28 Dec 2022 11:54:58 +0100 Subject: [PATCH 041/201] [ANDROSDK-1612] Add a data value conflict repository --- ...DataValueConflictCollectionRepository.java | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java new file mode 100644 index 0000000000..c579b7c24a --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.hisp.dhis.android.core.datavalue; + +import static org.hisp.dhis.android.core.datavalue.DataValueConflictTableInfo.Columns; + +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.ReadOnlyCollectionRepository; +import org.hisp.dhis.android.core.arch.repositories.collection.internal.ReadOnlyCollectionRepositoryImpl; +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.scope.RepositoryScope; +import org.hisp.dhis.android.core.common.IdentifiableColumns; +import org.hisp.dhis.android.core.imports.ImportStatus; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitProgramLinkTableInfo; +import org.hisp.dhis.android.core.user.UserOrganisationUnitLinkTableInfo; + +import java.util.Collections; +import java.util.Map; + +import javax.inject.Inject; + +import dagger.Reusable; + +@Reusable +public final class DataValueConflictCollectionRepository + extends ReadOnlyCollectionRepositoryImpl + implements ReadOnlyCollectionRepository { + + ObjectStore store; + + @Inject + DataValueConflictCollectionRepository(final ObjectStore store, + final Map> childrenAppenders, + final RepositoryScope scope) { + super(store, childrenAppenders, scope, new FilterConnectorFactory<>(scope, + s -> new DataValueConflictCollectionRepository(store, childrenAppenders, s))); + this.store = store; + } + + public DataValueConflictCollectionRepository byDataSet(String dataSetUid) { + return cf.subQuery(DataValueByDataSetQueryHelper.getKey()) + .rawSubQuery(DataValueByDataSetQueryHelper.getOperator(), + DataValueByDataSetQueryHelper.whereClause(dataSetUid)); + } + + public StringFilterConnector byConflict() { + return cf.string(Columns.CONFLICT); + } + + public StringFilterConnector byValue() { + return cf.string(Columns.VALUE); + } + + public StringFilterConnector byAttributeOptionCombo() { + return cf.string(Columns.ATTRIBUTE_OPTION_COMBO); + } + + public StringFilterConnector byCategoryOptionCombo() { + return cf.string(Columns.CATEGORY_OPTION_COMBO); + } + + public StringFilterConnector byDataElement() { + return cf.string(Columns.DATA_ELEMENT); + } + + public StringFilterConnector byPeriod() { + return cf.string(Columns.PERIOD); + } + + public StringFilterConnector byOrganisationUnitUid() { + return cf.string(Columns.ORG_UNIT); + } + + public StringFilterConnector byErrorCode() { + return cf.string(Columns.ERROR_CODE); + } + + public StringFilterConnector byDisplayDescription() { + return cf.string(Columns.DISPLAY_DESCRIPTION); + } + + public EnumFilterConnector byStatus() { + return cf.enumC(Columns.STATUS); + } + + public DateFilterConnector byCreated() { + return cf.date(Columns.CREATED); + } +} From edf81c0cbf18aad0732cd1ac409ce6259bbd60c1 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 28 Dec 2022 11:55:12 +0100 Subject: [PATCH 042/201] [ANDROSDK-1612] Add the repo to the module --- .../dhis/android/core/datavalue/DataValueModule.java | 1 + .../core/datavalue/internal/DataValueModuleImpl.java | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueModule.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueModule.java index 9fc9927c93..c2888c92aa 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueModule.java @@ -30,4 +30,5 @@ public interface DataValueModule { DataValueCollectionRepository dataValues(); + DataValueConflictCollectionRepository dataValueConflicts(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleImpl.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleImpl.java index d47cb8ef0f..7c3748be46 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueModuleImpl.java @@ -29,6 +29,7 @@ package org.hisp.dhis.android.core.datavalue.internal; import org.hisp.dhis.android.core.datavalue.DataValueCollectionRepository; +import org.hisp.dhis.android.core.datavalue.DataValueConflictCollectionRepository; import org.hisp.dhis.android.core.datavalue.DataValueModule; import javax.inject.Inject; @@ -39,14 +40,22 @@ public final class DataValueModuleImpl implements DataValueModule { private final DataValueCollectionRepository dataValues; + private final DataValueConflictCollectionRepository dataValueConflicts; @Inject - DataValueModuleImpl(DataValueCollectionRepository dataValueCollectionRepository) { + DataValueModuleImpl(DataValueCollectionRepository dataValueCollectionRepository, + DataValueConflictCollectionRepository dataValueConflicts) { this.dataValues = dataValueCollectionRepository; + this.dataValueConflicts = dataValueConflicts; } @Override public DataValueCollectionRepository dataValues() { return dataValues; } + + @Override + public DataValueConflictCollectionRepository dataValueConflicts() { + return dataValueConflicts; + } } From 7335c8fbb804f25884e6d00700db7bad350111ff Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 28 Dec 2022 11:55:32 +0100 Subject: [PATCH 043/201] [ANDROSDK-1612] Update DataValueConflictSamples --- .../android/core/data/datavalue/DataValueConflictSamples.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 829aacdeb8..ec5e5962e0 100644 --- 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 @@ -41,7 +41,7 @@ object DataValueConflictSamples { .attributeOptionCombo("HllvX50cXC0") .categoryOptionCombo("Prlt0C1RF0s") .created(getDate("021-06-02T12:38:53.743")) - .dataElement("UOlfIjgN8X6") + .dataElement("g9eOBujte1U") .period("202101") .orgUnit("DiszpKrYNg8").build() } From 878a0dda73a463146945be5fb5e86d76c8b7ab5b Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 28 Dec 2022 11:56:55 +0100 Subject: [PATCH 044/201] [ANDROSDK-1612] Add some dataValueConflicts to the mocked db --- .../mock/BaseMockIntegrationTestFullDispatcher.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt index 995c5d4aff..41442c10d0 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt @@ -27,10 +27,12 @@ */ package org.hisp.dhis.android.core.utils.integration.mock +import org.hisp.dhis.android.core.data.datavalue.DataValueConflictSamples 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.create +import org.hisp.dhis.android.core.datavalue.internal.DataValueConflictStore import org.hisp.dhis.android.core.imports.ImportStatus import org.hisp.dhis.android.core.imports.internal.TrackerImportConflictStoreImpl import org.hisp.dhis.android.core.maintenance.D2Error @@ -134,6 +136,13 @@ abstract class BaseMockIntegrationTestFullDispatcher : BaseMockIntegrationTest() .status(ImportStatus.ERROR) .build() ) + + val dataValueConflictStore = DataValueConflictStore.create(databaseAdapter) + + dataValueConflictStore.insert(DataValueConflictSamples.get()) + dataValueConflictStore.insert(DataValueConflictSamples.get().toBuilder() + .dataElement("bx6fsa0t90x") + .build()) } private fun storeSomeKeyValuesInLocalDataStore() { From fac53843c111be31d75850e38b4253c7fc448197 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Wed, 28 Dec 2022 11:57:20 +0100 Subject: [PATCH 045/201] [ANDROSDK-1612] Add the DataValueConflictCollectionRepositoryMockIntegrationShould --- ...llectionRepositoryMockIntegrationShould.kt | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt new file mode 100644 index 0000000000..af3ce434f2 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.testapp.datavalue + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.imports.ImportStatus +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockIntegrationTestFullDispatcher() { + + @Test + fun find_all() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .blockingGet() + + assertThat(dataValues.size).isEqualTo(7) + } + + @Test + fun filter_by_conflict() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byConflict() + .eq("conflict") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(7) + } + + @Test + fun filter_by_data_element() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byDataElement() + .eq("g9eOBujte1U") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(7) + } + + @Test + fun filter_by_period() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byPeriod() + .eq("2018") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(1) + } + + @Test + fun filter_by_organisation_unit() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byOrganisationUnitUid() + .eq("DiszpKrYNg8") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(6) + } + + @Test + fun filter_by_category_option_combo() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byCategoryOptionCombo() + .eq("Gmbgme7z9BF") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(6) + } + + @Test + fun filter_by_attribute_option_combo() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byAttributeOptionCombo() + .eq("bRowv6yZOF2") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(6) + } + + @Test + fun filter_by_value() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byValue() + .eq("11") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(1) + } + + @Test + fun filter_by_created() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byCreated() + .eq(DateUtils.DATE_FORMAT.parse("2010-02-11T00:00:00.000+0100")) + .blockingGet() + + assertThat(dataValues.size).isEqualTo(1) + } + + @Test + fun filter_by_state() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byStatus().eq(ImportStatus.SUCCESS) + .blockingGet() + + assertThat(dataValues.size).isEqualTo(7) + } + + @Test + fun filter_by_display_description() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byDisplayDescription() + .eq("display description") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(7) + } + + @Test + fun filter_by_error_code() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byErrorCode() + .eq("ERROR_CODE") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(7) + } + + @Test + fun filter_by_dataset() { + val dataValues = d2.dataValueModule().dataValueConflicts() + .byDataSet("lyLU2wR22tC") + .blockingGet() + + assertThat(dataValues.size).isEqualTo(5) + } +} From 30cf283ef041f5e81f833d613e307377fa946a5e Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 29 Dec 2022 12:54:41 +0100 Subject: [PATCH 046/201] [ANDROSDK-1612] Update DataValueConflictSamples --- .../core/data/datavalue/DataValueConflictSamples.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 index ec5e5962e0..6c356a276d 100644 --- 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 @@ -32,17 +32,20 @@ import java.text.ParseException import java.util.Date import org.hisp.dhis.android.core.common.BaseIdentifiableObject import org.hisp.dhis.android.core.datavalue.DataValueConflict +import org.hisp.dhis.android.core.imports.ImportStatus object DataValueConflictSamples { fun get(): DataValueConflict { return DataValueConflict.builder() .value("KKK") - .attributeOptionCombo("HllvX50cXC0") - .categoryOptionCombo("Prlt0C1RF0s") + .attributeOptionCombo("bRowv6yZOF2") + .categoryOptionCombo("Gmbgme7z9BF") .created(getDate("021-06-02T12:38:53.743")) .dataElement("g9eOBujte1U") .period("202101") + .status(ImportStatus.SUCCESS) + .displayDescription("display_description") .orgUnit("DiszpKrYNg8").build() } From 34a1e3c350739f784e42e7320af05753dfa392f3 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 29 Dec 2022 12:55:10 +0100 Subject: [PATCH 047/201] [ANDROSDK-1612] Update Data values in mocked DB --- .../mock/BaseMockIntegrationTestFullDispatcher.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt index 41442c10d0..44746c2ab5 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt @@ -141,7 +141,18 @@ abstract class BaseMockIntegrationTestFullDispatcher : BaseMockIntegrationTest() dataValueConflictStore.insert(DataValueConflictSamples.get()) dataValueConflictStore.insert(DataValueConflictSamples.get().toBuilder() + .value("5") + .conflict("conflict") .dataElement("bx6fsa0t90x") + .categoryOptionCombo("bRowv6yZOF2") + .status(ImportStatus.WARNING) + .build()) + dataValueConflictStore.insert(DataValueConflictSamples.get().toBuilder() + .attributeOptionCombo("DwrQJzeChWp") + .categoryOptionCombo("Gmbgme7z9BF") + .period("202201") + .orgUnit("YuQRtpLP10I") + .displayDescription("display_description_other") .build()) } From 3e2ba588c8d78d62fde2a6bdb98cd456f81cae3f Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 29 Dec 2022 12:55:31 +0100 Subject: [PATCH 048/201] [ANDROSDK-1612] Adapt DataValueConflictCollectionRepositoryMockIntegrationShould test to the DB --- ...llectionRepositoryMockIntegrationShould.kt | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt index af3ce434f2..c98503220e 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt @@ -43,7 +43,7 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg val dataValues = d2.dataValueModule().dataValueConflicts() .blockingGet() - assertThat(dataValues.size).isEqualTo(7) + assertThat(dataValues.size).isEqualTo(3) } @Test @@ -53,7 +53,7 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg .eq("conflict") .blockingGet() - assertThat(dataValues.size).isEqualTo(7) + assertThat(dataValues.size).isEqualTo(1) } @Test @@ -63,17 +63,17 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg .eq("g9eOBujte1U") .blockingGet() - assertThat(dataValues.size).isEqualTo(7) + assertThat(dataValues.size).isEqualTo(2) } @Test fun filter_by_period() { val dataValues = d2.dataValueModule().dataValueConflicts() .byPeriod() - .eq("2018") + .eq("202101") .blockingGet() - assertThat(dataValues.size).isEqualTo(1) + assertThat(dataValues.size).isEqualTo(2) } @Test @@ -83,7 +83,7 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg .eq("DiszpKrYNg8") .blockingGet() - assertThat(dataValues.size).isEqualTo(6) + assertThat(dataValues.size).isEqualTo(2) } @Test @@ -93,7 +93,7 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg .eq("Gmbgme7z9BF") .blockingGet() - assertThat(dataValues.size).isEqualTo(6) + assertThat(dataValues.size).isEqualTo(2) } @Test @@ -103,14 +103,14 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg .eq("bRowv6yZOF2") .blockingGet() - assertThat(dataValues.size).isEqualTo(6) + assertThat(dataValues.size).isEqualTo(2) } @Test fun filter_by_value() { val dataValues = d2.dataValueModule().dataValueConflicts() .byValue() - .eq("11") + .eq("5") .blockingGet() assertThat(dataValues.size).isEqualTo(1) @@ -120,10 +120,10 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg fun filter_by_created() { val dataValues = d2.dataValueModule().dataValueConflicts() .byCreated() - .eq(DateUtils.DATE_FORMAT.parse("2010-02-11T00:00:00.000+0100")) + .eq(DateUtils.DATE_FORMAT.parse("021-06-02T12:38:53.743")) .blockingGet() - assertThat(dataValues.size).isEqualTo(1) + assertThat(dataValues.size).isEqualTo(3) } @Test @@ -132,27 +132,26 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg .byStatus().eq(ImportStatus.SUCCESS) .blockingGet() - assertThat(dataValues.size).isEqualTo(7) + assertThat(dataValues.size).isEqualTo(2) } @Test fun filter_by_display_description() { val dataValues = d2.dataValueModule().dataValueConflicts() .byDisplayDescription() - .eq("display description") + .eq("display_description_other") .blockingGet() - assertThat(dataValues.size).isEqualTo(7) + assertThat(dataValues.size).isEqualTo(1) } @Test fun filter_by_error_code() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byErrorCode() - .eq("ERROR_CODE") + .byErrorCode().isNull .blockingGet() - assertThat(dataValues.size).isEqualTo(7) + assertThat(dataValues.size).isEqualTo(3) } @Test @@ -161,6 +160,6 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg .byDataSet("lyLU2wR22tC") .blockingGet() - assertThat(dataValues.size).isEqualTo(5) + assertThat(dataValues.size).isEqualTo(1) } } From 6b6da2a0bf834c42c638d98e538b4aadb953a840 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 29 Dec 2022 12:57:32 +0100 Subject: [PATCH 049/201] [ANDROSDK-1612] KtlintFormat --- .../BaseMockIntegrationTestFullDispatcher.kt | 12 ++-- ...llectionRepositoryMockIntegrationShould.kt | 68 +++++++++---------- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt index 44746c2ab5..a361dcbe19 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/integration/mock/BaseMockIntegrationTestFullDispatcher.kt @@ -140,20 +140,24 @@ abstract class BaseMockIntegrationTestFullDispatcher : BaseMockIntegrationTest() val dataValueConflictStore = DataValueConflictStore.create(databaseAdapter) dataValueConflictStore.insert(DataValueConflictSamples.get()) - dataValueConflictStore.insert(DataValueConflictSamples.get().toBuilder() + dataValueConflictStore.insert( + DataValueConflictSamples.get().toBuilder() .value("5") .conflict("conflict") .dataElement("bx6fsa0t90x") .categoryOptionCombo("bRowv6yZOF2") .status(ImportStatus.WARNING) - .build()) - dataValueConflictStore.insert(DataValueConflictSamples.get().toBuilder() + .build() + ) + dataValueConflictStore.insert( + DataValueConflictSamples.get().toBuilder() .attributeOptionCombo("DwrQJzeChWp") .categoryOptionCombo("Gmbgme7z9BF") .period("202201") .orgUnit("YuQRtpLP10I") .displayDescription("display_description_other") - .build()) + .build() + ) } private fun storeSomeKeyValuesInLocalDataStore() { diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt index c98503220e..e8447335a7 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/datavalue/DataValueConflictCollectionRepositoryMockIntegrationShould.kt @@ -41,7 +41,7 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun find_all() { val dataValues = d2.dataValueModule().dataValueConflicts() - .blockingGet() + .blockingGet() assertThat(dataValues.size).isEqualTo(3) } @@ -49,9 +49,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_conflict() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byConflict() - .eq("conflict") - .blockingGet() + .byConflict() + .eq("conflict") + .blockingGet() assertThat(dataValues.size).isEqualTo(1) } @@ -59,9 +59,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_data_element() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byDataElement() - .eq("g9eOBujte1U") - .blockingGet() + .byDataElement() + .eq("g9eOBujte1U") + .blockingGet() assertThat(dataValues.size).isEqualTo(2) } @@ -69,9 +69,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_period() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byPeriod() - .eq("202101") - .blockingGet() + .byPeriod() + .eq("202101") + .blockingGet() assertThat(dataValues.size).isEqualTo(2) } @@ -79,9 +79,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_organisation_unit() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byOrganisationUnitUid() - .eq("DiszpKrYNg8") - .blockingGet() + .byOrganisationUnitUid() + .eq("DiszpKrYNg8") + .blockingGet() assertThat(dataValues.size).isEqualTo(2) } @@ -89,9 +89,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_category_option_combo() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byCategoryOptionCombo() - .eq("Gmbgme7z9BF") - .blockingGet() + .byCategoryOptionCombo() + .eq("Gmbgme7z9BF") + .blockingGet() assertThat(dataValues.size).isEqualTo(2) } @@ -99,9 +99,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_attribute_option_combo() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byAttributeOptionCombo() - .eq("bRowv6yZOF2") - .blockingGet() + .byAttributeOptionCombo() + .eq("bRowv6yZOF2") + .blockingGet() assertThat(dataValues.size).isEqualTo(2) } @@ -109,9 +109,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_value() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byValue() - .eq("5") - .blockingGet() + .byValue() + .eq("5") + .blockingGet() assertThat(dataValues.size).isEqualTo(1) } @@ -119,9 +119,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_created() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byCreated() - .eq(DateUtils.DATE_FORMAT.parse("021-06-02T12:38:53.743")) - .blockingGet() + .byCreated() + .eq(DateUtils.DATE_FORMAT.parse("021-06-02T12:38:53.743")) + .blockingGet() assertThat(dataValues.size).isEqualTo(3) } @@ -129,8 +129,8 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_state() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byStatus().eq(ImportStatus.SUCCESS) - .blockingGet() + .byStatus().eq(ImportStatus.SUCCESS) + .blockingGet() assertThat(dataValues.size).isEqualTo(2) } @@ -138,9 +138,9 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_display_description() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byDisplayDescription() - .eq("display_description_other") - .blockingGet() + .byDisplayDescription() + .eq("display_description_other") + .blockingGet() assertThat(dataValues.size).isEqualTo(1) } @@ -148,8 +148,8 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_error_code() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byErrorCode().isNull - .blockingGet() + .byErrorCode().isNull + .blockingGet() assertThat(dataValues.size).isEqualTo(3) } @@ -157,8 +157,8 @@ class DataValueConflictCollectionRepositoryMockIntegrationShould : BaseMockInteg @Test fun filter_by_dataset() { val dataValues = d2.dataValueModule().dataValueConflicts() - .byDataSet("lyLU2wR22tC") - .blockingGet() + .byDataSet("lyLU2wR22tC") + .blockingGet() assertThat(dataValues.size).isEqualTo(1) } From f7a1dc4e527a63436e3de5a75f81445d89e5ef31 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Thu, 29 Dec 2022 13:07:43 +0100 Subject: [PATCH 050/201] [ANDROSDK-1612] Remove unused imports --- .../core/datavalue/DataValueConflictCollectionRepository.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java index c579b7c24a..3627b5a91f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java @@ -39,12 +39,8 @@ 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.common.IdentifiableColumns; import org.hisp.dhis.android.core.imports.ImportStatus; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitProgramLinkTableInfo; -import org.hisp.dhis.android.core.user.UserOrganisationUnitLinkTableInfo; -import java.util.Collections; import java.util.Map; import javax.inject.Inject; From 123e71fa6ef58590d48b9def1a2c277237dc1403 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 9 Jan 2023 09:12:36 +0100 Subject: [PATCH 051/201] [ANDROSDK-1612] Split DataValue and DataValueConflicts keys on the DataValueByDataSetQueryHelper --- .../core/datavalue/DataValueByDataSetQueryHelper.kt | 9 ++++++++- .../core/datavalue/DataValueCollectionRepository.java | 2 +- .../datavalue/DataValueConflictCollectionRepository.java | 2 +- .../android/core/datavalue/internal/DataValueStore.java | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueByDataSetQueryHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueByDataSetQueryHelper.kt index 32db8917dc..2d2b22005e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueByDataSetQueryHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueByDataSetQueryHelper.kt @@ -58,12 +58,19 @@ internal object DataValueByDataSetQueryHelper { private const val AOC_CATEGORYCOMBO = "$AOC_ALIAS.${CategoryOptionComboTableInfo.Columns.CATEGORY_COMBO}" @JvmStatic - val key = buildKey( + val dataValueKey = buildKey( DataValueTableInfo.Columns.DATA_ELEMENT, DataValueTableInfo.Columns.CATEGORY_OPTION_COMBO, DataValueTableInfo.Columns.ATTRIBUTE_OPTION_COMBO ) + @JvmStatic + val dataValueConflictKey = buildKey( + DataValueConflictTableInfo.Columns.DATA_ELEMENT, + DataValueConflictTableInfo.Columns.CATEGORY_OPTION_COMBO, + DataValueConflictTableInfo.Columns.ATTRIBUTE_OPTION_COMBO + ) + @JvmStatic val operator = FilterItemOperator.IN 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 68f7dfaa63..61ae86b750 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 @@ -122,7 +122,7 @@ public StringFilterConnector byValue() { } public DataValueCollectionRepository byDataSetUid(String dataSetUid) { - return cf.subQuery(DataValueByDataSetQueryHelper.getKey()) + return cf.subQuery(DataValueByDataSetQueryHelper.getDataValueConflictKey()) .rawSubQuery(DataValueByDataSetQueryHelper.getOperator(), DataValueByDataSetQueryHelper.whereClause(dataSetUid)); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java index 3627b5a91f..41d69aaad9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/DataValueConflictCollectionRepository.java @@ -64,7 +64,7 @@ public final class DataValueConflictCollectionRepository } public DataValueConflictCollectionRepository byDataSet(String dataSetUid) { - return cf.subQuery(DataValueByDataSetQueryHelper.getKey()) + return cf.subQuery(DataValueByDataSetQueryHelper.getDataValueConflictKey()) .rawSubQuery(DataValueByDataSetQueryHelper.getOperator(), DataValueByDataSetQueryHelper.whereClause(dataSetUid)); } 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 1c67f600f5..17d563148c 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 @@ -140,7 +140,7 @@ private WhereClauseBuilder uniqueWhereClauseBuilder(DataValue dataValue) { public Boolean existsInDataSet(DataValue dataValue, String dataSetUid) { WhereClauseBuilder whereClauseBuilder = uniqueWhereClauseBuilder(dataValue) .appendInSubQuery( - DataValueByDataSetQueryHelper.getKey(), + DataValueByDataSetQueryHelper.getDataValueKey(), DataValueByDataSetQueryHelper.whereClause(dataSetUid) ); return selectWhere(whereClauseBuilder.build()).size() > 0; From 924971d6f91842925cba18ac4b88e30973474765 Mon Sep 17 00:00:00 2001 From: Marcos Campos Date: Mon, 9 Jan 2023 10:12:00 +0100 Subject: [PATCH 052/201] [ANDROSDK-1612] Fix wrong subQuery key --- .../android/core/datavalue/DataValueCollectionRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 61ae86b750..a8d05556ed 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 @@ -122,7 +122,7 @@ public StringFilterConnector byValue() { } public DataValueCollectionRepository byDataSetUid(String dataSetUid) { - return cf.subQuery(DataValueByDataSetQueryHelper.getDataValueConflictKey()) + return cf.subQuery(DataValueByDataSetQueryHelper.getDataValueKey()) .rawSubQuery(DataValueByDataSetQueryHelper.getOperator(), DataValueByDataSetQueryHelper.whereClause(dataSetUid)); } From 07c242b85a33aa4c0c2692ad25590fc7896e25b2 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Tue, 10 Jan 2023 20:35:06 +1100 Subject: [PATCH 053/201] [ANDROSDK-1593] Evaluate syncStatus on-the-fly for DatabaseAccounts --- .../org/hisp/dhis/android/core/D2Manager.kt | 8 +- .../internal/DatabaseAdapterFactory.java | 14 ++- ...atabaseAccount.java => DatabaseAccount.kt} | 75 ++++++++-------- .../DatabaseConfigurationTransformer.kt | 2 +- .../configuration/internal/DateProvider.kt | 2 +- .../MultiUserDatabaseManagerForD2Manager.kt | 2 +- .../dataset/DataInputPeriodTableInfo.java | 3 +- .../stock/StockUseCaseTransactionTableInfo.kt | 3 +- .../user/internal/AccountManagerHelper.kt | 86 +++++++++++++++++++ .../core/user/internal/AccountManagerImpl.kt | 13 ++- 10 files changed, 155 insertions(+), 53 deletions(-) rename core/src/main/java/org/hisp/dhis/android/core/configuration/internal/{DatabaseAccount.java => DatabaseAccount.kt} (58%) create mode 100644 core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerHelper.kt diff --git a/core/src/main/java/org/hisp/dhis/android/core/D2Manager.kt b/core/src/main/java/org/hisp/dhis/android/core/D2Manager.kt index d602bc89bc..5b9e5bfeaf 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/D2Manager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/D2Manager.kt @@ -111,10 +111,10 @@ object D2Manager { if (wantToImportDBForExternalTesting()) { multiUserDatabaseManager.loadDbForTesting( - testingServerUrl, - testingDatabaseName, + testingServerUrl!!, + testingDatabaseName!!, false, - testingUsername + testingUsername!! ) } else { multiUserDatabaseManager.loadIfLogged(credentials) @@ -181,7 +181,7 @@ object D2Manager { } private fun wantToImportDBForExternalTesting(): Boolean { - return testingDatabaseName != null && testingUsername != null + return testingServerUrl != null && testingDatabaseName != null && testingUsername != null } @JvmStatic diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseAdapterFactory.java b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseAdapterFactory.java index ba4f881d43..a94d17c2c9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseAdapterFactory.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseAdapterFactory.java @@ -73,7 +73,7 @@ public DatabaseAdapter newParentDatabaseAdapter() { public void createOrOpenDatabase(DatabaseAdapter adapter, String databaseName, boolean encrypt, Integer version) { try { ParentDatabaseAdapter parentDatabaseAdapter = (ParentDatabaseAdapter) adapter; - DatabaseAdapter internalAdapter = newInternalAdapter(databaseName, context, encrypt, version); + DatabaseAdapter internalAdapter = newInternalAdapter(databaseName, encrypt, version); adaptersToPreventNotClosedError.add(internalAdapter); parentDatabaseAdapter.setAdapter(internalAdapter); } catch (ClassCastException cce) { @@ -100,8 +100,16 @@ public void deleteDatabase(DatabaseAccount userConfiguration) { } } - private DatabaseAdapter newInternalAdapter(String databaseName, Context context, - boolean encrypt, int version) { + public DatabaseAdapter getDatabaseAdapter(DatabaseAccount databaseAccount) { + DatabaseAdapter adapter = newInternalAdapter(databaseAccount.databaseName(), databaseAccount.encrypted(), + BaseDatabaseOpenHelper.VERSION); + adaptersToPreventNotClosedError.add(adapter); + return adapter; + } + + private DatabaseAdapter newInternalAdapter(String databaseName, + boolean encrypt, + int version) { if (encrypt) { EncryptedDatabaseOpenHelper openHelper = instantiateOpenHelper(databaseName, encryptedOpenHelpers, v -> new EncryptedDatabaseOpenHelper(context, databaseName, version)); diff --git a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.java b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.kt similarity index 58% rename from core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.java rename to core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.kt index 28f107e214..69425d017e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.java +++ b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.kt @@ -25,60 +25,59 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.configuration.internal -package org.hisp.dhis.android.core.configuration.internal; - -import androidx.annotation.NonNull; - -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 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.State @AutoValue -@JsonDeserialize(builder = AutoValue_DatabaseAccount.Builder.class) -public abstract class DatabaseAccount { - - @JsonProperty() - @NonNull - public abstract String username(); +@JsonDeserialize(builder = AutoValue_DatabaseAccount.Builder::class) +abstract class DatabaseAccount { + @JsonProperty + abstract fun username(): String - @JsonProperty() - @NonNull - public abstract String serverUrl(); + @JsonProperty + abstract fun serverUrl(): String - @JsonProperty() - @NonNull - public abstract String databaseName(); + @JsonProperty + abstract fun databaseName(): String - @JsonProperty() - @NonNull - public abstract String databaseCreationDate(); + @JsonProperty + abstract fun databaseCreationDate(): String - @JsonProperty() - @NonNull - public abstract boolean encrypted(); + @JsonProperty + abstract fun encrypted(): Boolean - public abstract Builder toBuilder(); + @JsonProperty + abstract fun syncState(): State? - public static Builder builder() { - return new AutoValue_DatabaseAccount.Builder(); - } + abstract fun toBuilder(): Builder @AutoValue.Builder @JsonPOJOBuilder(withPrefix = "") - public abstract static class Builder { + abstract class Builder { + abstract fun username(username: String): Builder + + abstract fun serverUrl(serverUrl: String): Builder - public abstract Builder username(String username); + abstract fun databaseName(databaseName: String): Builder - public abstract Builder serverUrl(String serverUrl); + abstract fun databaseCreationDate(databaseCreationDate: String): Builder - public abstract Builder databaseName(String databaseName); + abstract fun encrypted(encrypted: Boolean): Builder - public abstract Builder encrypted(boolean encrypted); + abstract fun syncState(syncState: State?): Builder - public abstract Builder databaseCreationDate(String databaseCreationDate); + abstract fun build(): DatabaseAccount + } - public abstract DatabaseAccount build(); + companion object { + @JvmStatic + fun builder(): Builder { + return AutoValue_DatabaseAccount.Builder() + } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseConfigurationTransformer.kt b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseConfigurationTransformer.kt index cdff5a8a12..ec5c71ee9a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseConfigurationTransformer.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseConfigurationTransformer.kt @@ -33,7 +33,7 @@ import org.hisp.dhis.android.core.common.BaseIdentifiableObject @Reusable internal object DatabaseConfigurationTransformer { - fun transform(serverUrl: String?, databaseName: String?, username: String?): DatabasesConfiguration { + fun transform(serverUrl: String, databaseName: String, username: String): DatabasesConfiguration { return DatabasesConfiguration.builder() .accounts( listOf( diff --git a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DateProvider.kt b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DateProvider.kt index eaeb2bb305..32efcdae5b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DateProvider.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DateProvider.kt @@ -28,5 +28,5 @@ package org.hisp.dhis.android.core.configuration.internal internal interface DateProvider { - val dateStr: String? + val dateStr: String } diff --git a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/MultiUserDatabaseManagerForD2Manager.kt b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/MultiUserDatabaseManagerForD2Manager.kt index c2727c8d44..c5f4012c32 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/MultiUserDatabaseManagerForD2Manager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/MultiUserDatabaseManagerForD2Manager.kt @@ -55,7 +55,7 @@ internal class MultiUserDatabaseManagerForD2Manager @Inject constructor( } } - fun loadDbForTesting(serverUrl: String?, name: String?, encrypt: Boolean, username: String?) { + fun loadDbForTesting(serverUrl: String, name: String, encrypt: Boolean, username: String) { val config = DatabaseAccount.builder() .databaseName(name) .encrypted(encrypt) diff --git a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataInputPeriodTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataInputPeriodTableInfo.java index 2fdb3bb1e9..8d4d077c51 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/dataset/DataInputPeriodTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/dataset/DataInputPeriodTableInfo.java @@ -31,7 +31,6 @@ 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.DeletableDataColumns; public class DataInputPeriodTableInfo { @@ -50,7 +49,7 @@ public CoreColumns columns() { } }; - public static class Columns extends DeletableDataColumns { + public static class Columns extends CoreColumns { public static final String DATA_SET = "dataSet"; public static final String PERIOD = "period"; diff --git a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransactionTableInfo.kt b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransactionTableInfo.kt index 7a25af92a2..83c4046020 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransactionTableInfo.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/usecase/stock/StockUseCaseTransactionTableInfo.kt @@ -30,7 +30,6 @@ package org.hisp.dhis.android.core.usecase.stock 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.DeletableDataColumns object StockUseCaseTransactionTableInfo { @@ -45,7 +44,7 @@ object StockUseCaseTransactionTableInfo { } } - class Columns : DeletableDataColumns() { + class Columns : CoreColumns() { override fun all(): Array { return CollectionsHelper.appendInNewArray( super.all(), diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerHelper.kt new file mode 100644 index 0000000000..c3a6fd9778 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerHelper.kt @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.hisp.dhis.android.core.user.internal + +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.common.DataColumns +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.dataset.DataSetCompleteRegistrationTableInfo +import org.hisp.dhis.android.core.datavalue.DataValueTableInfo +import org.hisp.dhis.android.core.enrollment.EnrollmentTableInfo +import org.hisp.dhis.android.core.event.EventTableInfo +import org.hisp.dhis.android.core.fileresource.FileResourceTableInfo +import org.hisp.dhis.android.core.note.NoteTableInfo +import org.hisp.dhis.android.core.relationship.RelationshipTableInfo +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceTableInfo +import org.hisp.dhis.android.core.trackedentity.ownership.ProgramOwnerTableInfo + +internal object AccountManagerHelper { + + private val tablesWithSyncState = listOf( + DataValueTableInfo.TABLE_INFO, + DataSetCompleteRegistrationTableInfo.TABLE_INFO, + TrackedEntityInstanceTableInfo.TABLE_INFO, + EnrollmentTableInfo.TABLE_INFO, + EventTableInfo.TABLE_INFO, + FileResourceTableInfo.TABLE_INFO, + NoteTableInfo.TABLE_INFO, + ProgramOwnerTableInfo.TABLE_INFO, + RelationshipTableInfo.TABLE_INFO, + ) + + private val syncStateQuery = + tablesWithSyncState.joinToString(" UNION ") { "SELECT ${DataColumns.SYNC_STATE} FROM ${it.name()}" } + + fun getSyncState(adapter: DatabaseAdapter): State { + val states = mutableSetOf() + + adapter.rawQuery(syncStateQuery).use { cursor -> + if (cursor.count > 0) { + cursor.moveToFirst() + do { + State.values() + .find { it.name == cursor.getString(0) } + ?.let { states.add(it) } + } while (cursor.moveToNext()) + } + } + + return when { + 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/user/internal/AccountManagerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt index 6906d7b8ca..648baf4d1f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt @@ -34,6 +34,7 @@ import io.reactivex.Observable import io.reactivex.subjects.PublishSubject import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.internal.DatabaseAdapterFactory +import org.hisp.dhis.android.core.arch.helpers.DateUtils import org.hisp.dhis.android.core.arch.helpers.FileResourceDirectoryHelper import org.hisp.dhis.android.core.arch.storage.internal.Credentials import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore @@ -47,6 +48,7 @@ import org.hisp.dhis.android.core.maintenance.D2ErrorCode import org.hisp.dhis.android.core.maintenance.D2ErrorComponent import org.hisp.dhis.android.core.user.AccountDeletionReason import org.hisp.dhis.android.core.user.AccountManager +import java.util.* @Reusable internal class AccountManagerImpl @Inject constructor( @@ -60,7 +62,7 @@ internal class AccountManagerImpl @Inject constructor( private val accountDeletionSubject = PublishSubject.create() override fun getAccounts(): List { - return databasesConfigurationStore.get()?.accounts() ?: emptyList() + return databasesConfigurationStore.get()?.accounts()?.map { updateSyncState(it) } ?: emptyList() } override fun setMaxAccounts(maxAccounts: Int) { @@ -128,6 +130,15 @@ internal class AccountManagerImpl @Inject constructor( databaseAdapterFactory.deleteDatabase(loggedAccount) } + private fun updateSyncState(account: DatabaseAccount): DatabaseAccount { + val databaseAdapter = databaseAdapterFactory.getDatabaseAdapter(account) + val syncState = AccountManagerHelper.getSyncState(databaseAdapter) + + return account.toBuilder() + .syncState(syncState) + .build() + } + override fun accountDeletionObservable(): Observable { return accountDeletionSubject } From df201f7a22822f09902cc8f21fb4ecb7327a23b9 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 11 Jan 2023 16:04:33 +1100 Subject: [PATCH 054/201] [ANDROSDK-1593] Add integration test --- .../AccountManagerMockIntegrationShould.java | 121 ------------- .../AccountManagerMockIntegrationShould.kt | 165 ++++++++++++++++++ ...atabaseAccount.kt => DatabaseAccount.java} | 80 +++++---- .../user/internal/AccountManagerHelper.kt | 9 +- .../core/user/internal/AccountManagerImpl.kt | 4 +- 5 files changed, 218 insertions(+), 161 deletions(-) delete mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.java create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.kt rename core/src/main/java/org/hisp/dhis/android/core/configuration/internal/{DatabaseAccount.kt => DatabaseAccount.java} (55%) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.java deleted file mode 100644 index 907d309374..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.testapp.user; - -import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; - -import org.hisp.dhis.android.core.configuration.internal.DatabaseAccount; -import org.hisp.dhis.android.core.configuration.internal.MultiUserDatabaseManager; -import org.hisp.dhis.android.core.maintenance.D2Error; -import org.hisp.dhis.android.core.maintenance.D2ErrorCode; -import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyEnqueable; -import org.hisp.dhis.android.core.utils.runner.D2JunitRunner; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.List; - -@RunWith(D2JunitRunner.class) -public class AccountManagerMockIntegrationShould extends BaseMockIntegrationTestEmptyEnqueable { - - @BeforeClass - public static void setUpTestClass() { - BaseMockIntegrationTestEmptyEnqueable.setUpClass(); - } - - @Test - public void find_accounts_after_login() { - if (d2.userModule().blockingIsLogged()) { - d2.userModule().blockingLogOut(); - } - - dhis2MockServer.enqueueLoginResponses(); - d2.userModule().blockingLogIn("u1", "pass1", dhis2MockServer.getBaseEndpoint()); - - List accountList = d2.userModule().accountManager().getAccounts(); - - assertThat(accountList.size()).isEqualTo(1); - assertThat(accountList.get(0).username()).isEqualTo("u1"); - } - - @Test - public void can_change_max_accounts() { - d2.userModule().accountManager().setMaxAccounts(5); - assertThat(d2.userModule().accountManager().getMaxAccounts()).isEqualTo(5); - - int defaultMaxAccounts = MultiUserDatabaseManager.DefaultMaxAccounts; - d2.userModule().accountManager().setMaxAccounts(defaultMaxAccounts); - assertThat(d2.userModule().accountManager().getMaxAccounts()).isEqualTo(defaultMaxAccounts); - } - - @Test - public void can_delete_current_logged_account() { - if (d2.userModule().blockingIsLogged()) { - d2.userModule().blockingLogOut(); - } - - dhis2MockServer.enqueueLoginResponses(); - d2.userModule().blockingLogIn("u1", "pass1", dhis2MockServer.getBaseEndpoint()); - - try { - d2.userModule().accountManager().deleteCurrentAccount(); - - List accountList = d2.userModule().accountManager().getAccounts(); - assertThat(accountList.size()).isEqualTo(0); - } catch (D2Error e) { - fail("Should not throw a D2Error"); - } - } - - @Test - public void cannot_delete_not_logged_account() { - if (d2.userModule().blockingIsLogged()) { - d2.userModule().blockingLogOut(); - } - - dhis2MockServer.enqueueLoginResponses(); - d2.userModule().blockingLogIn("u1", "pass1", dhis2MockServer.getBaseEndpoint()); - - d2.userModule().blockingLogOut(); - - try { - d2.userModule().accountManager().deleteCurrentAccount(); - fail("Should throw a D2Error"); - } catch (D2Error e) { - assertThat(e.errorCode()).isEqualTo(D2ErrorCode.NO_AUTHENTICATED_USER); - - List accountList = d2.userModule().accountManager().getAccounts(); - assertThat(accountList.size()).isEqualTo(1); - } catch (Exception e) { - fail("Should throw a D2Error"); - } - } -} \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.kt new file mode 100644 index 0000000000..030448c7a0 --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.kt @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.testapp.user + +import com.google.common.truth.Truth.assertThat +import java.util.* +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.configuration.internal.MultiUserDatabaseManager +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import org.hisp.dhis.android.core.mockwebserver.Dhis2MockServer +import org.hisp.dhis.android.core.period.PeriodType +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyEnqueable +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.Assert +import org.junit.BeforeClass +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class AccountManagerMockIntegrationShould : BaseMockIntegrationTestEmptyEnqueable() { + @Test + fun find_accounts_after_login() { + if (d2.userModule().blockingIsLogged()) { + d2.userModule().blockingLogOut() + } + dhis2MockServer.enqueueLoginResponses() + d2.userModule().blockingLogIn("u1", "pass1", dhis2MockServer.baseEndpoint) + + val accountList = d2.userModule().accountManager().getAccounts() + + assertThat(accountList.size).isEqualTo(1) + assertThat(accountList[0].username()).isEqualTo("u1") + } + + @Test + fun can_change_max_accounts() { + d2.userModule().accountManager().setMaxAccounts(5) + assertThat(d2.userModule().accountManager().getMaxAccounts()).isEqualTo(5) + + val defaultMaxAccounts = MultiUserDatabaseManager.DefaultMaxAccounts + d2.userModule().accountManager().setMaxAccounts(defaultMaxAccounts) + assertThat(d2.userModule().accountManager().getMaxAccounts()).isEqualTo(defaultMaxAccounts) + } + + @Test + fun can_delete_current_logged_account() { + if (d2.userModule().blockingIsLogged()) { + d2.userModule().blockingLogOut() + } + dhis2MockServer.enqueueLoginResponses() + d2.userModule().blockingLogIn("u1", "pass1", dhis2MockServer.baseEndpoint) + try { + d2.userModule().accountManager().deleteCurrentAccount() + val accountList = d2.userModule().accountManager().getAccounts() + assertThat(accountList.size).isEqualTo(0) + } catch (e: D2Error) { + Assert.fail("Should not throw a D2Error") + } + } + + @Test + fun cannot_delete_not_logged_account() { + if (d2.userModule().blockingIsLogged()) { + d2.userModule().blockingLogOut() + } + dhis2MockServer.enqueueLoginResponses() + d2.userModule().blockingLogIn("u1", "pass1", dhis2MockServer.baseEndpoint) + d2.userModule().blockingLogOut() + try { + d2.userModule().accountManager().deleteCurrentAccount() + Assert.fail("Should throw a D2Error") + } catch (e: D2Error) { + assertThat(e.errorCode()).isEqualTo(D2ErrorCode.NO_AUTHENTICATED_USER) + val accountList = d2.userModule().accountManager().getAccounts() + assertThat(accountList.size).isEqualTo(1) + } catch (e: Exception) { + Assert.fail("Should throw a D2Error") + } + } + + @Test + fun evaluate_sync_status() { + if (d2.userModule().blockingIsLogged()) { + d2.userModule().blockingLogOut() + } + d2.userModule().accountManager().setMaxAccounts(5) + + dhis2MockServer.enqueueLoginResponses() + d2.userModule().blockingLogIn("u1", "pass1", dhis2MockServer.baseEndpoint) + + val accounts = d2.userModule().accountManager().getAccounts() + assertThat(accounts.size).isEqualTo(1) + assertThat(accounts.first().syncState()).isEqualTo(State.SYNCED) + + d2.userModule().blockingLogOut() + + val server2 = Dhis2MockServer(0) + server2.enqueueLoginResponses() + d2.userModule().blockingLogIn("u2", "pass2", server2.baseEndpoint) + server2.enqueueMetadataResponses() + d2.metadataModule().blockingDownload() + addDataValue() + + val accounts2 = d2.userModule().accountManager().getAccounts() + assertThat(accounts2.size).isEqualTo(2) + + println(accounts2) + + accounts2.forEach { account -> + when (account.username()) { + "u1" -> assertThat(account.syncState()).isEqualTo(State.SYNCED) + "u2" -> assertThat(account.syncState()).isEqualTo(State.TO_UPDATE) + else -> Assert.fail("Should not get here") + } + } + + d2.userModule().accountManager().deleteCurrentAccount() + d2.userModule().accountManager().setMaxAccounts(MultiUserDatabaseManager.DefaultMaxAccounts) + } + + private fun addDataValue() { + val period = d2.periodModule().periodHelper().blockingGetPeriodForPeriodTypeAndDate(PeriodType.Yearly, Date()) + val orgunit = d2.organisationUnitModule().organisationUnits().one().blockingGet()!! + val coc = d2.categoryModule().categoryOptionCombos().one().blockingGet()!! + val dataElement = d2.dataElementModule().dataElements().one().blockingGet()!! + + d2.dataValueModule().dataValues() + .value(period.periodId()!!, orgunit.uid(), dataElement.uid(), coc.uid(), coc.uid()) + .blockingSet("45") + } + + companion object { + @JvmStatic + @BeforeClass + fun setUpTestClass() { + setUpClass() + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.kt b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.java similarity index 55% rename from core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.kt rename to core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.java index 69425d017e..4fcc0d3b6e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabaseAccount.java @@ -25,59 +25,67 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.configuration.internal +package org.hisp.dhis.android.core.configuration.internal; -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.State +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.google.auto.value.AutoValue; + +import org.hisp.dhis.android.core.common.State; @AutoValue -@JsonDeserialize(builder = AutoValue_DatabaseAccount.Builder::class) -abstract class DatabaseAccount { - @JsonProperty - abstract fun username(): String +@JsonDeserialize(builder = AutoValue_DatabaseAccount.Builder.class) +public abstract class DatabaseAccount { + + @JsonProperty() + @NonNull + public abstract String username(); - @JsonProperty - abstract fun serverUrl(): String + @JsonProperty() + @NonNull + public abstract String serverUrl(); - @JsonProperty - abstract fun databaseName(): String + @JsonProperty() + @NonNull + public abstract String databaseName(); - @JsonProperty - abstract fun databaseCreationDate(): String + @JsonProperty() + @NonNull + public abstract String databaseCreationDate(); - @JsonProperty - abstract fun encrypted(): Boolean + @JsonProperty() + @NonNull + public abstract boolean encrypted(); - @JsonProperty - abstract fun syncState(): State? + @Nullable + public abstract State syncState(); - abstract fun toBuilder(): Builder + public abstract Builder toBuilder(); + + public static Builder builder() { + return new AutoValue_DatabaseAccount.Builder(); + } @AutoValue.Builder @JsonPOJOBuilder(withPrefix = "") - abstract class Builder { - abstract fun username(username: String): Builder + public abstract static class Builder { - abstract fun serverUrl(serverUrl: String): Builder + public abstract Builder username(String username); - abstract fun databaseName(databaseName: String): Builder + public abstract Builder serverUrl(String serverUrl); - abstract fun databaseCreationDate(databaseCreationDate: String): Builder + public abstract Builder databaseName(String databaseName); - abstract fun encrypted(encrypted: Boolean): Builder + public abstract Builder encrypted(boolean encrypted); - abstract fun syncState(syncState: State?): Builder + public abstract Builder databaseCreationDate(String databaseCreationDate); - abstract fun build(): DatabaseAccount - } + public abstract Builder syncState(State syncState); - companion object { - @JvmStatic - fun builder(): Builder { - return AutoValue_DatabaseAccount.Builder() - } + public abstract DatabaseAccount build(); } -} +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerHelper.kt index c3a6fd9778..8b80c563bd 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerHelper.kt @@ -58,6 +58,7 @@ internal object AccountManagerHelper { private val syncStateQuery = tablesWithSyncState.joinToString(" UNION ") { "SELECT ${DataColumns.SYNC_STATE} FROM ${it.name()}" } + @Suppress("NestedBlockDepth") fun getSyncState(adapter: DatabaseAdapter): State { val states = mutableSetOf() @@ -72,12 +73,16 @@ internal object AccountManagerHelper { } } + return reduceSyncState(states) + } + + private fun reduceSyncState(states: Collection): State { return when { 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.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/user/internal/AccountManagerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt index 648baf4d1f..bf7483394a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt @@ -32,9 +32,9 @@ import android.content.Context import dagger.Reusable import io.reactivex.Observable import io.reactivex.subjects.PublishSubject +import java.util.* import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.internal.DatabaseAdapterFactory -import org.hisp.dhis.android.core.arch.helpers.DateUtils import org.hisp.dhis.android.core.arch.helpers.FileResourceDirectoryHelper import org.hisp.dhis.android.core.arch.storage.internal.Credentials import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore @@ -48,9 +48,9 @@ import org.hisp.dhis.android.core.maintenance.D2ErrorCode import org.hisp.dhis.android.core.maintenance.D2ErrorComponent import org.hisp.dhis.android.core.user.AccountDeletionReason import org.hisp.dhis.android.core.user.AccountManager -import java.util.* @Reusable +@Suppress("TooManyFunctions") internal class AccountManagerImpl @Inject constructor( private val databasesConfigurationStore: ObjectKeyValueStore, private val multiUserDatabaseManager: MultiUserDatabaseManager, From d4e837e331a7674ebb7e556a47892eac0612cd0a Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 11 Jan 2023 16:10:12 +1100 Subject: [PATCH 055/201] [ANDROSDK-1593] Remove unused imports --- .../hisp/dhis/android/core/user/internal/AccountManagerImpl.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt index bf7483394a..ffb8aef1b6 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt @@ -32,7 +32,6 @@ import android.content.Context import dagger.Reusable import io.reactivex.Observable import io.reactivex.subjects.PublishSubject -import java.util.* import javax.inject.Inject import org.hisp.dhis.android.core.arch.db.access.internal.DatabaseAdapterFactory import org.hisp.dhis.android.core.arch.helpers.FileResourceDirectoryHelper From 40e3b67709c45560ad6ee3585c21f9931c878dc1 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 11 Jan 2023 18:09:44 +1100 Subject: [PATCH 056/201] [ANDROSDK-1631] Make maxAccounts property nullable, meaning limitless --- .../testapp/user/AccountManagerMockIntegrationShould.kt | 8 ++++++++ .../configuration/internal/DatabasesConfiguration.java | 3 ++- .../configuration/internal/MultiUserDatabaseManager.kt | 8 ++++---- .../org/hisp/dhis/android/core/user/AccountManager.kt | 4 ++-- .../dhis/android/core/user/internal/AccountManagerImpl.kt | 6 +++--- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.kt index 030448c7a0..4616c664c6 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/user/AccountManagerMockIntegrationShould.kt @@ -68,6 +68,14 @@ class AccountManagerMockIntegrationShould : BaseMockIntegrationTestEmptyEnqueabl assertThat(d2.userModule().accountManager().getMaxAccounts()).isEqualTo(defaultMaxAccounts) } + @Test + fun can_set_null_max_accounts() { + d2.userModule().accountManager().setMaxAccounts(null) + assertThat(d2.userModule().accountManager().getMaxAccounts()).isNull() + + d2.userModule().accountManager().setMaxAccounts(MultiUserDatabaseManager.DefaultMaxAccounts) + } + @Test fun can_delete_current_logged_account() { if (d2.userModule().blockingIsLogged()) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabasesConfiguration.java b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabasesConfiguration.java index f31c3e5975..b2e32d3cb0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabasesConfiguration.java +++ b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/DatabasesConfiguration.java @@ -29,6 +29,7 @@ package org.hisp.dhis.android.core.configuration.internal; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -48,7 +49,7 @@ public abstract class DatabasesConfiguration { public abstract long versionCode(); @JsonProperty() - @NonNull + @Nullable public abstract Integer maxAccounts(); @JsonProperty() diff --git a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/MultiUserDatabaseManager.kt b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/MultiUserDatabaseManager.kt index a5c827b5df..60042b1c2e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/MultiUserDatabaseManager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/configuration/internal/MultiUserDatabaseManager.kt @@ -80,9 +80,9 @@ internal class MultiUserDatabaseManager @Inject internal constructor( fun createNew(serverUrl: String, username: String, encrypt: Boolean) { val configuration = databaseConfigurationSecureStore.get() - configuration?.let { + configuration?.maxAccounts()?.let { maxAccounts -> val exceedingAccounts = DatabaseConfigurationHelper - .getOldestAccounts(configuration.accounts(), configuration.maxAccounts() - 1) + .getOldestAccounts(configuration.accounts(), maxAccounts - 1) val updatedConfiguration = DatabaseConfigurationHelper.removeAccount(configuration, exceedingAccounts) @@ -116,8 +116,8 @@ internal class MultiUserDatabaseManager @Inject internal constructor( ) } - fun setMaxAccounts(maxAccounts: Int) { - if (maxAccounts <= 0) { + fun setMaxAccounts(maxAccounts: Int?) { + if (maxAccounts != null && maxAccounts <= 0) { throw IllegalArgumentException("MaxAccounts must be greater than 0") } else { val configuration = databaseConfigurationSecureStore.get() diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/AccountManager.kt b/core/src/main/java/org/hisp/dhis/android/core/user/AccountManager.kt index 815506de4e..0b87f7d92d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/AccountManager.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/AccountManager.kt @@ -36,9 +36,9 @@ import org.hisp.dhis.android.core.maintenance.D2Error interface AccountManager { fun getAccounts(): List - fun setMaxAccounts(maxAccounts: Int) + fun setMaxAccounts(maxAccounts: Int?) - fun getMaxAccounts(): Int + fun getMaxAccounts(): Int? @Throws(D2Error::class) fun deleteCurrentAccount() diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt index ffb8aef1b6..c2ec875667 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/AccountManagerImpl.kt @@ -64,12 +64,12 @@ internal class AccountManagerImpl @Inject constructor( return databasesConfigurationStore.get()?.accounts()?.map { updateSyncState(it) } ?: emptyList() } - override fun setMaxAccounts(maxAccounts: Int) { + override fun setMaxAccounts(maxAccounts: Int?) { multiUserDatabaseManager.setMaxAccounts(maxAccounts) } - override fun getMaxAccounts(): Int { - return databasesConfigurationStore.get()?.maxAccounts() ?: MultiUserDatabaseManager.DefaultMaxAccounts + override fun getMaxAccounts(): Int? { + return databasesConfigurationStore.get()?.maxAccounts() } @Throws(D2Error::class) From b0796d9067aa7ef02fced5bfa2f0dd2c8be3d01d Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Wed, 11 Jan 2023 18:53:08 +1100 Subject: [PATCH 057/201] [ANDROSDK-1581] Remove version from snapshot filename --- .../main/assets/snapshots/{136.sql => snapshot.sql} | 0 .../db/access/internal/BaseDatabaseOpenHelper.java | 7 +------ .../db/access/internal/DatabaseMigrationExecutor.kt | 11 +++++------ .../db/access/internal/DatabaseMigrationParser.kt | 12 ++++++------ 4 files changed, 12 insertions(+), 18 deletions(-) rename core/src/main/assets/snapshots/{136.sql => snapshot.sql} (100%) diff --git a/core/src/main/assets/snapshots/136.sql b/core/src/main/assets/snapshots/snapshot.sql similarity index 100% rename from core/src/main/assets/snapshots/136.sql rename to core/src/main/assets/snapshots/snapshot.sql 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 250756be02..850fc53bab 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 @@ -30,7 +30,6 @@ import android.content.Context; import android.content.res.AssetManager; -import android.os.Build; import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; @@ -47,11 +46,7 @@ class BaseDatabaseOpenHelper { } void onOpen(DatabaseAdapter databaseAdapter) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // enable foreign key support in database only for lollipop and newer versions - databaseAdapter.setForeignKeyConstraintsEnabled(true); - } - + databaseAdapter.setForeignKeyConstraintsEnabled(true); databaseAdapter.enableWriteAheadLogging(); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseMigrationExecutor.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseMigrationExecutor.kt index 1a9d86bb27..7f7d21b5da 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseMigrationExecutor.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseMigrationExecutor.kt @@ -37,7 +37,6 @@ internal class DatabaseMigrationExecutor(private val databaseAdapter: DatabaseAd private val parser = DatabaseMigrationParser(assetManager, databaseAdapter) companion object { - private const val SNAPSHOT_VERSION = BaseDatabaseOpenHelper.VERSION private val MIGRATIONS_ACCEPTING_ERRORS = setOf(98) @VisibleForTesting @@ -47,7 +46,7 @@ internal class DatabaseMigrationExecutor(private val databaseAdapter: DatabaseAd fun upgradeFromTo(oldVersion: Int, newVersion: Int) { val transaction = databaseAdapter.beginNewTransaction() try { - val initialMigrationVersion = if (USE_SNAPSHOT) performSnapshotIfRequired(oldVersion, newVersion) else 0 + val initialMigrationVersion = if (USE_SNAPSHOT) performSnapshotIfRequired(oldVersion) else 0 val migrations = parser.parseMigrations(initialMigrationVersion, newVersion) migrations.forEach { executeSQLMigration(it) @@ -62,10 +61,10 @@ internal class DatabaseMigrationExecutor(private val databaseAdapter: DatabaseAd } @Throws(IOException::class) - private fun performSnapshotIfRequired(oldVersion: Int, newVersion: Int): Int { - return if (oldVersion == 0 && newVersion >= SNAPSHOT_VERSION) { - executeFileSQL(parser.parseSnapshot(SNAPSHOT_VERSION)) - SNAPSHOT_VERSION + private fun performSnapshotIfRequired(oldVersion: Int): Int { + return if (oldVersion == 0) { + executeFileSQL(parser.parseSnapshot()) + BaseDatabaseOpenHelper.VERSION } else { oldVersion } diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseMigrationParser.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseMigrationParser.kt index ded7176ecf..9e3c29be87 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseMigrationParser.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/db/access/internal/DatabaseMigrationParser.kt @@ -49,19 +49,19 @@ internal class DatabaseMigrationParser( } @Throws(IOException::class) - fun parseSnapshot(version: Int): List { - return parseFile("snapshots", version) + fun parseSnapshot(): List { + return parseFile("snapshots", "snapshot") } @Throws(IOException::class) private fun parseMigration(version: Int): DatabaseMigration { - return DatabaseMigration(version, parseFile("migrations", version), codeMigrations.map[version]) + return DatabaseMigration(version, parseFile("migrations", version.toString()), codeMigrations.map[version]) } @Throws(IOException::class) - private fun parseFile(directory: String, newVersion: Int): List { - val fileName = "$directory/$newVersion.sql" - val inputStream = assetManager.open(fileName) + private fun parseFile(directory: String, fileName: String): List { + val path = "$directory/$fileName.sql" + val inputStream = assetManager.open(path) val sc = Scanner(inputStream, "UTF-8") val lines: MutableList = ArrayList() while (sc.hasNextLine()) { From 46ae77fd3ab567dd6fdf7c3001dd53d36e7570d9 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Fri, 13 Jan 2023 11:41:29 +1100 Subject: [PATCH 058/201] [ANDROSDK-1584] Remove UserCredentials table; add User properties --- core/src/main/assets/migrations/137.sql | 5 + core/src/main/assets/snapshots/snapshot.sql | 3 +- .../core/mockwebserver/Dhis2MockServer.java | 2 +- .../org/hisp/dhis/android/core/user/User.java | 14 + .../dhis/android/core/user/UserTableInfo.java | 3 +- .../android/core/user/internal/LogInCall.kt | 8 +- .../android/core/user/internal/UserCall.kt | 6 +- .../UserCredentialsEntityDIModule.java | 8 - .../core/user/internal/UserFields.java | 103 ----- .../android/core/user/internal/UserFields.kt | 124 ++++++ .../android/core/user/internal/UserHandler.kt | 27 +- .../android/core/user/internal/UserStore.java | 1 + .../resources/user/{user.json => user37.json} | 0 .../src/sharedTest/resources/user/user38.json | 32 ++ .../user/{UserShould.java => User37Should.kt} | 60 ++- .../dhis/android/core/user/User38Should.kt | 55 +++ .../internal/AuthorityEndpointCallShould.java | 103 ----- .../internal/AuthorityEndpointCallShould.kt | 87 +++++ ...d.java => IsUserLoggedInCallableShould.kt} | 63 ++- .../internal/LogInCallErrorCatcherShould.java | 93 ----- .../internal/LogInCallErrorCatcherShould.kt | 96 +++++ .../user/internal/LogInCallUnitShould.java | 360 ------------------ .../core/user/internal/LogInCallUnitShould.kt | 286 ++++++++++++++ ...OutCallShould.java => LogOutCallShould.kt} | 108 +++--- .../core/user/internal/UserCallShould.java | 121 ------ .../core/user/internal/UserCallShould.kt | 107 ++++++ ...andlerShould.java => UserHandlerShould.kt} | 98 +++-- 27 files changed, 976 insertions(+), 997 deletions(-) create mode 100644 core/src/main/assets/migrations/137.sql delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/user/internal/UserFields.java create mode 100644 core/src/main/java/org/hisp/dhis/android/core/user/internal/UserFields.kt rename core/src/sharedTest/resources/user/{user.json => user37.json} (100%) create mode 100644 core/src/sharedTest/resources/user/user38.json rename core/src/test/java/org/hisp/dhis/android/core/user/{UserShould.java => User37Should.kt} (54%) create mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/User38Should.kt delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/internal/AuthorityEndpointCallShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/internal/AuthorityEndpointCallShould.kt rename core/src/test/java/org/hisp/dhis/android/core/user/internal/{IsUserLoggedInCallableShould.java => IsUserLoggedInCallableShould.kt} (61%) delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallErrorCatcherShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallErrorCatcherShould.kt delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallUnitShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallUnitShould.kt rename core/src/test/java/org/hisp/dhis/android/core/user/internal/{LogOutCallShould.java => LogOutCallShould.kt} (51%) delete mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/internal/UserCallShould.java create mode 100644 core/src/test/java/org/hisp/dhis/android/core/user/internal/UserCallShould.kt rename core/src/test/java/org/hisp/dhis/android/core/user/internal/{UserHandlerShould.java => UserHandlerShould.kt} (50%) diff --git a/core/src/main/assets/migrations/137.sql b/core/src/main/assets/migrations/137.sql new file mode 100644 index 0000000000..807c5c954b --- /dev/null +++ b/core/src/main/assets/migrations/137.sql @@ -0,0 +1,5 @@ +# Remove UserCredentials model (ANDROSDK-1584); + +ALTER TABLE User ADD COLUMN username TEXT; +UPDATE TABLE User SET username = (SELECT username FROM UserCredentials); +DELETE TABLE UserCredentials; \ No newline at end of file diff --git a/core/src/main/assets/snapshots/snapshot.sql b/core/src/main/assets/snapshots/snapshot.sql index f979a04f74..be9d0fdacf 100644 --- a/core/src/main/assets/snapshots/snapshot.sql +++ b/core/src/main/assets/snapshots/snapshot.sql @@ -1,6 +1,5 @@ CREATE TABLE Configuration (_id INTEGER PRIMARY KEY AUTOINCREMENT, serverUrl TEXT NOT NULL UNIQUE); -CREATE TABLE User (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, birthday TEXT, education TEXT, gender TEXT, jobTitle TEXT, surname TEXT, firstName TEXT, introduction TEXT, employer TEXT, interests TEXT, languages TEXT, email TEXT, phoneNumber TEXT, nationality TEXT); -CREATE TABLE UserCredentials (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, username TEXT, user TEXT NOT NULL UNIQUE, FOREIGN KEY (user) REFERENCES User (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE User (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, birthday TEXT, education TEXT, gender TEXT, jobTitle TEXT, surname TEXT, firstName TEXT, introduction TEXT, employer TEXT, interests TEXT, languages TEXT, email TEXT, phoneNumber TEXT, nationality TEXT, username TEXT); CREATE TABLE OrganisationUnit (_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, path TEXT, openingDate TEXT, closedDate TEXT, level INTEGER, parent TEXT, displayNamePath TEXT, geometryType TEXT, geometryCoordinates TEXT); CREATE TABLE OptionSet (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, version INTEGER, valueType TEXT); CREATE TABLE Option (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, optionSet TEXT NOT NULL, sortOrder INTEGER, color TEXT, icon TEXT, FOREIGN KEY (optionSet) REFERENCES OptionSet (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); 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 a7a7bf7729..9c5b5776b9 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java +++ b/core/src/main/java/org/hisp/dhis/android/core/mockwebserver/Dhis2MockServer.java @@ -92,7 +92,7 @@ public class Dhis2MockServer { 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"; + private static final String USER_JSON = "user/user38.json"; private static final String EVENTS_JSON = "event/events.json"; private static final String LEGEND_SETS_JSON = "legendset/legend_sets.json"; private static final String TRACKED_ENTITY_INSTANCES_JSON = "trackedentity/tracked_entity_instances.json"; diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/User.java b/core/src/main/java/org/hisp/dhis/android/core/user/User.java index 438ebe998b..b65f1acf79 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/User.java +++ b/core/src/main/java/org/hisp/dhis/android/core/user/User.java @@ -30,6 +30,7 @@ import android.database.Cursor; +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; @@ -37,6 +38,7 @@ import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreOrganisationUnitListAdapter; import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreUserCredentialsAdapter; +import org.hisp.dhis.android.core.arch.db.adapters.ignore.internal.IgnoreUserRoleListColumnAdapter; import org.hisp.dhis.android.core.common.BaseIdentifiableObject; import org.hisp.dhis.android.core.common.CoreObject; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; @@ -50,6 +52,9 @@ @JsonDeserialize(builder = $$AutoValue_User.Builder.class) public abstract class User extends BaseIdentifiableObject implements CoreObject { + @Nullable + public abstract String username(); + @Nullable public abstract String birthday(); @@ -101,6 +106,11 @@ public abstract class User extends BaseIdentifiableObject implements CoreObject @ColumnAdapter(IgnoreOrganisationUnitListAdapter.class) abstract List teiSearchOrganisationUnits(); + @Nullable + @JsonProperty() + @ColumnAdapter(IgnoreUserRoleListColumnAdapter.class) + public abstract List userRoles(); + public abstract Builder toBuilder(); public static Builder builder() { @@ -118,6 +128,8 @@ public static abstract class Builder extends BaseIdentifiableObject.Builder teiSearchOrganisationUnits); + public abstract Builder userRoles(List userRoles); + public abstract User build(); } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/UserTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/user/UserTableInfo.java index 7c48de45bd..d90936255e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/UserTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/user/UserTableInfo.java @@ -68,7 +68,8 @@ public String[] all() { UserFields.LANGUAGES, UserFields.EMAIL, UserFields.PHONE_NUMBER, - UserFields.NATIONALITY + UserFields.NATIONALITY, + UserFields.USERNAME ); } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt index 61a3adb1d7..3e7f5d165e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/LogInCall.kt @@ -44,6 +44,7 @@ import org.hisp.dhis.android.core.arch.storage.internal.UserIdInMemoryStore import org.hisp.dhis.android.core.configuration.internal.ServerUrlParser import org.hisp.dhis.android.core.maintenance.D2Error import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.systeminfo.SystemInfo import org.hisp.dhis.android.core.user.AccountDeletionReason import org.hisp.dhis.android.core.user.AuthenticatedUser @@ -65,7 +66,8 @@ internal class LogInCall @Inject internal constructor( private val apiCallErrorCatcher: UserAuthenticateCallErrorCatcher, private val databaseManager: LogInDatabaseManager, private val exceptions: LogInExceptions, - private val accountManager: AccountManagerImpl + private val accountManager: AccountManagerImpl, + private val versionManager: DHISVersionManager ) { fun logIn(username: String?, password: String?, serverUrl: String?): Single { return Single.fromCallable { @@ -86,7 +88,7 @@ internal class LogInCall @Inject internal constructor( val authenticateCall = userService.authenticate( okhttp3.Credentials.basic(username!!, password!!), - UserFields.allFieldsWithoutOrgUnit + UserFields.allFieldsWithoutOrgUnit(versionManager.getVersion()) ) val credentials = Credentials(username, trimmedServerUrl!!, password, null) @@ -177,7 +179,7 @@ internal class LogInCall @Inject internal constructor( val authenticateCall = userService.authenticate( "Bearer ${openIDConnectState.idToken}", - UserFields.allFieldsWithoutOrgUnit + UserFields.allFieldsWithoutOrgUnit(versionManager.getVersion()) ) var credentials: Credentials? = null diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCall.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCall.kt index 3f6bcf6da6..76a2be801a 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCall.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCall.kt @@ -35,6 +35,7 @@ import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor import org.hisp.dhis.android.core.arch.call.internal.GenericCallData import org.hisp.dhis.android.core.arch.handlers.internal.Handler import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager import org.hisp.dhis.android.core.user.User @Reusable @@ -42,13 +43,14 @@ internal class UserCall @Inject constructor( private val genericCallData: GenericCallData, private val apiCallExecutor: APICallExecutor, private val userService: UserService, - private val userHandler: Handler + private val userHandler: Handler, + private val dhisVersionManager: DHISVersionManager ) : Callable { @Throws(D2Error::class) override fun call(): User { val user = apiCallExecutor.executeObjectCall( - userService.getUser(UserFields.allFieldsWithOrgUnit) + userService.getUser(UserFields.allFieldsWithOrgUnit(dhisVersionManager.getVersion())) ) val transaction = genericCallData.databaseAdapter().beginNewTransaction() diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCredentialsEntityDIModule.java b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCredentialsEntityDIModule.java index a2ec0fe543..560e3528e3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCredentialsEntityDIModule.java +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCredentialsEntityDIModule.java @@ -29,8 +29,6 @@ package org.hisp.dhis.android.core.user.internal; import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.handlers.internal.Handler; -import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl; import org.hisp.dhis.android.core.arch.repositories.children.internal.ChildrenAppender; import org.hisp.dhis.android.core.user.UserCredentials; @@ -50,12 +48,6 @@ UserCredentialsStore store(DatabaseAdapter databaseAdapter) { return UserCredentialsStoreImpl.create(databaseAdapter); } - @Provides - @Reusable - Handler handler(UserCredentialsStore store) { - return new IdentifiableHandlerImpl<>(store); - } - @Provides @Reusable Map> childrenAppenders( diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserFields.java b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserFields.java deleted file mode 100644 index e6eb2b3bd0..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserFields.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.user.internal; - -import org.hisp.dhis.android.core.arch.api.fields.internal.Field; -import org.hisp.dhis.android.core.arch.api.fields.internal.Fields; -import org.hisp.dhis.android.core.arch.api.fields.internal.NestedField; -import org.hisp.dhis.android.core.common.BaseIdentifiableObject; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; -import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitFields; -import org.hisp.dhis.android.core.user.User; -import org.hisp.dhis.android.core.user.UserCredentials; - -public final class UserFields { - public static final String BIRTHDAY = "birthday"; - public static final String EDUCATION = "education"; - public static final String GENDER = "gender"; - public static final String JOB_TITLE = "jobTitle"; - public static final String SURNAME = "surname"; - public static final String FIRST_NAME = "firstName"; - public static final String INTRODUCTION = "introduction"; - public static final String EMPLOYER = "employer"; - public static final String INTERESTS = "interests"; - public static final String LANGUAGES = "languages"; - public static final String EMAIL = "email"; - public static final String PHONE_NUMBER = "phoneNumber"; - public static final String NATIONALITY = "nationality"; - public static final String USER_CREDENTIALS = "userCredentials"; - private static final String ORGANISATION_UNITS = "organisationUnits"; - private static final String TEI_SEARCH_ORGANISATION_UNITS = "teiSearchOrganisationUnits"; - - private static final Field uid = Field.create(BaseIdentifiableObject.UID); - private static final Field code = Field.create(BaseIdentifiableObject.CODE); - private static final Field name = Field.create(BaseIdentifiableObject.NAME); - private static final Field displayName = Field.create(BaseIdentifiableObject.DISPLAY_NAME); - private static final Field created = Field.create(BaseIdentifiableObject.CREATED); - private static final Field lastUpdated = Field.create(BaseIdentifiableObject.LAST_UPDATED); - private static final Field birthday = Field.create(BIRTHDAY); - private static final Field education = Field.create(EDUCATION); - private static final Field gender = Field.create(GENDER); - private static final Field jobTitle = Field.create(JOB_TITLE); - private static final Field surname = Field.create(SURNAME); - private static final Field firstName = Field.create(FIRST_NAME); - private static final Field introduction = Field.create(INTRODUCTION); - private static final Field employer = Field.create(EMPLOYER); - private static final Field interests = Field.create(INTERESTS); - private static final Field languages = Field.create(LANGUAGES); - private static final Field email = Field.create(EMAIL); - private static final Field phoneNumber = Field.create(PHONE_NUMBER); - private static final Field nationality = Field.create(NATIONALITY); - private static final Field deleted = Field.create(BaseIdentifiableObject.DELETED); - - private static final NestedField userCredentials - = NestedField.create(USER_CREDENTIALS); - private static final NestedField organisationUnits - = NestedField.create(ORGANISATION_UNITS); - private static final NestedField teiSearchOrganisationUnits - = NestedField.create(TEI_SEARCH_ORGANISATION_UNITS); - - static final Fields allFieldsWithoutOrgUnit = Fields.builder().fields( - uid, code, name, displayName, created, lastUpdated, birthday, education, gender, jobTitle, - surname, firstName, introduction, employer, interests, languages, email, phoneNumber, nationality, - deleted, - userCredentials.with(UserCredentialsFields.allFields) - ).build(); - - static final Fields allFieldsWithOrgUnit = Fields.builder().fields( - uid, code, name, displayName, created, lastUpdated, birthday, education, gender, jobTitle, - surname, firstName, introduction, employer, interests, languages, email, phoneNumber, nationality, - deleted, - userCredentials.with(UserCredentialsFields.allFields), - organisationUnits.with(OrganisationUnitFields.fieldsInUserCall), - teiSearchOrganisationUnits.with(OrganisationUnitFields.fieldsInUserCall) - ).build(); - - private UserFields() {} -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserFields.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserFields.kt new file mode 100644 index 0000000000..16f7839ba0 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserFields.kt @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.user.internal + +import org.hisp.dhis.android.core.arch.api.fields.internal.Field +import org.hisp.dhis.android.core.arch.api.fields.internal.Fields +import org.hisp.dhis.android.core.arch.api.fields.internal.NestedField +import org.hisp.dhis.android.core.common.BaseIdentifiableObject +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit +import org.hisp.dhis.android.core.organisationunit.internal.OrganisationUnitFields +import org.hisp.dhis.android.core.systeminfo.DHISVersion +import org.hisp.dhis.android.core.user.User +import org.hisp.dhis.android.core.user.UserCredentials +import org.hisp.dhis.android.core.user.UserRole + +object UserFields { + const val USERNAME = "username" + const val BIRTHDAY = "birthday" + const val EDUCATION = "education" + const val GENDER = "gender" + const val JOB_TITLE = "jobTitle" + const val SURNAME = "surname" + const val FIRST_NAME = "firstName" + const val INTRODUCTION = "introduction" + const val EMPLOYER = "employer" + const val INTERESTS = "interests" + const val LANGUAGES = "languages" + const val EMAIL = "email" + const val PHONE_NUMBER = "phoneNumber" + const val NATIONALITY = "nationality" + const val USER_CREDENTIALS = "userCredentials" + const val USER_ROLES = "userRoles" + private const val ORGANISATION_UNITS = "organisationUnits" + private const val TEI_SEARCH_ORGANISATION_UNITS = "teiSearchOrganisationUnits" + + private val uid = Field.create(BaseIdentifiableObject.UID) + private val code = Field.create(BaseIdentifiableObject.CODE) + private val name = Field.create(BaseIdentifiableObject.NAME) + private val displayName = Field.create(BaseIdentifiableObject.DISPLAY_NAME) + private val created = Field.create(BaseIdentifiableObject.CREATED) + private val lastUpdated = Field.create(BaseIdentifiableObject.LAST_UPDATED) + private val username = Field.create(USERNAME) + private val birthday = Field.create(BIRTHDAY) + private val education = Field.create(EDUCATION) + private val gender = Field.create(GENDER) + private val jobTitle = Field.create(JOB_TITLE) + private val surname = Field.create(SURNAME) + private val firstName = Field.create(FIRST_NAME) + private val introduction = Field.create(INTRODUCTION) + private val employer = Field.create(EMPLOYER) + private val interests = Field.create(INTERESTS) + private val languages = Field.create(LANGUAGES) + private val email = Field.create(EMAIL) + private val phoneNumber = Field.create(PHONE_NUMBER) + private val nationality = Field.create(NATIONALITY) + private val deleted = Field.create(BaseIdentifiableObject.DELETED) + private val userCredentials = NestedField.create(USER_CREDENTIALS) + private val organisationUnits = NestedField.create(ORGANISATION_UNITS) + private val teiSearchOrganisationUnits = NestedField.create(TEI_SEARCH_ORGANISATION_UNITS) + private val userRoles = NestedField.create(USER_ROLES) + + private fun commonFields(): Fields.Builder { + return Fields.builder().fields( + uid, code, name, displayName, created, lastUpdated, birthday, education, gender, jobTitle, surname, + firstName, introduction, employer, interests, languages, email, phoneNumber, nationality, deleted + ) + } + + private fun baseFields37(): Fields.Builder { + return commonFields() + .fields(userCredentials.with(UserCredentialsFields.allFields)) + } + + private fun baseFields38(): Fields.Builder { + return commonFields() + .fields(username, userRoles.with(UserRoleFields.allFields)) + } + + private fun getBaseFields(version: DHISVersion): Fields.Builder { + return if (version >= DHISVersion.V2_38) { + baseFields38() + } else { + baseFields37() + } + } + + fun allFieldsWithoutOrgUnit(version: DHISVersion): Fields { + return getBaseFields(version).build() + } + + fun allFieldsWithOrgUnit(version: DHISVersion): Fields { + return getBaseFields(version) + .fields( + organisationUnits.with(OrganisationUnitFields.fieldsInUserCall), + teiSearchOrganisationUnits.with(OrganisationUnitFields.fieldsInUserCall) + ) + .build() + } +} \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserHandler.kt index d65b5d0390..2dddb94bba 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserHandler.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserHandler.kt @@ -28,31 +28,40 @@ package org.hisp.dhis.android.core.user.internal import dagger.Reusable -import javax.inject.Inject import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction import org.hisp.dhis.android.core.arch.handlers.internal.Handler import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl -import org.hisp.dhis.android.core.common.ObjectWithUid import org.hisp.dhis.android.core.user.User -import org.hisp.dhis.android.core.user.UserCredentials import org.hisp.dhis.android.core.user.UserInternalAccessor import org.hisp.dhis.android.core.user.UserRole +import javax.inject.Inject @Reusable internal class UserHandler @Inject constructor( userStore: IdentifiableObjectStore, - private val userCredentialsHandler: Handler, private val userRoleHandler: Handler, private val userRoleCollectionCleaner: CollectionCleaner ) : IdentifiableHandlerImpl(userStore) { + override fun beforeObjectHandled(o: User): User { + return o.toBuilder().run { + val userCredentials = UserInternalAccessor.accessUserCredentials(o) + + if (o.username() == null && userCredentials.username() != null) { + username(userCredentials.username()) + } + if (o.userRoles() == null && userCredentials.userRoles() != null) { + userRoles(userCredentials.userRoles()) + } + + build() + } + } + override fun afterObjectHandled(o: User, action: HandleAction) { - val credentials: UserCredentials = UserInternalAccessor.accessUserCredentials(o) - val credentialsWithUser = credentials.toBuilder().user(ObjectWithUid.create(o.uid())).build() - userCredentialsHandler.handle(credentialsWithUser) - userRoleCollectionCleaner.deleteNotPresent(credentialsWithUser.userRoles()) - userRoleHandler.handleMany(credentialsWithUser.userRoles()) + userRoleCollectionCleaner.deleteNotPresent(o.userRoles()) + userRoleHandler.handleMany(o.userRoles()) } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserStore.java b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserStore.java index 69db2bf70f..251b37c92c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/user/internal/UserStore.java @@ -60,6 +60,7 @@ public void bindToStatement(@NonNull User o, @NonNull StatementWrapper w) { w.bind(17, o.email()); w.bind(18, o.phoneNumber()); w.bind(19, o.nationality()); + w.bind(20, o.username()); } }; diff --git a/core/src/sharedTest/resources/user/user.json b/core/src/sharedTest/resources/user/user37.json similarity index 100% rename from core/src/sharedTest/resources/user/user.json rename to core/src/sharedTest/resources/user/user37.json diff --git a/core/src/sharedTest/resources/user/user38.json b/core/src/sharedTest/resources/user/user38.json new file mode 100644 index 0000000000..7cc893c83d --- /dev/null +++ b/core/src/sharedTest/resources/user/user38.json @@ -0,0 +1,32 @@ +{ + "created": "2015-03-31T13:31:09.324", + "lastUpdated": "2016-04-06T00:05:57.495", + "name": "John Barnes", + "id": "DXyJmlo9rge", + "username": "android", + "displayName": "John Barnes", + "firstName": "John", + "surname": "Barnes", + "email": "john@hmail.com", + "userRoles": [ + { + "created": "2018-10-01T14:22:46.030", + "lastUpdated": "2018-10-01T14:22:46.030", + "name": "Superuser", + "id": "Ufph3mGRmMo", + "displayName": "Superuser", + "programs": [ + { + "id": "lxAQ7Zs9VYR" + } + ] + } + ], + "teiSearchOrganisationUnits": [], + "organisationUnits": [ + { + "id": "YuQRtpLP10I", + "path": "/ImspTQPwCqd/O6uvpzGd5pu/YuQRtpLP10I" + } + ] +} \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/UserShould.java b/core/src/test/java/org/hisp/dhis/android/core/user/User37Should.kt similarity index 54% rename from core/src/test/java/org/hisp/dhis/android/core/user/UserShould.java rename to core/src/test/java/org/hisp/dhis/android/core/user/User37Should.kt index f2b612bdcc..dafbfb8f3d 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/user/UserShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/user/User37Should.kt @@ -25,45 +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.user -package org.hisp.dhis.android.core.user; +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.common.BaseObjectShould +import org.hisp.dhis.android.core.common.ObjectShould +import org.junit.Test -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.junit.Test; +class User37Should : BaseObjectShould("user/user37.json"), ObjectShould { -import java.io.IOException; -import java.text.ParseException; - -import static com.google.common.truth.Truth.assertThat; - -public class UserShould extends BaseObjectShould implements ObjectShould { - - public UserShould() { - super("user/user.json"); - } - - @Override @Test - public void map_from_json_string() throws IOException, ParseException { - User user = objectMapper.readValue(jsonStream, User.class); - - assertThat(user.name()).isEqualTo("John Barnes"); - assertThat(user.lastUpdated()).isEqualTo( - BaseIdentifiableObject.DATE_FORMAT.parse("2016-04-06T00:05:57.495")); - assertThat(user.created()).isEqualTo( - BaseIdentifiableObject.DATE_FORMAT.parse("2015-03-31T13:31:09.324")); - assertThat(user.uid()).isEqualTo("DXyJmlo9rge"); - assertThat(user.surname()).isEqualTo("Barnes"); - assertThat(user.firstName()).isEqualTo("John"); - assertThat(user.email()).isEqualTo("john@hmail.com"); - assertThat(user.displayName()).isEqualTo("John Barnes"); - assertThat(UserInternalAccessor.accessUserCredentials(user).uid()).isEqualTo("M0fCOxtkURr"); - assertThat(UserInternalAccessor.accessUserCredentials(user).username()).isEqualTo("android"); - - assertThat(UserInternalAccessor.accessUserCredentials(user).userRoles().get(0).uid()).isEqualTo("Ufph3mGRmMo"); - - assertThat(user.organisationUnits().get(0).uid()).isEqualTo("YuQRtpLP10I"); + override fun map_from_json_string() { + val user = objectMapper.readValue(jsonStream, User::class.java) + + assertThat(user.name()).isEqualTo("John Barnes") + assertThat(user.lastUpdated()).isEqualTo(DateUtils.DATE_FORMAT.parse("2016-04-06T00:05:57.495")) + assertThat(user.created()).isEqualTo(DateUtils.DATE_FORMAT.parse("2015-03-31T13:31:09.324")) + assertThat(user.uid()).isEqualTo("DXyJmlo9rge") + assertThat(user.surname()).isEqualTo("Barnes") + assertThat(user.firstName()).isEqualTo("John") + assertThat(user.email()).isEqualTo("john@hmail.com") + assertThat(user.displayName()).isEqualTo("John Barnes") + assertThat(UserInternalAccessor.accessUserCredentials(user).uid()).isEqualTo("M0fCOxtkURr") + assertThat(UserInternalAccessor.accessUserCredentials(user).username()).isEqualTo("android") + assertThat(UserInternalAccessor.accessUserCredentials(user).userRoles()!![0].uid()).isEqualTo("Ufph3mGRmMo") + assertThat(user.organisationUnits()!![0].uid()).isEqualTo("YuQRtpLP10I") } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/User38Should.kt b/core/src/test/java/org/hisp/dhis/android/core/user/User38Should.kt new file mode 100644 index 0000000000..47830a41dc --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/user/User38Should.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.user + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.common.BaseObjectShould +import org.hisp.dhis.android.core.common.ObjectShould +import org.junit.Test + +class User38Should : BaseObjectShould("user/user38.json"), ObjectShould { + + @Test + override fun map_from_json_string() { + val user = objectMapper.readValue(jsonStream, User::class.java) + + assertThat(user.name()).isEqualTo("John Barnes") + assertThat(user.lastUpdated()).isEqualTo(DateUtils.DATE_FORMAT.parse("2016-04-06T00:05:57.495")) + assertThat(user.created()).isEqualTo(DateUtils.DATE_FORMAT.parse("2015-03-31T13:31:09.324")) + assertThat(user.uid()).isEqualTo("DXyJmlo9rge") + assertThat(user.username()).isEqualTo("android") + assertThat(user.surname()).isEqualTo("Barnes") + assertThat(user.firstName()).isEqualTo("John") + assertThat(user.email()).isEqualTo("john@hmail.com") + assertThat(user.displayName()).isEqualTo("John Barnes") + assertThat(user.userRoles()!![0].uid()).isEqualTo("Ufph3mGRmMo") + assertThat(user.organisationUnits()!![0].uid()).isEqualTo("YuQRtpLP10I") + assertThat(UserInternalAccessor.accessUserCredentials(user)).isNull() + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/AuthorityEndpointCallShould.java b/core/src/test/java/org/hisp/dhis/android/core/user/internal/AuthorityEndpointCallShould.java deleted file mode 100644 index d40a724de0..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/user/internal/AuthorityEndpointCallShould.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.android.core.user.internal; - -import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor; -import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutorImpl; -import org.hisp.dhis.android.core.arch.call.internal.EndpointCall; -import org.hisp.dhis.android.core.arch.handlers.internal.Handler; -import org.hisp.dhis.android.core.common.BaseCallShould; -import org.hisp.dhis.android.core.user.Authority; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mock; - -import java.util.List; -import java.util.concurrent.Callable; - -import retrofit2.Response; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - -@RunWith(JUnit4.class) -public class AuthorityEndpointCallShould extends BaseCallShould { - - @Mock - private AuthorityService authorityService; - - @Mock - private Handler handler; - - @Mock - private retrofit2.Call> retrofitCall; - - @Mock - private List payload; - - @Mock - private UserAccountDisabledErrorCatcher userAccountDisabledErrorCatcher; - - private Callable> endpointCall; - - - @Before - @SuppressWarnings("unchecked") - public void setUp() throws Exception { - super.setUp(); - - APICallExecutor apiCallExecutor = APICallExecutorImpl.create(databaseAdapter, userAccountDisabledErrorCatcher); - endpointCall = new AuthorityEndpointCallFactory(genericCallData, apiCallExecutor, handler, - retrofit.create(AuthorityService.class)).create(); - when(retrofitCall.execute()).thenReturn(Response.success(payload)); - - when(authorityService.getAuthorities()).thenReturn(retrofitCall); - } - - private EndpointCall castedEndpointCall() { - return (EndpointCall) endpointCall; - } - - @Test - public void extend_endpoint_call() { - assertThat(endpointCall instanceof EndpointCall).isTrue(); - } - - @Test - public void have_payload_no_resource_fetcher() { - assertThat(castedEndpointCall().getFetcher() instanceof AuthorityCallFetcher).isTrue(); - } - - @Test - public void have_transactional_no_resource_call_processor() { - EndpointCall castedEndpointCall = (EndpointCall) endpointCall; - assertThat(castedEndpointCall.getProcessor() instanceof AuthorityCallProcessor).isTrue(); - } -} \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/AuthorityEndpointCallShould.kt b/core/src/test/java/org/hisp/dhis/android/core/user/internal/AuthorityEndpointCallShould.kt new file mode 100644 index 0000000000..73226cde7f --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/user/internal/AuthorityEndpointCallShould.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.user.internal + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutorImpl +import org.hisp.dhis.android.core.arch.call.internal.EndpointCall +import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.common.BaseCallShould +import org.hisp.dhis.android.core.user.Authority +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import retrofit2.Call +import retrofit2.Response +import java.util.concurrent.Callable + +@RunWith(JUnit4::class) +class AuthorityEndpointCallShould : BaseCallShould() { + private val authorityService: AuthorityService = mock() + private val handler: Handler = mock() + private val retrofitCall: Call> = mock() + private val payload: List = mock() + private val userAccountDisabledErrorCatcher: UserAccountDisabledErrorCatcher = mock() + + private lateinit var endpointCall: Callable> + + @Before + override fun setUp() { + super.setUp() + val apiCallExecutor = APICallExecutorImpl.create(databaseAdapter, userAccountDisabledErrorCatcher) + endpointCall = AuthorityEndpointCallFactory( + genericCallData, apiCallExecutor, handler, + retrofit.create(AuthorityService::class.java) + ).create() + whenever(retrofitCall.execute()).thenReturn(Response.success(payload)) + whenever(authorityService.authorities).thenReturn(retrofitCall) + } + + private fun castedEndpointCall(): EndpointCall { + return endpointCall as EndpointCall + } + + @Test + fun extend_endpoint_call() { + assertThat(endpointCall is EndpointCall<*>).isTrue() + } + + @Test + fun have_payload_no_resource_fetcher() { + assertThat(castedEndpointCall().fetcher is AuthorityCallFetcher).isTrue() + } + + @Test + fun have_transactional_no_resource_call_processor() { + val castedEndpointCall = endpointCall as EndpointCall + assertThat(castedEndpointCall.processor is AuthorityCallProcessor).isTrue() + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/IsUserLoggedInCallableShould.java b/core/src/test/java/org/hisp/dhis/android/core/user/internal/IsUserLoggedInCallableShould.kt similarity index 61% rename from core/src/test/java/org/hisp/dhis/android/core/user/internal/IsUserLoggedInCallableShould.java rename to core/src/test/java/org/hisp/dhis/android/core/user/internal/IsUserLoggedInCallableShould.kt index fe04ab7acf..c745d92472 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/user/internal/IsUserLoggedInCallableShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/user/internal/IsUserLoggedInCallableShould.kt @@ -25,52 +25,41 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.user.internal -package org.hisp.dhis.android.core.user.internal; +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import io.reactivex.Single +import org.hisp.dhis.android.core.arch.storage.internal.Credentials +import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; +@RunWith(JUnit4::class) +class IsUserLoggedInCallableShould { + private val credentialsSecureStore: CredentialsSecureStore = mock() + private val credentials: Credentials = mock() -import org.hisp.dhis.android.core.arch.storage.internal.Credentials; -import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore; -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 io.reactivex.Single; - -@RunWith(JUnit4.class) -public class IsUserLoggedInCallableShould { - - @Mock - private CredentialsSecureStore credentialsSecureStore; - - @Mock - private Credentials credentials; - - private Single isUserLoggedInSingle; + private lateinit var isUserLoggedInSingle: Single @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - when(credentials.getUsername()).thenReturn("user"); - when(credentials.getPassword()).thenReturn("password"); - - isUserLoggedInSingle = new IsUserLoggedInCallableFactory(credentialsSecureStore).isLogged(); + fun setUp() { + whenever(credentials.username).thenReturn("user") + whenever(credentials.password).thenReturn("password") + isUserLoggedInSingle = IsUserLoggedInCallableFactory(credentialsSecureStore).isLogged } @Test - public void return_false_if_credentials_not_stored() { - assertThat(isUserLoggedInSingle.blockingGet()).isFalse(); + fun return_false_if_credentials_not_stored() { + assertThat(isUserLoggedInSingle.blockingGet()).isFalse() } @Test - public void return_true_if_credentials_stored() { - when(credentialsSecureStore.get()).thenReturn(credentials); - assertThat(isUserLoggedInSingle.blockingGet()).isTrue(); + fun return_true_if_credentials_stored() { + whenever(credentialsSecureStore.get()).thenReturn(credentials) + assertThat(isUserLoggedInSingle.blockingGet()).isTrue() } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallErrorCatcherShould.java b/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallErrorCatcherShould.java deleted file mode 100644 index be555fbbac..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallErrorCatcherShould.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.user.internal; - -import org.hisp.dhis.android.core.arch.api.executors.internal.APIErrorMapper; -import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory; -import org.hisp.dhis.android.core.maintenance.D2ErrorCode; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import okhttp3.ResponseBody; -import retrofit2.Response; - -import static com.google.common.truth.Truth.assertThat; - -@RunWith(JUnit4.class) -public class LogInCallErrorCatcherShould { - - private UserAuthenticateCallErrorCatcher catcher; - - @Before - public void setUp() { - catcher = new UserAuthenticateCallErrorCatcher(ObjectMapperFactory.objectMapper()); - } - - @Test - public void return_bad_credentials_error_for_expected_error_response() { - String responseError = "{\"httpStatus\":\"Unauthorized\",\"httpStatusCode\":401,\"status\":\"ERROR\",\"message\":\"Unauthorized\"}"; - Response response = Response.error(401, ResponseBody.create(null, responseError)); - String errorBody = new APIErrorMapper().getErrorBody(response); - assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.BAD_CREDENTIALS); - } - - @Test - public void return_bad_credentials_error_for_other_messages() { - String responseError = "{\"httpStatus\":\"Unauthorized\",\"httpStatusCode\":401,\"status\":\"ERROR\",\"message\":\"Something new\"}"; - Response response = Response.error(401, ResponseBody.create(null, responseError)); - String errorBody = new APIErrorMapper().getErrorBody(response); - assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.BAD_CREDENTIALS); - } - - @Test - public void return_account_locked() { - String responseError = "{\"httpStatus\":\"Unauthorized\",\"httpStatusCode\":401,\"status\":\"ERROR\",\"message\":\"Account locked\"}"; - Response response = Response.error(401, ResponseBody.create(null, responseError)); - String errorBody = new APIErrorMapper().getErrorBody(response); - assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.USER_ACCOUNT_LOCKED); - } - - @Test - public void return_no_dhis_server_for_another_json() { - String responseError = "{\"other\":\"JSON\"}"; - Response response = Response.error(401, ResponseBody.create(null, responseError)); - String errorBody = new APIErrorMapper().getErrorBody(response); - assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.NO_DHIS2_SERVER); - } - - @Test - public void return_no_dhis_server_for_non_json() { - String responseError = "ERROR"; - Response response = Response.error(401, ResponseBody.create(null, responseError)); - String errorBody = new APIErrorMapper().getErrorBody(response); - assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.NO_DHIS2_SERVER); - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallErrorCatcherShould.kt b/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallErrorCatcherShould.kt new file mode 100644 index 0000000000..f2c4b8a7c2 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallErrorCatcherShould.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.user.internal + +import com.google.common.truth.Truth.assertThat +import okhttp3.ResponseBody +import org.hisp.dhis.android.core.arch.api.executors.internal.APIErrorMapper +import org.hisp.dhis.android.core.arch.json.internal.ObjectMapperFactory.objectMapper +import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import retrofit2.Response + +@RunWith(JUnit4::class) +class LogInCallErrorCatcherShould { + + private lateinit var catcher: UserAuthenticateCallErrorCatcher + + @Before + fun setUp() { + catcher = UserAuthenticateCallErrorCatcher(objectMapper()) + } + + @Test + fun return_bad_credentials_error_for_expected_error_response() { + val responseError = + "{\"httpStatus\":\"Unauthorized\",\"httpStatusCode\":401," + + "\"status\":\"ERROR\",\"message\":\"Unauthorized\"}" + val response = Response.error(401, ResponseBody.create(null, responseError)) + val errorBody = APIErrorMapper().getErrorBody(response) + assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.BAD_CREDENTIALS) + } + + @Test + fun return_bad_credentials_error_for_other_messages() { + val responseError = + "{\"httpStatus\":\"Unauthorized\",\"httpStatusCode\":401," + + "\"status\":\"ERROR\",\"message\":\"Something new\"}" + val response = Response.error(401, ResponseBody.create(null, responseError)) + val errorBody = APIErrorMapper().getErrorBody(response) + assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.BAD_CREDENTIALS) + } + + @Test + fun return_account_locked() { + val responseError = + "{\"httpStatus\":\"Unauthorized\",\"httpStatusCode\":401," + + "\"status\":\"ERROR\",\"message\":\"Account locked\"}" + val response = Response.error(401, ResponseBody.create(null, responseError)) + val errorBody = APIErrorMapper().getErrorBody(response) + assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.USER_ACCOUNT_LOCKED) + } + + @Test + fun return_no_dhis_server_for_another_json() { + val responseError = "{\"other\":\"JSON\"}" + val response = Response.error(401, ResponseBody.create(null, responseError)) + val errorBody = APIErrorMapper().getErrorBody(response) + assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.NO_DHIS2_SERVER) + } + + @Test + fun return_no_dhis_server_for_non_json() { + val responseError = "ERROR" + val response = Response.error(401, ResponseBody.create(null, responseError)) + val errorBody = APIErrorMapper().getErrorBody(response) + assertThat(catcher.catchError(response, errorBody)).isEqualTo(D2ErrorCode.NO_DHIS2_SERVER) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallUnitShould.java b/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallUnitShould.java deleted file mode 100644 index da40b0577e..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallUnitShould.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.user.internal; - -import static com.google.common.truth.Truth.assertThat; -import static org.hisp.dhis.android.core.arch.helpers.UserHelper.md5; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.same; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static okhttp3.Credentials.basic; - -import org.hisp.dhis.android.core.arch.api.executors.internal.APICallErrorCatcher; -import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor; -import org.hisp.dhis.android.core.arch.api.fields.internal.Fields; -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; -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.repositories.collection.ReadOnlyWithDownloadObjectRepository; -import org.hisp.dhis.android.core.arch.storage.internal.Credentials; -import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore; -import org.hisp.dhis.android.core.arch.storage.internal.UserIdInMemoryStore; -import org.hisp.dhis.android.core.common.BaseCallShould; -import org.hisp.dhis.android.core.configuration.internal.MultiUserDatabaseManager; -import org.hisp.dhis.android.core.maintenance.D2Error; -import org.hisp.dhis.android.core.maintenance.D2ErrorCode; -import org.hisp.dhis.android.core.settings.internal.GeneralSettingCall; -import org.hisp.dhis.android.core.systeminfo.SystemInfo; -import org.hisp.dhis.android.core.user.AuthenticatedUser; -import org.hisp.dhis.android.core.user.User; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.stubbing.OngoingStubbing; - -import io.reactivex.Completable; -import io.reactivex.Single; -import io.reactivex.observers.TestObserver; - - -@RunWith(JUnit4.class) -public class LogInCallUnitShould extends BaseCallShould { - - @Mock - private UserService userService; - - @Mock - private APICallExecutor apiCallExecutor; - - @Mock - private Handler userHandler; - - @Mock - private ObjectWithoutUidStore authenticatedUserStore; - - @Mock - private CredentialsSecureStore credentialsSecureStore; - - @Mock - private UserIdInMemoryStore userIdStore; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private retrofit2.Call authenticateAPICall; - - @Captor - private ArgumentCaptor credentialsCaptor; - - @Captor - private ArgumentCaptor> filterCaptor; - - @Mock - private User user; - - @Mock - private User anotherUser; - - @Mock - private User loggedUser; - - @Mock - private SystemInfo systemInfoFromAPI; - - @Mock - private SystemInfo systemInfoFromDb; - - @Mock - private AuthenticatedUser authenticatedUser; - - @Mock - private Credentials credentials; - - @Mock - private IdentifiableObjectStore userStore; - - @Mock - private ReadOnlyWithDownloadObjectRepository systemInfoRepository; - - @Mock - private MultiUserDatabaseManager multiUserDatabaseManager; - - @Mock - private GeneralSettingCall generalSettingCall; - - @Mock - private UserAuthenticateCallErrorCatcher apiCallErrorCatcher; - - @Mock - private AccountManagerImpl accountManager; - - // call we are testing - private Single logInSingle; - - private static final String USERNAME = "test_username"; - private static final String UID = "test_uid"; - private static final String PASSWORD = "test_password"; - - private static final String baseEndpoint = "https://dhis-instance.org"; - private static final String serverUrl = baseEndpoint; - - @Before - @SuppressWarnings("unchecked") - public void setUp() throws Exception { - super.setUp(); - - when(user.uid()).thenReturn(UID); - when(loggedUser.uid()).thenReturn(UID); - when(systemInfoFromAPI.serverDate()).thenReturn(serverDate); - - when(anotherUser.uid()).thenReturn("anotherUserUid"); - - when(credentials.getUsername()).thenReturn(USERNAME); - when(credentials.getPassword()).thenReturn(PASSWORD); - - when(authenticatedUser.user()).thenReturn(UID); - when(authenticatedUser.hash()).thenReturn(md5(USERNAME, PASSWORD)); - - when(systemInfoFromAPI.contextPath()).thenReturn(baseEndpoint); - when(systemInfoFromDb.contextPath()).thenReturn(baseEndpoint); - - when(userService.authenticate(any(String.class), any(Fields.class))).thenReturn(authenticateAPICall); - - when(systemInfoRepository.download()).thenReturn(Completable.complete()); - whenAPICall().thenReturn(user); - - when(userStore.selectFirst()).thenReturn(loggedUser); - when(systemInfoRepository.blockingGet()).thenReturn(systemInfoFromDb); - - when(databaseAdapter.beginNewTransaction()).thenReturn(transaction); - - when(d2Error.errorCode()).thenReturn(D2ErrorCode.SOCKET_TIMEOUT); - when(d2Error.isOffline()).thenReturn(true); - when(generalSettingCall.isDatabaseEncrypted()).thenReturn(Single.just(false)); - - logInSingle = instantiateCall(USERNAME, PASSWORD, serverUrl); - } - - private Single instantiateCall(String username, String password, String serverUrl) { - return new LogInCall(databaseAdapter, apiCallExecutor, - userService, credentialsSecureStore, userIdStore, userHandler, authenticatedUserStore, - systemInfoRepository, userStore, apiCallErrorCatcher, - new LogInDatabaseManager(multiUserDatabaseManager, generalSettingCall), - new LogInExceptions(credentialsSecureStore), accountManager).logIn(username, password, serverUrl); - } - - private OngoingStubbing whenAPICall() throws D2Error { - return when(apiCallExecutor.executeObjectCallWithErrorCatcher(same(authenticateAPICall), any(APICallErrorCatcher.class))); - } - - @Test - public void throw_d2_error_for_null_username() { - TestObserver testObserver = instantiateCall(null, PASSWORD, serverUrl).test(); - assertD2Error(testObserver, D2ErrorCode.LOGIN_USERNAME_NULL); - } - - @Test - public void throw_d2_error_for_null_password() { - TestObserver testObserver = instantiateCall(USERNAME, null, serverUrl).test(); - assertD2Error(testObserver, D2ErrorCode.LOGIN_PASSWORD_NULL); - } - - @Test - public void throw_d2_error_for_null_server_url() { - TestObserver testObserver = instantiateCall(USERNAME, PASSWORD, null).test(); - assertD2Error(testObserver, D2ErrorCode.SERVER_URL_NULL); - } - - @Test - public void throw_d2_error_for_wrong_server_url() { - TestObserver testObserver = instantiateCall(USERNAME, PASSWORD, "this is no URL").test(); - assertD2Error(testObserver, D2ErrorCode.SERVER_URL_MALFORMED); - } - - private void assertD2Error(TestObserver testObserver, D2ErrorCode code) { - testObserver.awaitTerminalEvent(); - Throwable error = testObserver.errors().get(0); - assertThat(error).isInstanceOf(D2Error.class); - assertThat(((D2Error) error).errorCode()).isEqualTo(code); - testObserver.dispose(); - } - - @Test - public void invoke_server_with_correct_parameters_after_call() { - when(userService.authenticate( - credentialsCaptor.capture(), filterCaptor.capture()) - ).thenReturn(authenticateAPICall); - - logInSingle.blockingGet(); - - assertThat(basic(USERNAME, PASSWORD)) - .isEqualTo(credentialsCaptor.getValue()); - } - - @Test - public void not_invoke_stores_on_exception_on_call() throws D2Error { - whenAPICall().thenThrow(d2Error); - when(d2Error.errorCode()).thenReturn(D2ErrorCode.UNEXPECTED); - - TestObserver testObserver = logInSingle.test(); - testObserver.awaitTerminalEvent(); - - assertThat(testObserver.errorCount()).isEqualTo(1); - testObserver.dispose(); - - verifyNoTransactionCompleted(); - - // stores must not be invoked - verify(authenticatedUserStore, never()).updateOrInsertWhere(any(AuthenticatedUser.class)); - verifyNoMoreInteractions(userHandler); - } - - @Test - public void succeed_when_no_previous_user_or_system_info() { - logInSingle.blockingGet(); - verifySuccess(); - } - - @Test - public void throw_d2_error_if_user_already_signed_in() { - when(credentialsSecureStore.get()).thenReturn(credentials); - when(userIdStore.get()).thenReturn("userId"); - TestObserver testObserver = logInSingle.test(); - assertD2Error(testObserver, D2ErrorCode.ALREADY_AUTHENTICATED); - } - - @Test - public void succeed_for_login_online_if_user_has_logged_out() { - when(authenticatedUserStore.selectFirst()).thenReturn(authenticatedUser); - logInSingle.blockingGet(); - verifySuccess(); - } - - // Offline support - - @Test - public void succeed_for_login_offline_if_database_exists_and_authenticated_user_too() throws Exception { - whenAPICall().thenThrow(d2Error); - - when(multiUserDatabaseManager.loadExistingKeepingEncryption(serverUrl, USERNAME)).thenReturn(true); - when(authenticatedUserStore.selectFirst()).thenReturn(authenticatedUser); - - logInSingle.test().awaitTerminalEvent(); - verifySuccessOffline(); - } - - @Test - public void succeed_for_login_offline_if_server_has_a_trailing_slash() throws Exception { - whenAPICall().thenThrow(d2Error); - - when(authenticatedUserStore.selectFirst()).thenReturn(authenticatedUser); - when(multiUserDatabaseManager.loadExistingKeepingEncryption(serverUrl, USERNAME)).thenReturn(true); - - Single loginCall = instantiateCall(USERNAME, PASSWORD, serverUrl + "/"); - - loginCall.test().awaitTerminalEvent(); - verifySuccessOffline(); - } - - @Test - public void throw_original_d2_error_if_no_previous_database_offline() throws Exception { - whenAPICall().thenThrow(d2Error); - - when(authenticatedUserStore.selectFirst()).thenReturn(null); - - TestObserver testObserver = logInSingle.test(); - assertD2Error(testObserver, d2Error.errorCode()); - } - - @Test - public void throw_d2_error_if_no_previous_authenticated_user_offline() throws Exception { - whenAPICall().thenThrow(d2Error); - - when(multiUserDatabaseManager.loadExistingKeepingEncryption(serverUrl, USERNAME)).thenReturn(true); - when(authenticatedUserStore.selectFirst()).thenReturn(null); - - TestObserver testObserver = logInSingle.test(); - assertD2Error(testObserver, D2ErrorCode.NO_AUTHENTICATED_USER_OFFLINE); - } - - @Test - public void throw_d2_error_if_logging_offline_with_bad_credentials() throws Exception { - whenAPICall().thenThrow(d2Error); - - when(authenticatedUser.hash()).thenReturn("different_hash"); - when(multiUserDatabaseManager.loadExistingKeepingEncryption(serverUrl, USERNAME)).thenReturn(true); - when(authenticatedUserStore.selectFirst()).thenReturn(authenticatedUser); - - TestObserver testObserver = logInSingle.test(); - assertD2Error(testObserver, D2ErrorCode.BAD_CREDENTIALS); - } - - private void verifySuccess() { - AuthenticatedUser authenticatedUserModel = - AuthenticatedUser.builder() - .user(UID) - .hash(md5(USERNAME, PASSWORD)) - .build(); - verifyTransactionComplete(); - verify(authenticatedUserStore).updateOrInsertWhere(authenticatedUserModel); - verify(userHandler).handle(eq(user)); - } - - private void verifySuccessOffline() { - verify(credentialsSecureStore).set(new Credentials(USERNAME, serverUrl, PASSWORD, null)); - verify(userIdStore).set("test_uid"); - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallUnitShould.kt b/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallUnitShould.kt new file mode 100644 index 0000000000..2cee3de4d4 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogInCallUnitShould.kt @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.user.internal + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.* +import io.reactivex.Completable +import io.reactivex.Single +import io.reactivex.observers.TestObserver +import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor +import org.hisp.dhis.android.core.arch.api.fields.internal.Fields +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +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.helpers.UserHelper +import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository +import org.hisp.dhis.android.core.arch.storage.internal.Credentials +import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore +import org.hisp.dhis.android.core.arch.storage.internal.UserIdInMemoryStore +import org.hisp.dhis.android.core.common.BaseCallShould +import org.hisp.dhis.android.core.configuration.internal.MultiUserDatabaseManager +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import org.hisp.dhis.android.core.settings.internal.GeneralSettingCall +import org.hisp.dhis.android.core.systeminfo.DHISVersion +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager +import org.hisp.dhis.android.core.systeminfo.SystemInfo +import org.hisp.dhis.android.core.user.AuthenticatedUser +import org.hisp.dhis.android.core.user.User +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.* +import org.mockito.stubbing.OngoingStubbing +import retrofit2.Call + +@RunWith(JUnit4::class) +class LogInCallUnitShould : BaseCallShould() { + private val userService: UserService = mock() + private val apiCallExecutor: APICallExecutor = mock() + private val userHandler: Handler = mock() + private val authenticatedUserStore: ObjectWithoutUidStore = mock() + private val credentialsSecureStore: CredentialsSecureStore = mock() + private val userIdStore: UserIdInMemoryStore = mock() + private val authenticateAPICall: Call = mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) + + private val credentialsCaptor: KArgumentCaptor = argumentCaptor() + private val filterCaptor: KArgumentCaptor> = argumentCaptor() + + private val user: User = mock() + private val anotherUser: User = mock() + private val loggedUser: User = mock() + private val systemInfoFromAPI: SystemInfo = mock() + private val systemInfoFromDb: SystemInfo = mock() + private val authenticatedUser: AuthenticatedUser = mock() + private val credentials: Credentials = mock() + private val userStore: IdentifiableObjectStore = mock() + private val systemInfoRepository: ReadOnlyWithDownloadObjectRepository = mock() + private val multiUserDatabaseManager: MultiUserDatabaseManager = mock() + private val generalSettingCall: GeneralSettingCall = mock() + private val apiCallErrorCatcher: UserAuthenticateCallErrorCatcher = mock() + private val accountManager: AccountManagerImpl = mock() + private val versionManager: DHISVersionManager = mock() + + // call we are testing + private lateinit var logInSingle: Single + + @Before + @Throws(Exception::class) + override fun setUp() { + super.setUp() + whenever(user.uid()).thenReturn(UID) + whenever(loggedUser.uid()).thenReturn(UID) + whenever(systemInfoFromAPI.serverDate()).thenReturn(serverDate) + whenever(anotherUser.uid()).thenReturn("anotherUserUid") + whenever(credentials.username).thenReturn(USERNAME) + whenever(credentials.password).thenReturn(PASSWORD) + whenever(authenticatedUser.user()).thenReturn(UID) + whenever(authenticatedUser.hash()).thenReturn(UserHelper.md5(USERNAME, PASSWORD)) + whenever(systemInfoFromAPI.contextPath()).thenReturn(baseEndpoint) + whenever(systemInfoFromDb.contextPath()).thenReturn(baseEndpoint) + whenever?>(userService.authenticate(any(), any())).thenReturn(authenticateAPICall) + whenever(systemInfoRepository.download()).thenReturn(Completable.complete()) + whenAPICall().thenReturn(user) + whenever(userStore.selectFirst()).thenReturn(loggedUser) + whenever(systemInfoRepository.blockingGet()).thenReturn(systemInfoFromDb) + whenever(databaseAdapter.beginNewTransaction()).thenReturn(transaction) + whenever(d2Error.errorCode()).thenReturn(D2ErrorCode.SOCKET_TIMEOUT) + whenever(d2Error.isOffline).thenReturn(true) + whenever(generalSettingCall.isDatabaseEncrypted()).thenReturn(Single.just(false)) + whenever(versionManager.getVersion()).thenReturn(DHISVersion.V2_39) + + logInSingle = instantiateCall(USERNAME, PASSWORD, serverUrl) + } + + private fun instantiateCall(username: String?, password: String?, serverUrl: String?): Single { + return LogInCall( + databaseAdapter, apiCallExecutor, + userService, credentialsSecureStore, userIdStore, userHandler, authenticatedUserStore, + systemInfoRepository, userStore, apiCallErrorCatcher, + LogInDatabaseManager(multiUserDatabaseManager, generalSettingCall), + LogInExceptions(credentialsSecureStore), accountManager, versionManager + ).logIn(username, password, serverUrl) + } + + private fun whenAPICall(): OngoingStubbing { + return whenever(apiCallExecutor.executeObjectCallWithErrorCatcher(same(authenticateAPICall), any())) + } + + @Test + fun throw_d2_error_for_null_username() { + val testObserver = instantiateCall(null, PASSWORD, serverUrl).test() + assertD2Error(testObserver, D2ErrorCode.LOGIN_USERNAME_NULL) + } + + @Test + fun throw_d2_error_for_null_password() { + val testObserver = instantiateCall(USERNAME, null, serverUrl).test() + assertD2Error(testObserver, D2ErrorCode.LOGIN_PASSWORD_NULL) + } + + @Test + fun throw_d2_error_for_null_server_url() { + val testObserver = instantiateCall(USERNAME, PASSWORD, null).test() + assertD2Error(testObserver, D2ErrorCode.SERVER_URL_NULL) + } + + @Test + fun throw_d2_error_for_wrong_server_url() { + val testObserver = instantiateCall(USERNAME, PASSWORD, "this is no URL").test() + assertD2Error(testObserver, D2ErrorCode.SERVER_URL_MALFORMED) + } + + private fun assertD2Error(testObserver: TestObserver, code: D2ErrorCode) { + testObserver.awaitTerminalEvent() + val error = testObserver.errors()[0] + assertThat(error).isInstanceOf(D2Error::class.java) + assertThat((error as D2Error).errorCode()).isEqualTo(code) + testObserver.dispose() + } + + @Test + fun invoke_server_with_correct_parameters_after_call() { + whenever( + userService.authenticate( + credentialsCaptor.capture(), filterCaptor.capture() + ) + ).thenReturn(authenticateAPICall) + logInSingle.blockingGet() + assertThat(okhttp3.Credentials.basic(USERNAME, PASSWORD)).isEqualTo(credentialsCaptor.firstValue) + } + + @Test + @Throws(D2Error::class) + fun not_invoke_stores_on_exception_on_call() { + whenAPICall().thenThrow(d2Error) + whenever(d2Error.errorCode()).thenReturn(D2ErrorCode.UNEXPECTED) + + val testObserver = logInSingle.test() + testObserver.awaitTerminalEvent() + assertThat(testObserver.errorCount()).isEqualTo(1) + testObserver.dispose() + verifyNoTransactionCompleted() + + // stores must not be invoked + verify(authenticatedUserStore, never()).updateOrInsertWhere(any()) + verifyNoMoreInteractions(userHandler) + } + + @Test + fun succeed_when_no_previous_user_or_system_info() { + logInSingle.blockingGet() + verifySuccess() + } + + @Test + fun throw_d2_error_if_user_already_signed_in() { + whenever(credentialsSecureStore.get()).thenReturn(credentials) + whenever(userIdStore.get()).thenReturn("userId") + val testObserver = logInSingle.test() + assertD2Error(testObserver, D2ErrorCode.ALREADY_AUTHENTICATED) + } + + @Test + fun succeed_for_login_online_if_user_has_logged_out() { + whenever(authenticatedUserStore.selectFirst()).thenReturn(authenticatedUser) + logInSingle.blockingGet() + verifySuccess() + } + + // Offline support + @Test + fun succeed_for_login_offline_if_database_exists_and_authenticated_user_too() { + whenAPICall().thenThrow(d2Error) + whenever(multiUserDatabaseManager.loadExistingKeepingEncryption(serverUrl, USERNAME)).thenReturn(true) + whenever(authenticatedUserStore.selectFirst()).thenReturn(authenticatedUser) + logInSingle.test().awaitTerminalEvent() + verifySuccessOffline() + } + + @Test + fun succeed_for_login_offline_if_server_has_a_trailing_slash() { + whenAPICall().thenThrow(d2Error) + whenever(authenticatedUserStore.selectFirst()).thenReturn(authenticatedUser) + whenever(multiUserDatabaseManager.loadExistingKeepingEncryption(serverUrl, USERNAME)).thenReturn(true) + val loginCall = instantiateCall(USERNAME, PASSWORD, serverUrl + "/") + loginCall.test().awaitTerminalEvent() + verifySuccessOffline() + } + + @Test + fun throw_original_d2_error_if_no_previous_database_offline() { + whenAPICall().thenThrow(d2Error) + whenever(authenticatedUserStore.selectFirst()).thenReturn(null) + val testObserver = logInSingle.test() + assertD2Error(testObserver, d2Error.errorCode()) + } + + @Test + fun throw_d2_error_if_no_previous_authenticated_user_offline() { + whenAPICall().thenThrow(d2Error) + whenever(multiUserDatabaseManager.loadExistingKeepingEncryption(serverUrl, USERNAME)).thenReturn(true) + whenever(authenticatedUserStore.selectFirst()).thenReturn(null) + val testObserver = logInSingle.test() + assertD2Error(testObserver, D2ErrorCode.NO_AUTHENTICATED_USER_OFFLINE) + } + + @Test + fun throw_d2_error_if_logging_offline_with_bad_credentials() { + whenAPICall().thenThrow(d2Error) + whenever(authenticatedUser.hash()).thenReturn("different_hash") + whenever(multiUserDatabaseManager.loadExistingKeepingEncryption(serverUrl, USERNAME)).thenReturn(true) + whenever(authenticatedUserStore.selectFirst()).thenReturn(authenticatedUser) + val testObserver = logInSingle.test() + assertD2Error(testObserver, D2ErrorCode.BAD_CREDENTIALS) + } + + private fun verifySuccess() { + val authenticatedUserModel = AuthenticatedUser.builder() + .user(UID) + .hash(UserHelper.md5(USERNAME, PASSWORD)) + .build() + verifyTransactionComplete() + verify(authenticatedUserStore).updateOrInsertWhere(authenticatedUserModel) + verify(userHandler).handle(eq(user)) + } + + private fun verifySuccessOffline() { + verify(credentialsSecureStore).set(Credentials(USERNAME, serverUrl, PASSWORD, null)) + verify(userIdStore).set("test_uid") + } + + companion object { + private const val USERNAME = "test_username" + private const val UID = "test_uid" + private const val PASSWORD = "test_password" + private const val baseEndpoint = "https://dhis-instance.org" + private const val serverUrl = baseEndpoint + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogOutCallShould.java b/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogOutCallShould.kt similarity index 51% rename from core/src/test/java/org/hisp/dhis/android/core/user/internal/LogOutCallShould.java rename to core/src/test/java/org/hisp/dhis/android/core/user/internal/LogOutCallShould.kt index fc22d4ccea..76de81dfe9 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogOutCallShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/user/internal/LogOutCallShould.kt @@ -25,78 +25,56 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -package org.hisp.dhis.android.core.user.internal; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; -import org.hisp.dhis.android.core.arch.db.access.internal.DatabaseAdapterFactory; -import org.hisp.dhis.android.core.arch.storage.internal.Credentials; -import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore; -import org.hisp.dhis.android.core.arch.storage.internal.UserIdInMemoryStore; -import org.hisp.dhis.android.core.maintenance.D2Error; -import org.hisp.dhis.android.core.maintenance.D2ErrorCode; -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 io.reactivex.observers.TestObserver; - -@RunWith(JUnit4.class) -public class LogOutCallShould { - - @Mock - private CredentialsSecureStore credentialsSecureStore; - - @Mock - private UserIdInMemoryStore userIdStore; - - @Mock - private Credentials credentials; - - @Mock - private DatabaseAdapter databaseAdapter; - - @Mock - private DatabaseAdapterFactory databaseAdapterFactory; - - private LogOutCall logOutCall; +package org.hisp.dhis.android.core.user.internal + +import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.times +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever +import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter +import org.hisp.dhis.android.core.arch.db.access.internal.DatabaseAdapterFactory +import org.hisp.dhis.android.core.arch.storage.internal.Credentials +import org.hisp.dhis.android.core.arch.storage.internal.CredentialsSecureStore +import org.hisp.dhis.android.core.arch.storage.internal.UserIdInMemoryStore +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.maintenance.D2ErrorCode +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class LogOutCallShould { + private val credentialsSecureStore: CredentialsSecureStore = mock() + private val userIdStore: UserIdInMemoryStore = mock() + private val credentials: Credentials = mock() + private val databaseAdapter: DatabaseAdapter = mock() + private val databaseAdapterFactory: DatabaseAdapterFactory = mock() + + private lateinit var logOutCall: LogOutCall @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(credentials.getUsername()).thenReturn("user"); - when(credentials.getPassword()).thenReturn("password"); - - logOutCall = new LogOutCall(databaseAdapter, databaseAdapterFactory, credentialsSecureStore, userIdStore); + fun setUp() { + whenever(credentials.username).thenReturn("user") + whenever(credentials.password).thenReturn("password") + logOutCall = LogOutCall(databaseAdapter, databaseAdapterFactory, credentialsSecureStore, userIdStore) } @Test - public void clear_user_credentials() { - when(credentialsSecureStore.get()).thenReturn(credentials); - when(userIdStore.get()).thenReturn("user-id"); - - logOutCall.logOut().blockingAwait(); - - verify(credentialsSecureStore, times(1)).remove(); + fun clear_user_credentials() { + whenever(credentialsSecureStore.get()).thenReturn(credentials) + whenever(userIdStore.get()).thenReturn("user-id") + logOutCall.logOut().blockingAwait() + verify(credentialsSecureStore, times(1)).remove() } @Test - public void throw_d2_exception_if_no_authenticated_user() { - TestObserver testObserver = logOutCall.logOut().test(); - testObserver.awaitTerminalEvent(); - - D2Error d2Error = (D2Error) testObserver.errors().get(0); - assertThat(d2Error.errorCode()).isEqualTo(D2ErrorCode.NO_AUTHENTICATED_USER); - - testObserver.dispose(); + fun throw_d2_exception_if_no_authenticated_user() { + val testObserver = logOutCall.logOut().test() + testObserver.awaitTerminalEvent() + val d2Error = testObserver.errors()[0] as D2Error + assertThat(d2Error.errorCode()).isEqualTo(D2ErrorCode.NO_AUTHENTICATED_USER) + testObserver.dispose() } } \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserCallShould.java b/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserCallShould.java deleted file mode 100644 index 5ab3c3c278..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserCallShould.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.hisp.dhis.android.core.user.internal; - -import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor; -import org.hisp.dhis.android.core.arch.api.fields.internal.Fields; -import org.hisp.dhis.android.core.arch.handlers.internal.Handler; -import org.hisp.dhis.android.core.common.BaseCallShould; -import org.hisp.dhis.android.core.maintenance.D2Error; -import org.hisp.dhis.android.core.user.User; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mock; - -import java.util.concurrent.Callable; - -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(JUnit4.class) -public class UserCallShould extends BaseCallShould { - - @Mock - private UserService userService; - - @Mock - private APICallExecutor apiCallExecutor; - - @Mock - private Handler userHandler; - - @Mock - private retrofit2.Call userCall; - - @Mock - private User user; - - private Callable userSyncCall; - - @Override - @Before - @SuppressWarnings("unchecked") - public void setUp() throws Exception { - super.setUp(); - userSyncCall = new UserCall(genericCallData, apiCallExecutor, userService, userHandler); - when(userService.getUser(any(Fields.class))).thenReturn(userCall); - } - - @Test - public void not_invoke_stores_on_call_io_exception() throws D2Error { - when(apiCallExecutor.executeObjectCall(userCall)).thenThrow(d2Error); - - try { - userSyncCall.call(); - fail("Exception was not thrown"); - } catch (Exception ex) { - - // verify that handlers was not touched - verify(databaseAdapter, never()).beginNewTransaction(); - verify(transaction, never()).setSuccessful(); - verify(transaction, never()).end(); - - verify(userHandler, never()).handle(eq(user)); - } - } - - @Test - public void not_invoke_handler_after_call_failure() throws Exception { - when(apiCallExecutor.executeObjectCall(userCall)).thenThrow(d2Error); - - try { - userSyncCall.call(); - fail("Call should't succeed"); - } catch (D2Error d2Exception) { - } - - // verify that database was not touched - verify(databaseAdapter, never()).beginNewTransaction(); - verify(transaction, never()).setSuccessful(); - verify(transaction, never()).end(); - verify(userHandler, never()).handle(eq(user)); - } - - @Test - public void invoke_handlers_on_success() throws Exception { - when(apiCallExecutor.executeObjectCall(userCall)).thenReturn(user); - userSyncCall.call(); - verify(userHandler).handle(eq(user)); - } -} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserCallShould.kt b/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserCallShould.kt new file mode 100644 index 0000000000..f06e508646 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserCallShould.kt @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.user.internal + +import com.nhaarman.mockitokotlin2.* +import org.hisp.dhis.android.core.arch.api.executors.internal.APICallExecutor +import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.common.BaseCallShould +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.systeminfo.DHISVersion +import org.hisp.dhis.android.core.systeminfo.DHISVersionManager +import org.hisp.dhis.android.core.user.User +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import retrofit2.Call +import java.util.concurrent.Callable + +@RunWith(JUnit4::class) +class UserCallShould : BaseCallShould() { + private val userService: UserService = mock() + private val apiCallExecutor: APICallExecutor = mock() + private val userHandler: Handler = mock() + private val userCall: Call = mock() + private val dhisVersionManager: DHISVersionManager = mock() + + private val user: User = mock() + + private lateinit var userSyncCall: Callable + + @Before + @Throws(Exception::class) + override fun setUp() { + super.setUp() + userSyncCall = UserCall(genericCallData, apiCallExecutor, userService, userHandler, dhisVersionManager) + whenever?>(userService.getUser(any())).thenReturn(userCall) + whenever(dhisVersionManager.getVersion()).thenReturn(DHISVersion.V2_39) + } + + @Test + @Throws(D2Error::class) + fun not_invoke_stores_on_call_io_exception() { + whenever(apiCallExecutor.executeObjectCall(userCall)).thenThrow(d2Error) + try { + userSyncCall.call() + Assert.fail("Exception was not thrown") + } catch (ex: Exception) { + + // verify that handlers was not touched + verify(databaseAdapter, never()).beginNewTransaction() + verify(transaction, never()).setSuccessful() + verify(transaction, never()).end() + verify(userHandler, never()).handle(user) + } + } + + @Test + @Throws(Exception::class) + fun not_invoke_handler_after_call_failure() { + whenever(apiCallExecutor.executeObjectCall(userCall)).thenThrow(d2Error) + try { + userSyncCall.call() + Assert.fail("Call should't succeed") + } catch (d2Exception: D2Error) { + } + + // verify that database was not touched + verify(databaseAdapter, never()).beginNewTransaction() + verify(transaction, never()).setSuccessful() + verify(transaction, never()).end() + verify(userHandler, never()).handle(user) + } + + @Test + fun invoke_handlers_on_success() { + whenever(apiCallExecutor.executeObjectCall(userCall)).thenReturn(user) + userSyncCall.call() + verify(userHandler).handle(eq(user)) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserHandlerShould.java b/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserHandlerShould.kt similarity index 50% rename from core/src/test/java/org/hisp/dhis/android/core/user/internal/UserHandlerShould.java rename to core/src/test/java/org/hisp/dhis/android/core/user/internal/UserHandlerShould.kt index df5d8ebc9e..4cfdf87be3 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserHandlerShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/user/internal/UserHandlerShould.kt @@ -25,70 +25,64 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.user.internal; +package org.hisp.dhis.android.core.user.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.handlers.internal.HandleAction; -import org.hisp.dhis.android.core.arch.handlers.internal.Handler; -import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl; -import org.hisp.dhis.android.core.common.ObjectWithUid; -import org.hisp.dhis.android.core.user.User; -import org.hisp.dhis.android.core.user.UserCredentials; -import org.hisp.dhis.android.core.user.UserInternalAccessor; -import org.hisp.dhis.android.core.user.UserRole; -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 com.nhaarman.mockitokotlin2.* +import org.hisp.dhis.android.core.arch.cleaners.internal.CollectionCleaner +import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.arch.handlers.internal.HandleAction +import org.hisp.dhis.android.core.arch.handlers.internal.Handler +import org.hisp.dhis.android.core.arch.handlers.internal.IdentifiableHandlerImpl +import org.hisp.dhis.android.core.user.User +import org.hisp.dhis.android.core.user.UserCredentials +import org.hisp.dhis.android.core.user.UserInternalAccessor +import org.hisp.dhis.android.core.user.UserRole +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +@RunWith(JUnit4::class) +class UserHandlerShould { + private val userStore: IdentifiableObjectStore = mock() + private val userRoleHandler: Handler = mock() + private val userRoleCollectionCleaner: CollectionCleaner = mock() -@RunWith(JUnit4.class) -public class UserHandlerShould { + private val userRoles: List = mock() - @Mock - private IdentifiableObjectStore userStore; - - @Mock - private Handler userCredentialsHandler; - - @Mock - private Handler userRoleHandler; - - @Mock - private CollectionCleaner userRoleCollectionCleaner; - - @Mock - private User user; - - private UserCredentials userCredentials; + private lateinit var user: User + private lateinit var userCredentials: UserCredentials // object to test - private UserHandler userHandler; + private lateinit var userHandler: UserHandler @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - userHandler = new UserHandler(userStore, userCredentialsHandler, userRoleHandler, userRoleCollectionCleaner); - userCredentials = UserCredentials.builder().uid("credentialsUid").build(); - when(UserInternalAccessor.accessUserCredentials(user)).thenReturn(userCredentials); - when(user.uid()).thenReturn("userUid"); - when(userStore.updateOrInsert(user)).thenReturn(HandleAction.Insert); + fun setUp() { + userHandler = UserHandler(userStore, userRoleHandler, userRoleCollectionCleaner) + userCredentials = UserCredentials.builder() + .uid("credentialsUid") + .username("username") + .userRoles(userRoles) + .build() + user = User.builder() + .uid("userUid") + .userCredentials(userCredentials) + .build() + + whenever(userStore.updateOrInsert(any())).thenReturn(HandleAction.Insert) } @Test - public void extend_identifiable_sync_handler_impl() { - IdentifiableHandlerImpl genericHandler = new UserHandler(userStore, userCredentialsHandler, userRoleHandler, userRoleCollectionCleaner); + fun extend_identifiable_sync_handler_impl() { + val genericHandler: IdentifiableHandlerImpl = + UserHandler(userStore, userRoleHandler, userRoleCollectionCleaner) } @Test - public void call_user_credentials_handler() { - userHandler.handle(user); - UserCredentials credentialsWithUser = userCredentials.toBuilder().user(ObjectWithUid.create(user.uid())).build(); - verify(userCredentialsHandler).handle(credentialsWithUser); + fun add_username_and_roles_from_credentials() { + userHandler.handle(user) + + verify(userRoleCollectionCleaner, times(1)).deleteNotPresent(eq(userRoles)) + verify(userRoleHandler, times(1)).handleMany(eq(userRoles)) } -} \ No newline at end of file +} From 5bdf4229bf98f518812103f3917707e640d05f57 Mon Sep 17 00:00:00 2001 From: Victor Garcia Date: Mon, 16 Jan 2023 14:36:31 +1100 Subject: [PATCH 059/201] [ANDROSDK-1584] Simplify UserCredentials object for backward compatibility --- ...ConfigurationMigrationIntegrationShould.kt | 26 ++- .../internal/ForeignKeyCleanerShould.java | 155 ------------------ .../internal/ForeignKeyCleanerShould.kt | 133 +++++++++++++++ .../user/UserModuleMockIntegrationShould.java | 6 +- ...java => LogInCallMockIntegrationShould.kt} | 87 +++++----- .../internal/UserCallMockIntegrationShould.kt | 16 +- ...java => UserRoleStoreIntegrationShould.kt} | 45 +++-- .../internal/UserStoreIntegrationShould.kt} | 48 +++--- ...sObjectRepositoryMockIntegrationShould.kt} | 45 +++-- ...rObjectRepositoryMockIntegrationShould.kt} | 43 +++-- core/src/main/assets/migrations/137.sql | 4 +- .../internal/BaseDatabaseOpenHelper.java | 2 +- .../internal/DatabaseImportExportImpl.kt | 6 +- .../core/arch/handlers/internal/Handler.kt | 2 +- .../handlers/internal/TwoWayTransformer.kt | 2 +- ...WithTransformerCollectionRepositoryImpl.kt | 2 +- .../object/ReadOnlyObjectRepository.java | 4 +- .../internal/ObjectRepositoryFactory.java | 3 +- .../ReadOnlyObjectRepositoryImpl.java | 3 + ...OnlyWithTransformerObjectRepositoryImpl.kt | 2 +- .../DatabaseConfigurationMigration.kt | 23 ++- .../core/option/internal/OptionStore.java | 2 +- .../android/core/user/UserCredentials.java | 38 ++--- ...ava => UserCredentialsObjectRepository.kt} | 51 +++--- .../core/user/UserCredentialsTableInfo.java | 66 -------- .../core/user/UserObjectRepository.java | 5 + .../android/core/user/internal/LogInCall.kt | 2 +- .../internal/UserCredentialsStoreImpl.java | 73 --------- ...ityDIModule.java => UserEntityDIModule.kt} | 53 +++--- .../android/core/user/internal/UserFields.kt | 22 ++- .../android/core/user/internal/UserHandler.kt | 2 +- .../core/user/internal/UserModuleWiper.java | 2 - ...geDIModule.java => UserPackageDIModule.kt} | 48 +++--- ...Store.java => UserRoleChildrenAppender.kt} | 23 ++- .../android/core/user/internal/UserStore.java | 2 +- ...java => UserUserCredentialsTransformer.kt} | 48 +++--- .../data/user/UserCredentialsSamples.java | 52 ------ .../android/core/data/user/UserSamples.java | 1 + .../internal/AuthorityEndpointCallShould.kt | 2 +- .../internal/LogInCallErrorCatcherShould.kt | 6 +- .../core/user/internal/LogOutCallShould.kt | 2 +- .../core/user/internal/UserCallShould.kt | 16 +- .../core/user/internal/UserHandlerShould.kt | 4 +- 43 files changed, 487 insertions(+), 690 deletions(-) delete mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/internal/ForeignKeyCleanerShould.java create mode 100644 core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/internal/ForeignKeyCleanerShould.kt rename core/src/androidTest/java/org/hisp/dhis/android/core/user/internal/{LogInCallMockIntegrationShould.java => LogInCallMockIntegrationShould.kt} (50%) rename core/src/androidTest/java/org/hisp/dhis/android/core/user/internal/{UserRoleStoreIntegrationShould.java => UserRoleStoreIntegrationShould.kt} (65%) rename core/src/{main/java/org/hisp/dhis/android/core/user/internal/UserRoleChildrenAppender.java => androidTest/java/org/hisp/dhis/android/core/user/internal/UserStoreIntegrationShould.kt} (62%) rename core/src/androidTest/java/org/hisp/dhis/android/testapp/user/{UserCredentialsObjectRepositoryMockIntegrationShould.java => UserCredentialsObjectRepositoryMockIntegrationShould.kt} (66%) rename core/src/androidTest/java/org/hisp/dhis/android/testapp/user/{UserObjectRepositoryMockIntegrationShould.java => UserObjectRepositoryMockIntegrationShould.kt} (66%) rename core/src/main/java/org/hisp/dhis/android/core/user/{UserCredentialsObjectRepository.java => UserCredentialsObjectRepository.kt} (57%) delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/user/UserCredentialsTableInfo.java delete mode 100644 core/src/main/java/org/hisp/dhis/android/core/user/internal/UserCredentialsStoreImpl.java rename core/src/main/java/org/hisp/dhis/android/core/user/internal/{UserEntityDIModule.java => UserEntityDIModule.kt} (66%) rename core/src/main/java/org/hisp/dhis/android/core/user/internal/{UserPackageDIModule.java => UserPackageDIModule.kt} (67%) rename core/src/main/java/org/hisp/dhis/android/core/user/internal/{UserCredentialsStore.java => UserRoleChildrenAppender.kt} (72%) rename core/src/main/java/org/hisp/dhis/android/core/user/internal/{UserCredentialsEntityDIModule.java => UserUserCredentialsTransformer.kt} (62%) delete mode 100644 core/src/sharedTest/java/org/hisp/dhis/android/core/data/user/UserCredentialsSamples.java diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/configuration/internal/DatabaseConfigurationMigrationIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/configuration/internal/DatabaseConfigurationMigrationIntegrationShould.kt index cc885d2cd6..1bf8592f55 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/configuration/internal/DatabaseConfigurationMigrationIntegrationShould.kt +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/configuration/internal/DatabaseConfigurationMigrationIntegrationShould.kt @@ -35,13 +35,11 @@ import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter import org.hisp.dhis.android.core.arch.db.access.internal.DatabaseAdapterFactory import org.hisp.dhis.android.core.arch.helpers.FileResourceDirectoryHelper import org.hisp.dhis.android.core.arch.storage.internal.* -import org.hisp.dhis.android.core.common.ObjectWithUid import org.hisp.dhis.android.core.configuration.internal.migration.DatabaseConfigurationInsecureStoreOld import org.hisp.dhis.android.core.configuration.internal.migration.DatabaseServerConfigurationOld import org.hisp.dhis.android.core.configuration.internal.migration.DatabaseUserConfigurationOld import org.hisp.dhis.android.core.configuration.internal.migration.DatabasesConfigurationOld import org.hisp.dhis.android.core.user.UserCredentials -import org.hisp.dhis.android.core.user.internal.UserCredentialsStoreImpl import org.hisp.dhis.android.core.utils.runner.D2JunitRunner import org.junit.Before import org.junit.Test @@ -64,10 +62,7 @@ class DatabaseConfigurationMigrationIntegrationShould { private val newName = nameGenerator.getDatabaseName(serverUrl, username, false) private val credentials = UserCredentials.builder() - .id(1L) - .uid("uid") .username(username) - .user(ObjectWithUid.create("user")) .build() private lateinit var migration: DatabaseConfigurationMigration @@ -113,8 +108,7 @@ class DatabaseConfigurationMigrationIntegrationShould { assertThat(context.databaseList().contains(newName)).isTrue() databaseAdapterFactory.createOrOpenDatabase(databaseAdapter, newName, false) - val credentialsStore = UserCredentialsStoreImpl.create(databaseAdapter) - assertThat(credentialsStore.selectFirst()).isEqualTo(credentials) + assertThat(getUsernameForOldDatabase(databaseAdapter)).isEqualTo(credentials.username()) assertThat(credentialsSecureStore.get()).isNull() } @@ -184,10 +178,24 @@ class DatabaseConfigurationMigrationIntegrationShould { } private fun setCredentialsAndServerUrl(databaseAdapter: DatabaseAdapter) { + databaseAdapter.execSQL("CREATE TABLE UserCredentials (_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT)") databaseAdapter.setForeignKeyConstraintsEnabled(false) - val credentialsStore = UserCredentialsStoreImpl.create(databaseAdapter) - credentialsStore.insert(credentials) + databaseAdapter.execSQL("INSERT INTO UserCredentials (username) VALUES ('${credentials.username()}')") val configurationStore = ConfigurationStore.create(databaseAdapter) configurationStore.insert(Configuration.forServerUrl(serverUrl)) } + + private fun getUsernameForOldDatabase(databaseAdapter: DatabaseAdapter): String? { + val cursor = databaseAdapter.rawQuery("SELECT username FROM UserCredentials") + var username: String? = null + + cursor.use { + if (cursor.count > 0) { + cursor.moveToFirst() + username = it.getString(0) + } + } + + return username + } } diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/internal/ForeignKeyCleanerShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/internal/ForeignKeyCleanerShould.java deleted file mode 100644 index 300da3fb68..0000000000 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/internal/ForeignKeyCleanerShould.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2004-2022, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.maintenance.internal; - -import androidx.test.runner.AndroidJUnit4; - -import org.hisp.dhis.android.core.arch.call.executors.internal.D2CallExecutor; -import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore; -import org.hisp.dhis.android.core.common.IdentifiableColumns; -import org.hisp.dhis.android.core.common.ObjectWithUid; -import org.hisp.dhis.android.core.maintenance.D2Error; -import org.hisp.dhis.android.core.maintenance.ForeignKeyViolation; -import org.hisp.dhis.android.core.maintenance.ForeignKeyViolationTableInfo; -import org.hisp.dhis.android.core.program.ProgramRule; -import org.hisp.dhis.android.core.program.ProgramRuleAction; -import org.hisp.dhis.android.core.program.ProgramRuleActionType; -import org.hisp.dhis.android.core.program.internal.ProgramRuleActionStore; -import org.hisp.dhis.android.core.program.internal.ProgramRuleStore; -import org.hisp.dhis.android.core.user.UserCredentials; -import org.hisp.dhis.android.core.user.UserCredentialsTableInfo; -import org.hisp.dhis.android.core.user.UserTableInfo; -import org.hisp.dhis.android.core.user.internal.UserCredentialsStoreImpl; -import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyDispatcher; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import static com.google.common.truth.Truth.assertThat; - -@RunWith(AndroidJUnit4.class) -public class ForeignKeyCleanerShould extends BaseMockIntegrationTestEmptyDispatcher { - - @Before - public void setUp() { - d2.databaseAdapter().delete(ForeignKeyViolationTableInfo.TABLE_INFO.name()); - } - - @Test - public void remove_rows_that_produce_foreign_key_errors() throws Exception { - addUserCredentialsForeignKeyViolation(); - UserCredentials userCredentials = d2.userModule().userCredentials().blockingGet(); - assertThat(userCredentials.uid()).isEqualTo("M0fCOxtkURr"); - assertThat(userCredentials.user().uid()).isEqualTo("DXyJmlo9rge"); - } - - @Test - public void add_foreign_key_violation_to_table() throws Exception { - addUserCredentialsForeignKeyViolation(); - - assertThat(d2.maintenanceModule().foreignKeyViolations().blockingCount()).isEqualTo(1); - - ForeignKeyViolation foreignKeyViolation = d2.maintenanceModule().foreignKeyViolations().one().blockingGet(); - - ForeignKeyViolation expectedViolation = ForeignKeyViolation.builder() - .toTable(UserTableInfo.TABLE_INFO.name()) - .toColumn(IdentifiableColumns.UID) - .fromTable(UserCredentialsTableInfo.TABLE_INFO.name()) - .fromColumn(UserCredentialsTableInfo.Columns.USER) - .notFoundValue("no_user_uid") - .fromObjectUid("user_credential_uid1") - .build(); - - ForeignKeyViolation violationWithoutId = foreignKeyViolation.toBuilder() - .id(null) - .created(null) - .fromObjectRow(null) - .build(); - - assertThat(expectedViolation).isEqualTo(violationWithoutId); - } - - @Test - public void delete_in_cascade_on_foreign_key_error() throws Exception { - final D2CallExecutor executor = D2CallExecutor.create(d2.databaseAdapter()); - - final String PROGRAM_RULE_UID = "program_rule_uid"; - - final ObjectWithUid program = ObjectWithUid.create("nonexisent-program"); - - executor.executeD2CallTransactionally(() -> { - ProgramRuleStore.create(d2.databaseAdapter()).insert(ProgramRule.builder() - .uid(PROGRAM_RULE_UID).name("Rule").program(program).build()); - - ProgramRuleAction programRuleAction = ProgramRuleAction.builder() - .uid("action_uid") - .name("name") - .programRuleActionType(ProgramRuleActionType.ASSIGN) - .programRule(ObjectWithUid.create(PROGRAM_RULE_UID)) - .build(); - - ProgramRuleActionStore.create(d2.databaseAdapter()).insert(programRuleAction); - - assertThat(d2.programModule().programRules().blockingCount()).isEqualTo(1); - assertThat(d2.programModule().programRuleActions().blockingCount()).isEqualTo(1); - - ForeignKeyCleaner foreignKeyCleaner = ForeignKeyCleanerImpl.create(d2.databaseAdapter()); - Integer rowsAffected = foreignKeyCleaner.cleanForeignKeyErrors(); - - assertThat(rowsAffected).isEqualTo(1); - - assertThat(d2.programModule().programRules().blockingCount()).isEqualTo(0); - assertThat(d2.programModule().programRuleActions().blockingCount()).isEqualTo(0); - - return null; - }); - - } - - private void addUserCredentialsForeignKeyViolation() throws D2Error { - final D2CallExecutor executor = D2CallExecutor.create(d2.databaseAdapter()); - - executor.executeD2CallTransactionally(() -> { - ObjectWithUid user = ObjectWithUid.create("no_user_uid"); - UserCredentials userCredentials = UserCredentials.builder() - .id(2L) - .uid("user_credential_uid1") - .user(user) - .build(); - - IdentifiableObjectStore userCredentialsStore = - UserCredentialsStoreImpl.create(d2.databaseAdapter()); - userCredentialsStore.insert(userCredentials); - - ForeignKeyCleanerImpl.create(d2.databaseAdapter()).cleanForeignKeyErrors(); - - return null; - }); - } -} \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/internal/ForeignKeyCleanerShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/internal/ForeignKeyCleanerShould.kt new file mode 100644 index 0000000000..487a7f676e --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/internal/ForeignKeyCleanerShould.kt @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2004-2022, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.maintenance.internal + +import androidx.test.runner.AndroidJUnit4 +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.db.stores.internal.IdentifiableObjectStore +import org.hisp.dhis.android.core.common.IdentifiableColumns +import org.hisp.dhis.android.core.common.ObjectWithUid +import org.hisp.dhis.android.core.maintenance.ForeignKeyViolation +import org.hisp.dhis.android.core.maintenance.ForeignKeyViolationTableInfo +import org.hisp.dhis.android.core.option.Option +import org.hisp.dhis.android.core.option.OptionSetTableInfo +import org.hisp.dhis.android.core.option.OptionTableInfo +import org.hisp.dhis.android.core.option.internal.OptionStore +import org.hisp.dhis.android.core.program.ProgramRule +import org.hisp.dhis.android.core.program.ProgramRuleAction +import org.hisp.dhis.android.core.program.ProgramRuleActionType +import org.hisp.dhis.android.core.program.internal.ProgramRuleActionStore +import org.hisp.dhis.android.core.program.internal.ProgramRuleStore +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestEmptyDispatcher +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ForeignKeyCleanerShould : BaseMockIntegrationTestEmptyDispatcher() { + + @Before + fun setUp() { + d2.databaseAdapter().delete(ForeignKeyViolationTableInfo.TABLE_INFO.name()) + } + + @Test + fun remove_rows_that_produce_foreign_key_errors() { + addOptionForeignKeyViolation() + val options = d2.optionModule().options().blockingGet() + assertThat(options).isEmpty() + } + + @Test + fun add_foreign_key_violation_to_table() { + addOptionForeignKeyViolation() + assertThat(d2.maintenanceModule().foreignKeyViolations().blockingCount()).isEqualTo(1) + val foreignKeyViolation = d2.maintenanceModule().foreignKeyViolations().one().blockingGet() + val expectedViolation = ForeignKeyViolation.builder() + .toTable(OptionSetTableInfo.TABLE_INFO.name()) + .toColumn(IdentifiableColumns.UID) + .fromTable(OptionTableInfo.TABLE_INFO.name()) + .fromColumn(OptionTableInfo.Columns.OPTION_SET) + .notFoundValue("no_option_set") + .fromObjectUid("option_uid") + .build() + val violationWithoutId = foreignKeyViolation.toBuilder() + .id(null) + .created(null) + .fromObjectRow(null) + .build() + assertThat(expectedViolation).isEqualTo(violationWithoutId) + } + + @Test + @Throws(Exception::class) + fun delete_in_cascade_on_foreign_key_error() { + val executor = D2CallExecutor.create(d2.databaseAdapter()) + val PROGRAM_RULE_UID = "program_rule_uid" + val program = ObjectWithUid.create("nonexisent-program") + executor.executeD2CallTransactionally { + ProgramRuleStore.create(d2.databaseAdapter()).insert( + ProgramRule.builder() + .uid(PROGRAM_RULE_UID).name("Rule").program(program).build() + ) + val programRuleAction = ProgramRuleAction.builder() + .uid("action_uid") + .name("name") + .programRuleActionType(ProgramRuleActionType.ASSIGN) + .programRule(ObjectWithUid.create(PROGRAM_RULE_UID)) + .build() + ProgramRuleActionStore.create(d2.databaseAdapter()).insert(programRuleAction) + assertThat(d2.programModule().programRules().blockingCount()).isEqualTo(1) + assertThat(d2.programModule().programRuleActions().blockingCount()).isEqualTo(1) + val foreignKeyCleaner = ForeignKeyCleanerImpl.create(d2.databaseAdapter()) + val rowsAffected = foreignKeyCleaner.cleanForeignKeyErrors() + assertThat(rowsAffected).isEqualTo(1) + assertThat(d2.programModule().programRules().blockingCount()).isEqualTo(0) + assertThat(d2.programModule().programRuleActions().blockingCount()).isEqualTo(0) + null + } + } + + private fun addOptionForeignKeyViolation() { + val executor = D2CallExecutor.create(d2.databaseAdapter()) + + executor.executeD2CallTransactionally { + val optionSet = ObjectWithUid.create("no_option_set") + val option = Option.builder() + .uid("option_uid") + .optionSet(optionSet) + .build() + val optionStore: IdentifiableObjectStore