diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/ForeignKeyCleanerShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/ForeignKeyCleanerShould.java index 46a2be6868..d4f15ec897 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/ForeignKeyCleanerShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/maintenance/ForeignKeyCleanerShould.java @@ -39,7 +39,7 @@ import org.hisp.dhis.android.core.common.D2CallExecutor; import org.hisp.dhis.android.core.common.D2Factory; import org.hisp.dhis.android.core.common.IdentifiableObjectStore; -import org.hisp.dhis.android.core.utils.integration.real.BaseRealIntegrationTest; +import org.hisp.dhis.android.core.common.ObjectWithUid; import org.hisp.dhis.android.core.data.server.Dhis2MockServer; import org.hisp.dhis.android.core.program.Program; import org.hisp.dhis.android.core.program.ProgramRule; @@ -54,6 +54,7 @@ import org.hisp.dhis.android.core.user.UserCredentialsStoreImpl; import org.hisp.dhis.android.core.user.UserCredentialsTableInfo; import org.hisp.dhis.android.core.user.UserTableInfo; +import org.hisp.dhis.android.core.utils.integration.real.BaseRealIntegrationTest; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -199,7 +200,7 @@ public void cascade_deletion_on_foreign_key_error() throws Exception { .uid("action_uid") .name("name") .programRuleActionType(ProgramRuleActionType.ASSIGN) - .programRule(ProgramRule.builder().uid(PROGRAM_RULE_UID).build()) + .programRule(ObjectWithUid.create(PROGRAM_RULE_UID)) .build(); ProgramRuleActionStore.create(d2.databaseAdapter()).insert(programRuleAction); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/period/PeriodMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/period/PeriodMockIntegrationShould.java index 6366de4ecc..b414f5faed 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/period/PeriodMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/period/PeriodMockIntegrationShould.java @@ -45,7 +45,7 @@ public class PeriodMockIntegrationShould extends BaseMockIntegrationTestFullDisp @Test public void get_period_passing_period_type_and_a_date() throws ParseException { Period period = d2.periodModule().periodHelper.getPeriod(PeriodType.BiWeekly, - BaseIdentifiableObject.DATE_FORMAT.parse("2018-12-24T12:24:25.319")); - assertThat(period.periodId(), is("2018BiW26")); + BaseIdentifiableObject.DATE_FORMAT.parse("2019-06-24T12:24:25.319")); + assertThat(period.periodId(), is("2019BiW13")); } } \ No newline at end of file diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngineIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngineIntegrationShould.java index 600ab32dc0..1b5cb3530a 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngineIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngineIntegrationShould.java @@ -201,6 +201,20 @@ public void evaluate_addition_two_dataelement() { assertThat(result).isEqualTo("15"); } + @Test + public void evaluate_division_two_dataelement() { + createEnrollment(null, null); + createEvent(event1, programStage1, null); + insertTrackedEntityDataValue(event1, dataElement1, "3"); + insertTrackedEntityDataValue(event1, dataElement2, "5"); + + setProgramIndicatorExpressionAsAverage(de(programStage1,dataElement1) + " / " + de(programStage1,dataElement2)); + + String result = programIndicatorEngine.getProgramIndicatorValue(enrollmentUid, event1, programIndicatorUid); + + assertThat(result).isEqualTo("0.6"); + } + @Test public void evaluate_last_value_indicators_different_dates() { createEnrollment(null, null); diff --git a/core/src/main/java/org/hisp/dhis/android/core/data/api/Field.java b/core/src/main/java/org/hisp/dhis/android/core/data/api/Field.java index 5bace41223..390e02450f 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/data/api/Field.java +++ b/core/src/main/java/org/hisp/dhis/android/core/data/api/Field.java @@ -47,6 +47,10 @@ public Filter gt(String value) { return SingleValueFilter.gt(this, value); } + public Filter like(String value) { + return SingleValueFilter.like(this, value); + } + public Filter in(Collection values) { return InFilter.create(this, values); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/data/api/SingleValueFilter.java b/core/src/main/java/org/hisp/dhis/android/core/data/api/SingleValueFilter.java index 5e61bc0ca4..b0be3374c1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/data/api/SingleValueFilter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/data/api/SingleValueFilter.java @@ -58,6 +58,10 @@ public static Filter eq(@NonNull Field field, @Nullable Strin return create(field, "eq", value); } + public static Filter like(@NonNull Field field, @Nullable String value) { + return create(field, "like", value); + } + @Override public String generateString() { StringBuilder builder = new StringBuilder(); diff --git a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitCallFactory.java b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitCallFactory.java index 5d9d6ff34a..fa3bc92504 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitCallFactory.java +++ b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitCallFactory.java @@ -136,16 +136,27 @@ private Set downloadSearchOrgunits(final User user, private Set downloadOrgunits(Set orguntis) throws D2Error { Set organisationUnits = new HashSet<>(); for (String uid : orguntis) { - organisationUnits.addAll(apiCallExecutor.executePayloadCall( - getOrganisationUnitAndDescendants(uid))); + OrganisationUnitQuery.Builder queryBuilder = OrganisationUnitQuery.builder().orgUnit(uid); + + List pageOrgunits; + OrganisationUnitQuery pageQuery; + do { + pageQuery = queryBuilder.build(); + pageOrgunits = apiCallExecutor.executePayloadCall(getOrganisationUnitAndDescendants(pageQuery)); + organisationUnits.addAll(pageOrgunits); + + queryBuilder.page(pageQuery.page() + 1); + } + while (pageOrgunits.size() == pageQuery.pageSize()); } return organisationUnits; } - private retrofit2.Call> getOrganisationUnitAndDescendants(@NonNull String uid) { - return organisationUnitService.getOrganisationUnitWithDescendants( - uid, OrganisationUnitFields.allFields, true, false); + private retrofit2.Call> getOrganisationUnitAndDescendants(OrganisationUnitQuery query) { + return organisationUnitService.getOrganisationUnits( + OrganisationUnitFields.allFields, OrganisationUnitFields.path.like(query.orgUnit()), + query.paging(), query.pageSize(), query.page()); } private Set getLinkedPrograms(Set capture, @NonNull Set programUids) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitFields.java b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitFields.java index 543d3b1a26..42ac2f9f42 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitFields.java +++ b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitFields.java @@ -48,7 +48,7 @@ public final class OrganisationUnitFields { static final Field uid = fh.uid(); private static final Field displayName = fh.displayName(); - private static final Field path = Field.create(PATH); + static final Field path = Field.create(PATH); private static final Field openingDate = Field.create(OPENING_DATE); private static final Field closedDate = Field.create(CLOSED_DATE); diff --git a/core/src/main/java/org/hisp/dhis/android/core/data/database/ProgramRuleWithUidColumnAdapter.java b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitQuery.java similarity index 66% rename from core/src/main/java/org/hisp/dhis/android/core/data/database/ProgramRuleWithUidColumnAdapter.java rename to core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitQuery.java index 79fc66838e..80f655a11e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/data/database/ProgramRuleWithUidColumnAdapter.java +++ b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitQuery.java @@ -26,14 +26,33 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.hisp.dhis.android.core.data.database; +package org.hisp.dhis.android.core.organisationunit; -import org.hisp.dhis.android.core.program.ProgramRule; +import androidx.annotation.Nullable; -public class ProgramRuleWithUidColumnAdapter extends IdentifiableObjectColumnAdapter { +import com.google.auto.value.AutoValue; - @Override - protected ProgramRule build(String uid) { - return ProgramRule.builder().uid(uid).build(); +import org.hisp.dhis.android.core.common.BaseQuery; + +@AutoValue +abstract class OrganisationUnitQuery extends BaseQuery { + + private static int DEFAULT_PAGE_SIZE = 500; + + @Nullable + public abstract String orgUnit(); + + public static Builder builder() { + return new AutoValue_OrganisationUnitQuery.Builder() + .page(1) + .pageSize(DEFAULT_PAGE_SIZE) + .paging(true); + } + + @AutoValue.Builder + public abstract static class Builder extends BaseQuery.Builder { + public abstract Builder orgUnit(String orgUnit); + + public abstract OrganisationUnitQuery build(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitService.java b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitService.java index db87ba92ac..254950c0d3 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitService.java +++ b/core/src/main/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitService.java @@ -35,23 +35,31 @@ import retrofit2.Call; import retrofit2.http.GET; -import retrofit2.http.Path; import retrofit2.http.Query; interface OrganisationUnitService { + String ORGANISATION_UNITS = "organisationUnits"; - @GET("organisationUnits/{uid}") - Call> getOrganisationUnitWithDescendants( - @Path("uid") String organisationUnitUid, - @Query("fields") @Which Fields fields, - @Query("includeDescendants") Boolean descendants, - @Query("paging") Boolean paging + String UID = "uid"; + String FIELDS = "fields"; + String FILTER = "filter"; + String PAGING = "paging"; + String PAGE = "page"; + String PAGE_SIZE = "pageSize"; + + @GET(ORGANISATION_UNITS) + Call> getOrganisationUnits( + @Query(FIELDS) @Which Fields fields, + @Query(FILTER) @Where Filter filter, + @Query(PAGING) Boolean paging, + @Query(PAGE_SIZE) Integer pageSize, + @Query(PAGE) Integer page ); - @GET("organisationUnits") + @GET(ORGANISATION_UNITS) Call> getSearchOrganisationUnits( - @Query("fields") @Which Fields fields, - @Query("filter") @Where Filter filter, - @Query("paging") Boolean paging + @Query(FIELDS) @Which Fields fields, + @Query(FILTER) @Where Filter filter, + @Query(PAGING) Boolean paging ); } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramIndicator.java b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramIndicator.java index d73c2fa42e..425507d8dc 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramIndicator.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramIndicator.java @@ -49,7 +49,7 @@ import java.util.List; @AutoValue -@JsonDeserialize(builder = AutoValue_ProgramIndicator.Builder.class) +@JsonDeserialize(builder = $$AutoValue_ProgramIndicator.Builder.class) public abstract class ProgramIndicator extends BaseNameableObject implements Model { @Nullable @@ -94,7 +94,8 @@ public static ProgramIndicator create(Cursor cursor) { public abstract Builder toBuilder(); public static Builder builder() { - return new AutoValue_ProgramIndicator.Builder(); + return new $$AutoValue_ProgramIndicator.Builder() + .aggregationType(AggregationType.NONE); } @AutoValue.Builder diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleAction.java b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleAction.java index e5dcaf21a7..1860b363fa 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleAction.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleAction.java @@ -29,7 +29,6 @@ package org.hisp.dhis.android.core.program; import android.database.Cursor; -import androidx.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -43,13 +42,14 @@ import org.hisp.dhis.android.core.data.database.DataElementWithUidColumnAdapter; import org.hisp.dhis.android.core.data.database.ObjectWithUidColumnAdapter; import org.hisp.dhis.android.core.data.database.ProgramIndicatorWithUidColumnAdapter; -import org.hisp.dhis.android.core.data.database.ProgramRuleWithUidColumnAdapter; import org.hisp.dhis.android.core.data.database.ProgramStageSectionWithUidColumnAdapter; import org.hisp.dhis.android.core.data.database.ProgramStageWithUidColumnAdapter; import org.hisp.dhis.android.core.data.database.TrackedEntityAttributeWithUidColumnAdapter; import org.hisp.dhis.android.core.dataelement.DataElement; import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute; +import androidx.annotation.Nullable; + @AutoValue @JsonDeserialize(builder = AutoValue_ProgramRuleAction.Builder.class) public abstract class ProgramRuleAction extends BaseIdentifiableObject implements Model { @@ -98,8 +98,8 @@ public abstract class ProgramRuleAction extends BaseIdentifiableObject implement @Nullable @JsonProperty() - @ColumnAdapter(ProgramRuleWithUidColumnAdapter.class) - public abstract ProgramRule programRule(); + @ColumnAdapter(ObjectWithUidColumnAdapter.class) + public abstract ObjectWithUid programRule(); @Nullable @JsonProperty() @@ -144,7 +144,7 @@ public abstract static class Builder extends BaseIdentifiableObject.Builder store(DatabaseAdapter database @Provides @Reusable - SyncHandler handler(IdentifiableObjectStore store) { + SyncHandlerWithTransformer handler(IdentifiableObjectStore store) { return new IdentifiableSyncHandlerImpl<>(store); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleActionFields.java b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleActionFields.java index 4cef9644c8..611ee7d330 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleActionFields.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleActionFields.java @@ -59,7 +59,6 @@ final class ProgramRuleActionFields { fh.field(PROGRAM_RULE_ACTION_TYPE), fh.nestedFieldWithUid(PROGRAM_STAGE), fh.nestedFieldWithUid(DATA_ELEMENT), - fh.nestedFieldWithUid(PROGRAM_RULE), fh.nestedFieldWithUid(OPTION), fh.nestedFieldWithUid(OPTION_GROUP) ).build(); diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleHandler.java b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleHandler.java index 15ddc5a926..2805e8ad67 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleHandler.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramRuleHandler.java @@ -28,9 +28,10 @@ package org.hisp.dhis.android.core.program; import org.hisp.dhis.android.core.arch.handlers.IdentifiableSyncHandlerImpl; -import org.hisp.dhis.android.core.arch.handlers.SyncHandler; +import org.hisp.dhis.android.core.arch.handlers.SyncHandlerWithTransformer; import org.hisp.dhis.android.core.common.HandleAction; import org.hisp.dhis.android.core.common.IdentifiableObjectStore; +import org.hisp.dhis.android.core.common.ObjectWithUid; import org.hisp.dhis.android.core.common.OrphanCleaner; import javax.inject.Inject; @@ -39,12 +40,12 @@ @Reusable final class ProgramRuleHandler extends IdentifiableSyncHandlerImpl { - private final SyncHandler programRuleActionHandler; + private final SyncHandlerWithTransformer programRuleActionHandler; private final OrphanCleaner programRuleActionCleaner; @Inject ProgramRuleHandler(IdentifiableObjectStore programRuleStore, - SyncHandler programRuleActionHandler, + SyncHandlerWithTransformer programRuleActionHandler, OrphanCleaner programRuleActionCleaner) { super(programRuleStore); this.programRuleActionHandler = programRuleActionHandler; @@ -53,7 +54,8 @@ final class ProgramRuleHandler extends IdentifiableSyncHandlerImpl @Override protected void afterObjectHandled(ProgramRule programRule, HandleAction handleAction) { - programRuleActionHandler.handleMany(programRule.programRuleActions()); + programRuleActionHandler.handleMany(programRule.programRuleActions(), + pra -> pra.toBuilder().programRule(ObjectWithUid.create(programRule.uid())).build()); if (handleAction == HandleAction.Update) { programRuleActionCleaner.deleteOrphan(programRule, programRule.programRuleActions()); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceWithLimitCallFactory.java b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceWithLimitCallFactory.java index cbcecdfbe7..8dced64cff 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceWithLimitCallFactory.java +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/TrackedEntityInstanceWithLimitCallFactory.java @@ -33,16 +33,17 @@ import org.hisp.dhis.android.core.arch.call.D2CallWithProgressImpl; import org.hisp.dhis.android.core.arch.call.D2Progress; import org.hisp.dhis.android.core.arch.call.D2ProgressManager; +import org.hisp.dhis.android.core.arch.db.WhereClauseBuilder; import org.hisp.dhis.android.core.arch.repositories.collection.ReadOnlyWithDownloadObjectRepository; import org.hisp.dhis.android.core.common.LinkModelStore; import org.hisp.dhis.android.core.data.api.OuMode; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitProgramLink; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitProgramLinkTableInfo; import org.hisp.dhis.android.core.resource.Resource; import org.hisp.dhis.android.core.resource.ResourceHandler; import org.hisp.dhis.android.core.systeminfo.DHISVersionManager; import org.hisp.dhis.android.core.systeminfo.SystemInfo; -import org.hisp.dhis.android.core.user.UserOrganisationUnitLink; import org.hisp.dhis.android.core.user.UserOrganisationUnitLinkStore; import org.hisp.dhis.android.core.utils.services.ApiPagingEngine; import org.hisp.dhis.android.core.utils.services.Paging; @@ -165,35 +166,38 @@ private List getTeiQueryBuilders(boolean limitByOrgUnit, boole List builders = new ArrayList<>(); if (limitByOrgUnit) { + List captureOrgUnitUids = getCaptureOrgUnitUids(); if (limitByProgram) { - for (OrganisationUnitProgramLink link : organisationUnitProgramLinkStore.selectAll()) { + for (OrganisationUnitProgramLink link : + getOrganisationUnitProgramLinksByOrgunitUids(captureOrgUnitUids)) { builders.add(getTeiBuilderForOrgUnit(lastUpdated, link.organisationUnit()).program(link.program())); } } else { - for (String orgUnitUid: getOrgUnitUids()) { + for (String orgUnitUid : captureOrgUnitUids) { builders.add(getTeiBuilderForOrgUnit(lastUpdated, orgUnitUid)); } } } else { + List rootCaptureOrgUnitUids = getRootCaptureOrgUnitUids(); if (limitByProgram) { Set programs = new HashSet<>(); for (OrganisationUnitProgramLink link : organisationUnitProgramLinkStore.selectAll()) { programs.add(link.program()); } for (String program : programs) { - builders.add(getTeiBuilderForRoot(lastUpdated).program(program)); + builders.add(getTeiBuilderForRootOrgunits(lastUpdated, rootCaptureOrgUnitUids).program(program)); } } else { - builders.add(getTeiBuilderForRoot(lastUpdated)); + builders.add(getTeiBuilderForRootOrgunits(lastUpdated, rootCaptureOrgUnitUids)); } } return builders; } - private TeiQuery.Builder getTeiBuilderForRoot(String lastUpdated) { + private TeiQuery.Builder getTeiBuilderForRootOrgunits(String lastUpdated, List rootOrgunitUids) { return TeiQuery.builder() .lastUpdatedStartDate(lastUpdated) - .orgUnits(userOrganisationUnitLinkStore.queryRootCaptureOrganisationUnitUids()) + .orgUnits(rootOrgunitUids) .ouMode(OuMode.DESCENDANTS); } @@ -236,19 +240,21 @@ private List limitTeisForPage(List } } - private Set getOrgUnitUids() { - List userOrganisationUnitLinks = userOrganisationUnitLinkStore.selectAll(); - - Set organisationUnitUids = new HashSet<>(); + private List getRootCaptureOrgUnitUids() { + return userOrganisationUnitLinkStore.queryRootCaptureOrganisationUnitUids(); + } - for (UserOrganisationUnitLink userOrganisationUnitLink : userOrganisationUnitLinks) { - if (userOrganisationUnitLink.organisationUnitScope().equals( - OrganisationUnit.Scope.SCOPE_DATA_CAPTURE.name())) { - organisationUnitUids.add(userOrganisationUnitLink.organisationUnit()); - } - } + private List getCaptureOrgUnitUids() { + return userOrganisationUnitLinkStore + .queryOrganisationUnitUidsByScope(OrganisationUnit.Scope.SCOPE_DATA_CAPTURE); + } - return organisationUnitUids; + private List getOrganisationUnitProgramLinksByOrgunitUids(List uids) { + return organisationUnitProgramLinkStore.selectWhere( + new WhereClauseBuilder().appendInKeyStringValues( + OrganisationUnitProgramLinkTableInfo.Columns.ORGANISATION_UNIT, + uids + ).build()); } private Observable updateResource(D2ProgressManager progressManager, BooleanWrapper allOkay) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/UserOrganisationUnitLinkStore.java b/core/src/main/java/org/hisp/dhis/android/core/user/UserOrganisationUnitLinkStore.java index 0834cec29d..b2e28252e1 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/UserOrganisationUnitLinkStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/user/UserOrganisationUnitLinkStore.java @@ -29,10 +29,13 @@ package org.hisp.dhis.android.core.user; import org.hisp.dhis.android.core.common.LinkModelStore; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; import java.util.List; public interface UserOrganisationUnitLinkStore extends LinkModelStore { List queryRootCaptureOrganisationUnitUids() throws RuntimeException; + + List queryOrganisationUnitUidsByScope(OrganisationUnit.Scope scope); } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/UserOrganisationUnitLinkStoreImpl.java b/core/src/main/java/org/hisp/dhis/android/core/user/UserOrganisationUnitLinkStoreImpl.java index fda0cdeddf..31d748f64b 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/UserOrganisationUnitLinkStoreImpl.java +++ b/core/src/main/java/org/hisp/dhis/android/core/user/UserOrganisationUnitLinkStoreImpl.java @@ -30,6 +30,7 @@ import android.database.sqlite.SQLiteStatement; +import org.hisp.dhis.android.core.arch.db.WhereClauseBuilder; import org.hisp.dhis.android.core.arch.db.binders.StatementBinder; import org.hisp.dhis.android.core.common.LinkModelStoreImpl; import org.hisp.dhis.android.core.common.SQLStatementBuilder; @@ -77,4 +78,13 @@ public List queryRootCaptureOrganisationUnitUids() throws RuntimeExcepti + Columns.ORGANISATION_UNIT_SCOPE + " = '" + OrganisationUnit.Scope.SCOPE_DATA_CAPTURE + "'"); } + + @Override + public List queryOrganisationUnitUidsByScope(OrganisationUnit.Scope scope) { + return selectStringColumnsWhereClause(Columns.ORGANISATION_UNIT, + new WhereClauseBuilder().appendKeyStringValue( + Columns.ORGANISATION_UNIT_SCOPE, + scope.name() + ).build()); + } } \ No newline at end of file diff --git a/core/src/main/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngine.java b/core/src/main/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngine.java index 867be96618..017c902396 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngine.java +++ b/core/src/main/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngine.java @@ -353,8 +353,8 @@ private TrackedEntityDataValue evaluateDataElementInStage(String deId, if (candidates.isEmpty()) { return null; - } else if (aggregationType.equals(AggregationType.LAST) || - aggregationType.equals(AggregationType.LAST_AVERAGE_ORG_UNIT)) { + } else if (AggregationType.LAST.equals(aggregationType) || + AggregationType.LAST_AVERAGE_ORG_UNIT.equals(aggregationType)) { return candidates.get(candidates.size() - 1); } else { return candidates.get(0); @@ -362,18 +362,24 @@ private TrackedEntityDataValue evaluateDataElementInStage(String deId, } private String formatDataValue(TrackedEntityDataValue dataValue) { - if (dataElementStore.selectByUid(dataValue.dataElement()) - .valueType() == ValueType.BOOLEAN) { - if (dataValue.value().equals("true")) { - return "1"; - } else { - return "0"; + + DataElement dataElement = dataElementStore.selectByUid(dataValue.dataElement()); + + if (dataElement.valueType() == ValueType.BOOLEAN) { + return dataValue.value().equals("true") ? "1" : "0"; + } + + if (MathUtils.isNumeric(dataValue.value())) { + if (dataValue.value().endsWith(".")) { + return (dataValue.value() + "0"); + } + + if (!dataValue.value().contains(".")) { + return (dataValue.value() + ".0"); } - } else if (dataValue.value().endsWith(".")) { - return (dataValue.value() + "0"); - } else { - return dataValue.value(); } + + return dataValue.value(); } private static boolean isZeroOrPositive(String value) { diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/program/ProgramRuleActionSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/program/ProgramRuleActionSamples.java index b269062ea7..c30cd792ce 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/program/ProgramRuleActionSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/program/ProgramRuleActionSamples.java @@ -31,7 +31,6 @@ import org.hisp.dhis.android.core.common.ObjectWithUid; import org.hisp.dhis.android.core.dataelement.DataElement; import org.hisp.dhis.android.core.program.ProgramIndicator; -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.ProgramStage; @@ -57,7 +56,7 @@ public static ProgramRuleAction getProgramRuleAction() { .programRuleActionType(ProgramRuleActionType.ASSIGN) .programStage(ProgramStage.builder().uid("ps").build()) .dataElement(DataElement.builder().uid("de").build()) - .programRule(ProgramRule.builder().uid("pr").build()) + .programRule(ObjectWithUid.create("pr")) .option(ObjectWithUid.create("option")) .optionGroup(ObjectWithUid.create("option_group")) .build(); diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/server/Dhis2MockServer.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/server/Dhis2MockServer.java index afc4b9d321..b7a8659493 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/server/Dhis2MockServer.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/server/Dhis2MockServer.java @@ -153,7 +153,7 @@ public MockResponse dispatch(RecordedRequest request) { return createMockResponse(CATEGORY_COMBOS_JSON); } else if (path.startsWith("/api/categories?")) { return createMockResponse(CATEGORIES_JSON); - } else if (path.startsWith("/api/organisationUnits/")) { + } else if (path.startsWith("/api/organisationUnits?")) { return createMockResponse(ORGANISATION_UNITS_JSON); } else if (path.startsWith("/api/organisationUnitLevels?")) { return createMockResponse(ORGANISATION_UNIT_LEVELS_JSON); diff --git a/core/src/sharedTest/resources/program/program_rules.json b/core/src/sharedTest/resources/program/program_rules.json index 593d4408b3..075c241a64 100644 --- a/core/src/sharedTest/resources/program/program_rules.json +++ b/core/src/sharedTest/resources/program/program_rules.json @@ -19,9 +19,6 @@ "content": "The hemoglobin value cannot be above 99", "dataElement": { "id": "vANAXwtLwcT" - }, - "programRule": { - "id": "dahuKlP7jR2" } } ] @@ -62,9 +59,6 @@ "dataElement": { "id": "Ok9OQpitjQr" }, - "programRule": { - "id": "xOe5qCzRS0Y" - }, "option": { "id": "egT1YqFWsVk" }, @@ -95,9 +89,6 @@ "content": "Hemoglobin value lower than normal", "dataElement": { "id": "vANAXwtLwcT" - }, - "programRule": { - "id": "GC4gpdoSD4r" } } ] diff --git a/core/src/test/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitCallUnitShould.java b/core/src/test/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitCallUnitShould.java index 34720ae5f2..68f4a90eac 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitCallUnitShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/organisationunit/OrganisationUnitCallUnitShould.java @@ -33,9 +33,9 @@ import org.hisp.dhis.android.core.common.Payload; import org.hisp.dhis.android.core.common.Transformer; import org.hisp.dhis.android.core.data.api.Fields; +import org.hisp.dhis.android.core.data.api.Filter; import org.hisp.dhis.android.core.data.database.DatabaseAdapter; import org.hisp.dhis.android.core.dataset.DataSet; -import org.hisp.dhis.android.core.maintenance.D2Error; import org.hisp.dhis.android.core.program.Program; import org.hisp.dhis.android.core.resource.Resource; import org.hisp.dhis.android.core.resource.ResourceHandler; @@ -59,8 +59,8 @@ import java.util.concurrent.Callable; import static org.assertj.core.api.Java6Assertions.assertThat; -import static org.mockito.Matchers.anyCollectionOf; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyCollectionOf; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -82,18 +82,21 @@ public class OrganisationUnitCallUnitShould { private OrganisationUnitService organisationUnitService; //Captors for the organisationUnitService arguments: - @Captor - private ArgumentCaptor uidCaptor; - @Captor private ArgumentCaptor> fieldsCaptor; @Captor - private ArgumentCaptor descendantsCaptor; + private ArgumentCaptor> filtersCaptor; @Captor private ArgumentCaptor pagingCaptor; + @Captor + private ArgumentCaptor pageCaptor; + + @Captor + private ArgumentCaptor pageSizeCaptor; + @Mock private OrganisationUnit organisationUnit; @@ -115,9 +118,6 @@ public class OrganisationUnitCallUnitShould { @Mock private GenericCallData genericCallData; - @Mock - private D2Error d2Error; - @Mock private OrganisationUnitHandler organisationUnitHandler; @@ -187,8 +187,9 @@ public void setUp() throws IOException { organisationUnits = Collections.singletonList(organisationUnit); when(user.organisationUnits()).thenReturn(new ArrayList<>(organisationUnits)); - when(organisationUnitService.getOrganisationUnitWithDescendants( - uidCaptor.capture(), fieldsCaptor.capture(), descendantsCaptor.capture(), pagingCaptor.capture() + when(organisationUnitService.getOrganisationUnits( + fieldsCaptor.capture(), filtersCaptor.capture(), pagingCaptor.capture(), + pageSizeCaptor.capture(), pageCaptor.capture() )).thenReturn(retrofitCall); when(genericCallData.resourceHandler()).thenReturn(resourceHandler); @@ -205,10 +206,11 @@ public void invoke_server_with_correct_parameters() throws Exception { organisationUnitCall.call(); - assertThat(uidCaptor.getValue()).isEqualTo(organisationUnit.uid()); assertThat(fieldsCaptor.getValue()).isEqualTo(OrganisationUnitFields.allFields); - assertThat(descendantsCaptor.getValue()).isTrue(); - assertThat(pagingCaptor.getValue()).isFalse(); + assertThat(filtersCaptor.getValue().operator()).isEqualTo("like"); + assertThat(filtersCaptor.getValue().field()).isEqualTo(OrganisationUnitFields.path); + assertThat(pagingCaptor.getValue()).isTrue(); + assertThat(pageCaptor.getValue()).isEqualTo(1); } @Test diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/ProgramRuleHandlerShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/ProgramRuleHandlerShould.java index 6d62d3d983..35c4343cd7 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/program/ProgramRuleHandlerShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/program/ProgramRuleHandlerShould.java @@ -28,10 +28,11 @@ package org.hisp.dhis.android.core.program; import org.hisp.dhis.android.core.arch.handlers.IdentifiableSyncHandlerImpl; -import org.hisp.dhis.android.core.arch.handlers.SyncHandler; +import org.hisp.dhis.android.core.arch.handlers.SyncHandlerWithTransformer; import org.hisp.dhis.android.core.common.HandleAction; import org.hisp.dhis.android.core.common.IdentifiableObjectStore; import org.hisp.dhis.android.core.common.OrphanCleaner; +import org.hisp.dhis.android.core.common.Transformer; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,6 +42,8 @@ import java.util.List; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -51,7 +54,7 @@ public class ProgramRuleHandlerShould { private IdentifiableObjectStore programRuleStore; @Mock - private SyncHandler programRuleActionHandler; + private SyncHandlerWithTransformer programRuleActionHandler; @Mock private ProgramRule programRule; @@ -83,7 +86,7 @@ public void extend_identifiable_sync_handler_impl() { @Test public void call_program_rule_action_handler() { programRuleHandler.handle(programRule); - verify(programRuleActionHandler).handleMany(programRuleActions); + verify(programRuleActionHandler).handleMany(same(programRuleActions), any(Transformer.class)); } @Test diff --git a/core/src/test/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngineShould.java b/core/src/test/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngineShould.java index 48f00d10e5..1deaa29923 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngineShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/utils/services/ProgramIndicatorEngineShould.java @@ -222,7 +222,7 @@ public void setUp() throws Exception { } @Test - public void parse_static_value() throws Exception { + public void parse_static_value() { when(programIndicator.expression()).thenReturn("5 * 10"); String result = programIndicatorEngine.parseIndicatorExpression(enrollmentUid, null, @@ -232,7 +232,7 @@ public void parse_static_value() throws Exception { } @Test - public void parse_one_dataelement() throws Exception { + public void parse_one_dataelement() { when(programIndicator.expression()).thenReturn(de(programStageUid1, dataElementUid1)); when(value1.value()).thenReturn("3.5"); @@ -243,7 +243,7 @@ public void parse_one_dataelement() throws Exception { } @Test - public void parse_one_text_dataelement() throws Exception { + public void parse_one_text_dataelement() { when(programIndicator.expression()).thenReturn(de(programStageUid1, dataElementUid1)); when(value1.value()).thenReturn("text data-value"); @@ -254,7 +254,7 @@ public void parse_one_text_dataelement() throws Exception { } @Test - public void parse_operation_two_dataelements() throws Exception { + public void parse_operation_two_dataelements() { when(programIndicator.expression()).thenReturn( de(programStageUid1, dataElementUid1) + " + " + de(programStageUid1, dataElementUid2)); @@ -263,11 +263,11 @@ public void parse_operation_two_dataelements() throws Exception { String result = programIndicatorEngine.parseIndicatorExpression(null, eventUid1, programIndicatorUid); - assertThat(result).isEqualTo("3.5 + 2"); + assertThat(result).isEqualTo("3.5 + 2.0"); } @Test - public void parse_operation_two_mixed_dataelements() throws Exception { + public void parse_operation_two_mixed_dataelements() { when(programIndicator.expression()).thenReturn( de(programStageUid1, dataElementUid1) + " + " + de(programStageUid1, dataElementUid2)); @@ -280,7 +280,7 @@ public void parse_operation_two_mixed_dataelements() throws Exception { } @Test - public void parse_operation_with_parenthesis() throws Exception { + public void parse_operation_with_parenthesis() { when(programIndicator.expression()).thenReturn( "(" + de(programStageUid1, dataElementUid1) + " + " + de(programStageUid1, dataElementUid2) + ") / " + de(programStageUid1, dataElementUid3)); @@ -291,11 +291,11 @@ public void parse_operation_with_parenthesis() throws Exception { String result = programIndicatorEngine.parseIndicatorExpression(null, eventUid1, programIndicatorUid); - assertThat(result).isEqualTo("(2.5 + 2) / 1.5"); + assertThat(result).isEqualTo("(2.5 + 2.0) / 1.5"); } @Test - public void parse_dataelement_and_constant() throws Exception { + public void parse_dataelement_and_constant() { when(programIndicator.expression()).thenReturn( de(programStageUid1, dataElementUid1) + " + " + cons(constantUid1)); @@ -332,7 +332,7 @@ public void parse_incident_date_variable() throws Exception { } @Test - public void parse_enrollment_status_variable() throws Exception { + public void parse_enrollment_status_variable() { when(programIndicator.expression()).thenReturn(var("enrollment_status")); when(enrollment.status()).thenReturn(EnrollmentStatus.ACTIVE); @@ -367,7 +367,7 @@ public void parse_due_date_variable() throws Exception { } @Test - public void parse_event_count_variable() throws Exception { + public void parse_event_count_variable() { when(programIndicator.expression()).thenReturn(var("event_count")); String result = programIndicatorEngine.parseIndicatorExpression(enrollmentUid, eventUid1, programIndicatorUid); @@ -376,7 +376,7 @@ public void parse_event_count_variable() throws Exception { } @Test - public void parse_value_count_variable() throws Exception { + public void parse_value_count_variable() { when(programIndicator.expression()).thenReturn( "(" + de(programStageUid1, dataElementUid1) + " + " + de(programStageUid1, dataElementUid2) + ") / " + var("value_count")); @@ -386,7 +386,7 @@ public void parse_value_count_variable() throws Exception { String result = programIndicatorEngine.parseIndicatorExpression(null, eventUid1, programIndicatorUid); - assertThat(result).isEqualTo("(3.5 + 2) / 2"); + assertThat(result).isEqualTo("(3.5 + 2.0) / 2"); } @Test @@ -426,9 +426,9 @@ public void parse_values_from_different_program_stage_instances() { String resultWithEvent2 = programIndicatorEngine.parseIndicatorExpression(enrollmentUid, eventUid2_1, programIndicatorUid); - assertThat(resultWithoutEvent).isEqualTo("3.5 + 2"); - assertThat(resultWithEvent1).isEqualTo("3.5 + 2"); - assertThat(resultWithEvent2).isEqualTo("3.5 + 2"); + assertThat(resultWithoutEvent).isEqualTo("3.5 + 2.0"); + assertThat(resultWithEvent1).isEqualTo("3.5 + 2.0"); + assertThat(resultWithEvent2).isEqualTo("3.5 + 2.0"); } @Test @@ -452,7 +452,7 @@ public void ignore_values_from_non_existing_events() { } @Test - public void parse_operation_with_zero_values() throws Exception { + public void parse_operation_with_zero_values() { when(programIndicator.expression()).thenReturn( de(programStageUid2, dataElementUid2) + " * 10"); @@ -467,7 +467,7 @@ public void parse_operation_with_zero_values() throws Exception { } @Test - public void parse_last_aggregation_type() throws Exception { + public void parse_last_aggregation_type() { when(programIndicator.expression()).thenReturn( de(programStageUid2, dataElementUid4) + " * 10"); @@ -478,15 +478,29 @@ public void parse_last_aggregation_type() throws Exception { String sumResult = programIndicatorEngine.parseIndicatorExpression(enrollmentUid, null, programIndicatorUid); - assertThat(sumResult).isEqualTo("2 * 10"); + assertThat(sumResult).isEqualTo("2.0 * 10"); when(programIndicator.aggregationType()).thenReturn(AggregationType.LAST); String lastResult = programIndicatorEngine.parseIndicatorExpression(enrollmentUid, null, programIndicatorUid); - assertThat(lastResult).isEqualTo("4 * 10"); + assertThat(lastResult).isEqualTo("4.0 * 10"); } + @Test + public void default_to_none_on_null_aggregation_type() { + when(programIndicator.expression()).thenReturn(de(programStageUid1, dataElementUid1)); + + when(value1.value()).thenReturn("10"); + + when(programIndicator.aggregationType()).thenReturn(null); + String sumResult = programIndicatorEngine.parseIndicatorExpression(enrollmentUid, null, + programIndicatorUid); + + assertThat(sumResult).isEqualTo("10.0"); + } + + // ------------------------------------------------------------------------- // Supportive methods // -------------------------------------------------------------------------