diff --git a/core/build.gradle b/core/build.gradle index 6bb079b2a0..4e6ad95fd3 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -43,8 +43,8 @@ ext { buildToolsVersion: "29.0.3", minSdkVersion : 19, targetSdkVersion : 29, - versionCode : 240, - versionName : "1.4.0" + versionCode : 241, + versionName : "1.4.1" ] libraries = [ diff --git a/core/gradle.properties b/core/gradle.properties index 888c4b4e0e..339527049f 100644 --- a/core/gradle.properties +++ b/core/gradle.properties @@ -29,8 +29,8 @@ # Properties which are consumed by plugins/gradle-mvn-push.gradle plugin. # They are used for publishing artifact to snapshot repository. -VERSION_NAME=1.4.0 -VERSION_CODE=240 +VERSION_NAME=1.4.1 +VERSION_CODE=241 GROUP=org.hisp.dhis diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallMockIntegrationShould.kt b/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallMockIntegrationShould.kt new file mode 100644 index 0000000000..bad0bd18ba --- /dev/null +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/datavalue/internal/DataValuePostCallMockIntegrationShould.kt @@ -0,0 +1,79 @@ +package org.hisp.dhis.android.core.datavalue.internal + +import com.google.common.truth.Truth.assertThat +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestMetadataEnqueable +import org.hisp.dhis.android.core.utils.runner.D2JunitRunner +import org.junit.After +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(D2JunitRunner::class) +class DataValuePostCallMockIntegrationShould : BaseMockIntegrationTestMetadataEnqueable() { + + @After + @Throws(D2Error::class) + fun tearDown() { + d2.wipeModule().wipeData() + } + + @Test + fun post_dataValues_success() { + // Given user sets correct data values + dhis2MockServer.enqueueMockResponse("datavalueset/data_value_set_success.json") + provideDataValues("30", "40") + + // When user sync data in order to upload the data values + d2.dataValueModule().dataValues().blockingUpload() + + // Then all data set should be properly synced + val warnings = d2.dataValueModule().dataValues().byState().eq(State.SYNCED).blockingGet() + assertThat(warnings.size).isEqualTo(2) + } + + @Test + fun post_dataValues_warning() { + // Given user sets one unsupported type of data value + dhis2MockServer.enqueueMockResponse("datavalueset/data_value_set_warning.json") + provideDataValues("30", "40L") + + // When user sync data in order to upload the data values + d2.dataValueModule().dataValues().blockingUpload() + + // Then one data set should marked as WARNING + val warnings = d2.dataValueModule().dataValues().byState().eq(State.WARNING).blockingGet() + assertThat(warnings.size).isEqualTo(1) + } + + @Test + fun post_dataValues_undetermined_warning() { + // Given user sets one undetermined data value + dhis2MockServer.enqueueMockResponse("datavalueset/data_value_set_warning.json") + provideDataValues("40", "50L") + + // When user sync data in order to upload the data values + d2.dataValueModule().dataValues().blockingUpload() + + // Then all data values should be marked as WARNING + val warnings = d2.dataValueModule().dataValues().byState().eq(State.WARNING).blockingGet() + assertThat(warnings.size).isEqualTo(2) + } + + private fun provideDataValues(value1: String, value2: String) { + d2.dataValueModule().dataValues().value( + "20191021", + "DiszpKrYNg8", + "Ok9OQpitjQr", + "DwrQJzeChWp", + "DwrQJzeChWp" + ).blockingSet(value1) + d2.dataValueModule().dataValues().value( + "20191021", + "DiszpKrYNg8", + "vANAXwtLwcT", + "bRowv6yZOF2", + "bRowv6yZOF2" + ).blockingSet(value2) + } +} diff --git a/core/src/androidTest/java/org/hisp/dhis/android/core/program/CreateProgramStageUtils.java b/core/src/androidTest/java/org/hisp/dhis/android/core/program/CreateProgramStageUtils.java index fe5c1a2ab5..f0218c238b 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/core/program/CreateProgramStageUtils.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/core/program/CreateProgramStageUtils.java @@ -40,6 +40,7 @@ public class CreateProgramStageUtils { private static final String DISPLAY_NAME = "test_display_name"; private static final String EXECUTION_DATE_LABEL = "test_executionDateLabel"; + private static final String DUE_DATE_LABEL = "test_dueDateLabel"; private static final Integer ALLOW_GENERATE_NEXT_VISIT = 0; private static final Integer VALID_COMPLETE_ONLY = 0; private static final String REPORT_DATE_TO_USE = "test_reportDateToUse"; @@ -69,6 +70,7 @@ public static ContentValues create(long id, String uid, String programId) { programStage.put(IdentifiableColumns.CREATED, DATE); programStage.put(IdentifiableColumns.LAST_UPDATED, DATE); programStage.put(ProgramStageTableInfo.Columns.EXECUTION_DATE_LABEL, EXECUTION_DATE_LABEL); + programStage.put(ProgramStageTableInfo.Columns.DUE_DATE_LABEL, DUE_DATE_LABEL); programStage.put(ProgramStageTableInfo.Columns.ALLOW_GENERATE_NEXT_VISIT, ALLOW_GENERATE_NEXT_VISIT); programStage.put(ProgramStageTableInfo.Columns.VALID_COMPLETE_ONLY, VALID_COMPLETE_ONLY); programStage.put(ProgramStageTableInfo.Columns.REPORT_DATE_TO_USE, REPORT_DATE_TO_USE); 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 1b431cf492..0b85265799 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 @@ -141,7 +141,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp createEnrollment() createTrackerEvent(eventUid = event1, programStageUid = programStage1, eventDate = Date()) insertTrackedEntityDataValue(event1, dataElement1, "4") - setProgramIndicatorExpressionAsAvg(de(programStage1, dataElement1)) + setProgramIndicatorExpression(de(programStage1, dataElement1)) val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue(enrollmentUid, programIndicatorUid) assertThat(result).isEqualTo("4") } @@ -151,7 +151,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp createEnrollment() createTrackerEvent(eventUid = event1, programStageUid = programStage1, eventDate = Date()) insertTrackedEntityDataValue(event1, dataElement1, "text data-value") - setProgramIndicatorExpressionAsAvg(de(programStage1, dataElement1)) + setProgramIndicatorExpression(de(programStage1, dataElement1)) val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue(enrollmentUid, programIndicatorUid) assertThat(result).isEqualTo("text data-value") } @@ -162,7 +162,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp createTrackerEvent(eventUid = event1, programStageUid = programStage1) insertTrackedEntityDataValue(event1, dataElement1, "5") insertTrackedEntityDataValue(event1, dataElement2, "3") - setProgramIndicatorExpressionAsAvg("${de(programStage1, dataElement1)} * ${de(programStage1, dataElement2)}") + setProgramIndicatorExpression("${de(programStage1, dataElement1)} * ${de(programStage1, dataElement2)}") val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue(enrollmentUid, programIndicatorUid) assertThat(result).isEqualTo("15") } @@ -173,13 +173,13 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp createTrackerEvent(eventUid = event1, programStageUid = programStage1) insertTrackedEntityDataValue(event1, dataElement1, "3") insertTrackedEntityDataValue(event1, dataElement2, "5") - setProgramIndicatorExpressionAsAvg("${de(programStage1, dataElement1)} / ${de(programStage1, dataElement2)}") + setProgramIndicatorExpression("${de(programStage1, dataElement1)} / ${de(programStage1, dataElement2)}") val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue(enrollmentUid, programIndicatorUid) assertThat(result).isEqualTo("0.6") } @Test - fun evaluate_last_value_indicators_different_dates() { + fun evaluate_last_value_in_repeatable_stages() { createEnrollment() createTrackerEvent( eventUid = event1, programStageUid = programStage1, @@ -196,7 +196,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp insertTrackedEntityDataValue(event1, dataElement1, "1") insertTrackedEntityDataValue(event2, dataElement1, "2") // Expected as last value insertTrackedEntityDataValue(event3, dataElement1, "3") - setProgramIndicatorExpressionAsLast(de(programStage1, dataElement1)) + setProgramIndicatorExpression(de(programStage1, dataElement1)) val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue(enrollmentUid, programIndicatorUid) assertThat(result).isEqualTo("2") } @@ -220,7 +220,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp insertTrackedEntityDataValue(event1, dataElement1, "1") insertTrackedEntityDataValue(event2, dataElement1, "2") // Expected as last value insertTrackedEntityDataValue(event3, dataElement1, "3") - setProgramIndicatorExpressionAsLast(de(programStage1, dataElement1)) + setProgramIndicatorExpression(de(programStage1, dataElement1)) val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue(enrollmentUid, programIndicatorUid) assertThat(result).isEqualTo("2") } @@ -233,7 +233,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp insertTrackedEntityDataValue(event1, dataElement1, "5") insertTrackedEntityDataValue(event2, dataElement2, "1.5") insertTrackedEntityAttributeValue(attribute1, "2") - setProgramIndicatorExpressionAsAvg( + setProgramIndicatorExpression( "(${de(programStage1, dataElement1)} + ${de(programStage2, dataElement2)})" + " / ${att(attribute1)}" ) @@ -254,7 +254,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp createTrackerEvent(event1, programStage1) createTrackerEvent(event2, programStage2) createTrackerEvent(event3, programStage2, deleted = true) - setProgramIndicatorExpressionAsAvg(`var`("event_count")) + setProgramIndicatorExpression(`var`("event_count")) val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue( enrollmentUid, programIndicatorUid @@ -268,7 +268,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp createTrackerEvent(event1, programStage1) insertTrackedEntityDataValue(event1, dataElement1, "4.8") insertTrackedEntityDataValue(event1, dataElement2, "3") - setProgramIndicatorExpressionAsAvg( + setProgramIndicatorExpression( "d2:round(${de(programStage1, dataElement1)}) * ${de(programStage1, dataElement2)}" ) val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue(enrollmentUid, programIndicatorUid) @@ -281,7 +281,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp val enrollmentDate = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").parse("2018-05-05T00:00:00.000") val incidentDate = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").parse("2018-05-21T00:00:00.000") createEnrollment(enrollmentDate, incidentDate) - setProgramIndicatorExpressionAsAvg("d2:daysBetween(V{enrollment_date}, V{incident_date})") + setProgramIndicatorExpression("d2:daysBetween(V{enrollment_date}, V{incident_date})") val result = programIndicatorEngine!!.getEnrollmentProgramIndicatorValue(enrollmentUid, programIndicatorUid) assertThat(result).isEqualTo("16") } @@ -291,7 +291,7 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp createSingleEvent(eventUid = event1, programStageUid = programStage1) insertTrackedEntityDataValue(event1, dataElement1, "3.0") insertTrackedEntityDataValue(event1, dataElement2, "4.0") - setProgramIndicatorExpressionAsLast("${de(programStage1, dataElement1)} + ${de(programStage1, dataElement2)}") + setProgramIndicatorExpression("${de(programStage1, dataElement1)} + ${de(programStage1, dataElement2)}") val result = programIndicatorEngine!!.getEventProgramIndicatorValue(event1, programIndicatorUid) assertThat(result).isEqualTo("7") } @@ -350,14 +350,10 @@ class ProgramIndicatorEngineIntegrationShould : BaseMockIntegrationTestEmptyDisp ) } - private fun setProgramIndicatorExpressionAsAvg(expression: String) { + private fun setProgramIndicatorExpression(expression: String) { insertProgramIndicator(expression, AggregationType.AVERAGE) } - private fun setProgramIndicatorExpressionAsLast(expression: String) { - insertProgramIndicator(expression, AggregationType.LAST) - } - private fun insertProgramIndicator(expression: String, aggregationType: AggregationType) { val programIndicator = ProgramIndicator.builder().uid(programIndicatorUid) .program(ObjectWithUid.create(programUid)).expression(expression).aggregationType(aggregationType).build() diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentCollectionRepositoryMockIntegrationShould.java index 057724e4ae..84db5f7422 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/enrollment/EnrollmentCollectionRepositoryMockIntegrationShould.java @@ -138,7 +138,7 @@ public void filter_by_program() { @Test public void filter_by_enrollment_date() throws ParseException { - Date created = BaseIdentifiableObject.DATE_FORMAT.parse("2018-01-10T00:00:00.000"); + Date created = BaseIdentifiableObject.DATE_FORMAT.parse("2018-01-10T13:45:00.000"); List enrollments = d2.enrollmentModule().enrollments() .byEnrollmentDate().eq(created) .blockingGet(); @@ -147,7 +147,7 @@ public void filter_by_enrollment_date() throws ParseException { @Test public void filter_by_incident_date() throws ParseException { - Date lastUpdated = BaseIdentifiableObject.DATE_FORMAT.parse("2019-01-10T00:00:00.000"); + Date lastUpdated = BaseIdentifiableObject.DATE_FORMAT.parse("2019-01-10T12:23:00.000"); List enrollments = d2.enrollmentModule().enrollments() .byIncidentDate().eq(lastUpdated) .blockingGet(); diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventCollectionRepositoryMockIntegrationShould.java index 8fc8db8b4a..0d47b07ff9 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/event/EventCollectionRepositoryMockIntegrationShould.java @@ -36,12 +36,14 @@ import org.hisp.dhis.android.core.event.EventCreateProjection; import org.hisp.dhis.android.core.event.EventStatus; import org.hisp.dhis.android.core.maintenance.D2Error; +import org.hisp.dhis.android.core.period.Period; import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; import org.hisp.dhis.android.core.utils.runner.D2JunitRunner; import org.junit.Test; import org.junit.runner.RunWith; import java.text.ParseException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -181,9 +183,14 @@ public void filter_by_organization_unit() { @Test public void filter_by_event_date() throws ParseException { + List periods = new ArrayList<>(); + periods.add(Period.builder() + .startDate(BaseNameableObject.DATE_FORMAT.parse("2017-02-27T00:00:00.000")) + .endDate(BaseNameableObject.DATE_FORMAT.parse("2017-02-27T00:00:00.000")) + .build()); List events = d2.eventModule().events() - .byEventDate().eq(BaseNameableObject.DATE_FORMAT.parse("2017-02-27T00:00:00.000")) + .byEventDate().inPeriods(periods) .blockingGet(); assertThat(events.size()).isEqualTo(1); @@ -193,7 +200,7 @@ public void filter_by_event_date() throws ParseException { public void filter_by_complete_date() throws ParseException { List events = d2.eventModule().events() - .byCompleteDate().eq(BaseNameableObject.DATE_FORMAT.parse("2016-02-27T00:00:00.000")) + .byCompleteDate().eq(BaseNameableObject.DATE_FORMAT.parse("2016-02-27T14:34:00.000")) .blockingGet(); assertThat(events.size()).isEqualTo(1); @@ -203,10 +210,11 @@ public void filter_by_complete_date() throws ParseException { public void filter_by_due_date() throws ParseException { List events = d2.eventModule().events() - .byDueDate().eq(BaseNameableObject.DATE_FORMAT.parse("2017-01-28T00:00:00.000")) + .byDueDate() + .afterOrEqual(BaseNameableObject.DATE_FORMAT.parse("2017-01-28T12:35:00.000")) .blockingGet(); - assertThat(events.size()).isEqualTo(1); + assertThat(events.size()).isEqualTo(2); } @Test diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageCollectionRepositoryMockIntegrationShould.java index 4e479feae5..49717af923 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/program/ProgramStageCollectionRepositoryMockIntegrationShould.java @@ -87,6 +87,17 @@ public void filter_by_execution_date_label() { assertThat(programStages.size()).isEqualTo(1); } + @Test + public void filter_by_due_date_label() { + List programStages = + d2.programModule().programStages() + .byDueDateLabel() + .eq("Due date") + .blockingGet(); + + assertThat(programStages.size()).isEqualTo(1); + } + @Test public void filter_by_allow_generate_next_visit() { List programStages = diff --git a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockIntegrationShould.java b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockIntegrationShould.java index c9f6fa2e9f..32ba61a2dc 100644 --- a/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockIntegrationShould.java +++ b/core/src/androidTest/java/org/hisp/dhis/android/testapp/trackedentity/search/TrackedEntityInstanceQueryCollectionRepositoryMockIntegrationShould.java @@ -28,6 +28,7 @@ package org.hisp.dhis.android.testapp.trackedentity.search; +import org.hisp.dhis.android.core.arch.helpers.DateUtils; import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance; import org.hisp.dhis.android.core.trackedentity.search.TrackedEntityInstanceQueryRepositoryScope; import org.hisp.dhis.android.core.utils.integration.mock.BaseMockIntegrationTestFullDispatcher; @@ -35,6 +36,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.text.ParseException; +import java.util.Date; import java.util.List; import static com.google.common.truth.Truth.assertThat; @@ -63,6 +66,34 @@ public void find_uids_by_program() { assertThat(trackedEntityInstanceUids.size()).isEqualTo(2); } + @Test + public void find_by_enrollment_date() throws ParseException { + Date refDate = DateUtils.DATE_FORMAT.parse("2018-01-10T00:00:00.000"); + + List trackedEntityInstances = + d2.trackedEntityModule().trackedEntityInstanceQuery() + .byProgram().eq("lxAQ7Zs9VYR") + .byProgramDate().afterOrEqual(refDate) + .byProgramDate().beforeOrEqual(refDate) + .blockingGet(); + + assertThat(trackedEntityInstances.size()).isEqualTo(1); + } + + @Test + public void find_by_event_date() throws ParseException { + Date refDate = DateUtils.DATE_FORMAT.parse("2015-05-01T00:00:00.000"); + + List trackedEntityInstances = + d2.trackedEntityModule().trackedEntityInstanceQuery() + .byProgram().eq("lxAQ7Zs9VYR") + .byEventDate().afterOrEqual(refDate) + .byEventDate().beforeOrEqual(refDate) + .blockingGet(); + + assertThat(trackedEntityInstances.size()).isEqualTo(1); + } + @Test public void get_scope() { TrackedEntityInstanceQueryRepositoryScope scope = diff --git a/core/src/main/assets/migrations/99.sql b/core/src/main/assets/migrations/99.sql new file mode 100644 index 0000000000..da734f947f --- /dev/null +++ b/core/src/main/assets/migrations/99.sql @@ -0,0 +1,2 @@ +# Adds column ProgramStage.dueDateLabel +ALTER TABLE ProgramStage ADD COLUMN dueDateLabel TEXT; \ No newline at end of file diff --git a/core/src/main/assets/snapshots/98.sql b/core/src/main/assets/snapshots/99.sql similarity index 99% rename from core/src/main/assets/snapshots/98.sql rename to core/src/main/assets/snapshots/99.sql index 8f1b137ed6..9cfc3749d5 100644 --- a/core/src/main/assets/snapshots/98.sql +++ b/core/src/main/assets/snapshots/99.sql @@ -66,7 +66,7 @@ CREATE TABLE TrackerImportConflict (_id INTEGER PRIMARY KEY AUTOINCREMENT, confl 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, FOREIGN KEY (user) REFERENCES User (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, UNIQUE (organisationUnitScope, user, organisationUnit)); CREATE TABLE RelationshipType (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, fromToName TEXT, toFromName TEXT, bidirectional INTEGER, accessDataWrite INTEGER ); -CREATE TABLE ProgramStage (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, executionDateLabel TEXT, allowGenerateNextVisit INTEGER, validCompleteOnly INTEGER, reportDateToUse TEXT, openAfterEnrollment INTEGER, repeatable INTEGER, formType TEXT, displayGenerateEventBox INTEGER, generatedByEnrollmentDate INTEGER, autoGenerateEvent INTEGER, sortOrder INTEGER, hideDueDate INTEGER, blockEntryForm INTEGER, minDaysFromStart INTEGER, standardInterval INTEGER, program TEXT NOT NULL, periodType TEXT, accessDataWrite INTEGER, remindCompleted INTEGER, description TEXT, displayDescription TEXT, featureType TEXT, color TEXT, icon TEXT, enableUserAssignment INTEGER, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); +CREATE TABLE ProgramStage (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, executionDateLabel TEXT, allowGenerateNextVisit INTEGER, validCompleteOnly INTEGER, reportDateToUse TEXT, openAfterEnrollment INTEGER, repeatable INTEGER, formType TEXT, displayGenerateEventBox INTEGER, generatedByEnrollmentDate INTEGER, autoGenerateEvent INTEGER, sortOrder INTEGER, hideDueDate INTEGER, blockEntryForm INTEGER, minDaysFromStart INTEGER, standardInterval INTEGER, program TEXT NOT NULL, periodType TEXT, accessDataWrite INTEGER, remindCompleted INTEGER, description TEXT, displayDescription TEXT, featureType TEXT, color TEXT, icon TEXT, enableUserAssignment INTEGER, dueDateLabel TEXT, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE Program (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, code TEXT, name TEXT, displayName TEXT, created TEXT, lastUpdated TEXT, shortName TEXT, displayShortName TEXT, description TEXT, displayDescription TEXT, version INTEGER, onlyEnrollOnce INTEGER, enrollmentDateLabel TEXT, displayIncidentDate INTEGER, incidentDateLabel TEXT, registration INTEGER, selectEnrollmentDatesInFuture INTEGER, dataEntryMethod INTEGER, ignoreOverdueEvents INTEGER, selectIncidentDatesInFuture INTEGER, useFirstStageDuringRegistration INTEGER, displayFrontPageList INTEGER, programType TEXT, relatedProgram TEXT, trackedEntityType TEXT, categoryCombo TEXT, accessDataWrite INTEGER, expiryDays INTEGER, completeEventsExpiryDays INTEGER, expiryPeriodType TEXT, minAttributesRequiredToSearch INTEGER, maxTeiCountToReturn INTEGER, featureType TEXT, accessLevel TEXT, color TEXT, icon TEXT, FOREIGN KEY (trackedEntityType) REFERENCES TrackedEntityType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (categoryCombo) REFERENCES CategoryCombo (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE TrackedEntityInstance (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, organisationUnit TEXT, trackedEntityType TEXT, geometryType TEXT, geometryCoordinates TEXT, state TEXT, deleted INTEGER, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityType) REFERENCES TrackedEntityType (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); CREATE TABLE Enrollment (_id INTEGER PRIMARY KEY AUTOINCREMENT, uid TEXT NOT NULL UNIQUE, created TEXT, lastUpdated TEXT, createdAtClient TEXT, lastUpdatedAtClient TEXT, organisationUnit TEXT NOT NULL, program TEXT NOT NULL, enrollmentDate TEXT, incidentDate TEXT, followup INTEGER, status TEXT, trackedEntityInstance TEXT NOT NULL, state TEXT, geometryType TEXT, geometryCoordinates TEXT, deleted INTEGER, completedDate TEXT, FOREIGN KEY (organisationUnit) REFERENCES OrganisationUnit (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (program) REFERENCES Program (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY (trackedEntityInstance) REFERENCES TrackedEntityInstance (uid) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED); 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 dd448decb7..1dfd2092f1 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 = 98; + static final int VERSION = 99; private final AssetManager assetManager; private final int targetVersion; diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/BaseAbstractFilterConnector.java b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/BaseAbstractFilterConnector.java index 4953c53eed..c89787637e 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/BaseAbstractFilterConnector.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/BaseAbstractFilterConnector.java @@ -36,7 +36,7 @@ import java.util.Arrays; import java.util.Collection; -abstract class BaseAbstractFilterConnector extends AbstractFilterConnector { +public abstract class BaseAbstractFilterConnector extends AbstractFilterConnector { BaseAbstractFilterConnector(BaseRepositoryFactory repositoryFactory, RepositoryScope scope, diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateFilterConnector.java b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateFilterConnector.kt similarity index 77% rename from core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateFilterConnector.java rename to core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateFilterConnector.kt index 23eb7c4c72..2f07ea29e0 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateFilterConnector.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateFilterConnector.kt @@ -25,31 +25,24 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.arch.repositories.filters.internal -package org.hisp.dhis.android.core.arch.repositories.filters.internal; +import java.util.* +import org.hisp.dhis.android.core.arch.dateformat.internal.SafeDateFormat +import org.hisp.dhis.android.core.arch.repositories.collection.BaseRepository +import org.hisp.dhis.android.core.arch.repositories.collection.internal.BaseRepositoryFactory +import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope +import org.hisp.dhis.android.core.arch.repositories.scope.internal.FilterItemOperator +import org.hisp.dhis.android.core.period.DatePeriod +import org.hisp.dhis.android.core.period.Period +import org.hisp.dhis.android.core.period.internal.InPeriodQueryHelper -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.arch.helpers.DateUtils; -import org.hisp.dhis.android.core.arch.repositories.collection.BaseRepository; -import org.hisp.dhis.android.core.arch.repositories.collection.internal.BaseRepositoryFactory; -import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope; -import org.hisp.dhis.android.core.arch.repositories.scope.internal.FilterItemOperator; -import org.hisp.dhis.android.core.period.DatePeriod; -import org.hisp.dhis.android.core.period.Period; -import org.hisp.dhis.android.core.period.internal.InPeriodQueryHelper; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public final class DateFilterConnector extends BaseAbstractFilterConnector { - - DateFilterConnector(BaseRepositoryFactory repositoryFactory, - RepositoryScope scope, - String key) { - super(repositoryFactory, scope, key); - } +abstract class DateFilterConnector internal constructor( + repositoryFactory: BaseRepositoryFactory, + scope: RepositoryScope, + key: String, + val formatter: SafeDateFormat +) : BaseAbstractFilterConnector(repositoryFactory, scope, key) { /** * Returns a new repository whose scope is the one of the current repository plus the new filter being applied. @@ -57,8 +50,8 @@ public final class DateFilterConnector extends BaseAbs * @param value value to compare with the target field * @return the new repository */ - public R before(Date value) { - return newWithWrappedScope(FilterItemOperator.LT, value); + fun before(value: Date): R { + return newWithWrappedScope(FilterItemOperator.LT, value) } /** @@ -67,8 +60,8 @@ public R before(Date value) { * @param value value to compare with the target field * @return the new repository */ - public R beforeOrEqual(Date value) { - return newWithWrappedScope(FilterItemOperator.LE, value); + fun beforeOrEqual(value: Date): R { + return newWithWrappedScope(FilterItemOperator.LE, value) } /** @@ -77,8 +70,8 @@ public R beforeOrEqual(Date value) { * @param value value to compare with the target field * @return the new repository */ - public R after(Date value) { - return newWithWrappedScope(FilterItemOperator.GT, value); + fun after(value: Date): R { + return newWithWrappedScope(FilterItemOperator.GT, value) } /** @@ -87,8 +80,8 @@ public R after(Date value) { * @param value value to compare with the target field * @return the new repository */ - public R afterOrEqual(Date value) { - return newWithWrappedScope(FilterItemOperator.GE, value); + fun afterOrEqual(value: Date): R { + return newWithWrappedScope(FilterItemOperator.GE, value) } /** @@ -98,8 +91,8 @@ public R afterOrEqual(Date value) { * @param datePeriods date periods to compare with the target field * @return the new repository */ - public R inDatePeriods(@NonNull List datePeriods) { - return newWithWrappedScope(InPeriodQueryHelper.buildInPeriodsQuery(key, datePeriods)); + fun inDatePeriods(datePeriods: List): R { + return newWithWrappedScope(InPeriodQueryHelper.buildInPeriodsQuery(key, datePeriods, formatter)) } /** @@ -109,15 +102,15 @@ public R inDatePeriods(@NonNull List datePeriods) { * @param periods periods to compare with the target field * @return the new repository */ - public R inPeriods(@NonNull List periods) { - List datePeriods = new ArrayList<>(); - for (Period period : periods) { - datePeriods.add(DatePeriod.builder().startDate(period.startDate()).endDate(period.endDate()).build()); + fun inPeriods(periods: List): R { + val datePeriods: MutableList = ArrayList() + for (period in periods) { + datePeriods.add(DatePeriod.builder().startDate(period.startDate()).endDate(period.endDate()).build()) } - return inDatePeriods(datePeriods); + return inDatePeriods(datePeriods) } - protected String wrapValue(Date value) { - return "'" + DateUtils.DATE_FORMAT.format(value) + "'"; + override fun wrapValue(value: Date): String { + return "'${formatter.format(value)}'" } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateTimeFilterConnector.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateTimeFilterConnector.kt new file mode 100644 index 0000000000..a495ed034f --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/DateTimeFilterConnector.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2021, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.arch.repositories.filters.internal + +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.arch.repositories.collection.BaseRepository +import org.hisp.dhis.android.core.arch.repositories.collection.internal.BaseRepositoryFactory +import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope + +class DateTimeFilterConnector internal constructor( + repositoryFactory: BaseRepositoryFactory, + scope: RepositoryScope, + key: String +) : DateFilterConnector(repositoryFactory, scope, key, DateUtils.DATE_FORMAT) diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/FilterConnectorFactory.java b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/FilterConnectorFactory.java index 66e27afd13..190b7e7dfe 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/FilterConnectorFactory.java +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/FilterConnectorFactory.java @@ -55,7 +55,11 @@ public BaseStringFilterConnector baseString(String key) { } public DateFilterConnector date(String key) { - return new DateFilterConnector<>(repositoryFactory, scope, key); + return new DateTimeFilterConnector<>(repositoryFactory, scope, key); + } + + public DateFilterConnector simpleDate(String key) { + return new SimpleDateFilterConnector<>(repositoryFactory, scope, key); } public BooleanFilterConnector bool(String key) { diff --git a/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/SimpleDateFilterConnector.kt b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/SimpleDateFilterConnector.kt new file mode 100644 index 0000000000..67e2049d47 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/arch/repositories/filters/internal/SimpleDateFilterConnector.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2021, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.arch.repositories.filters.internal + +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.arch.repositories.collection.BaseRepository +import org.hisp.dhis.android.core.arch.repositories.collection.internal.BaseRepositoryFactory +import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope + +class SimpleDateFilterConnector internal constructor( + repositoryFactory: BaseRepositoryFactory, + scope: RepositoryScope, + key: String +) : DateFilterConnector(repositoryFactory, scope, "date($key)", DateUtils.SIMPLE_DATE_FORMAT) diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.java b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.java deleted file mode 100644 index b0aa1e3ed2..0000000000 --- a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2004-2021, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.datavalue.internal; - -import androidx.annotation.NonNull; - -import org.hisp.dhis.android.core.common.State; -import org.hisp.dhis.android.core.datavalue.DataValue; -import org.hisp.dhis.android.core.imports.ImportStatus; -import org.hisp.dhis.android.core.imports.internal.DataValueImportSummary; -import org.hisp.dhis.android.core.imports.internal.ImportConflict; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.inject.Inject; - -import dagger.Reusable; - -@Reusable -final class DataValueImportHandler { - - private final DataValueStore dataValueStore; - - @Inject - DataValueImportHandler(DataValueStore dataValueStore) { - this.dataValueStore = dataValueStore; - } - - void handleImportSummary(@NonNull DataValueSet dataValueSet, - @NonNull DataValueImportSummary dataValueImportSummary) { - if (dataValueImportSummary == null || dataValueSet == null) { - return; - } - - State state = (dataValueImportSummary.importStatus() == ImportStatus.ERROR) ? State.ERROR : - (dataValueImportSummary.importStatus() == ImportStatus.WARNING) ? State.WARNING : State.SYNCED; - - if (state == State.WARNING) { - handleDataValueWarnings(dataValueSet, dataValueImportSummary); - } else { - setStateToDataValues(state, dataValueSet.getDataValues()); - } - } - - private void handleDataValueWarnings(DataValueSet dataValueSet, DataValueImportSummary dataValueImportSummary) { - if (dataValueImportSummary.importConflicts() == null) { - setStateToDataValues(State.WARNING, dataValueSet.getDataValues()); - } else { - Set dataValueConflicts = new HashSet<>(); - boolean setStateOnlyForConflicts = Boolean.TRUE; - for (ImportConflict importConflict : dataValueImportSummary.importConflicts()) { - List dataValues = getDataValues(importConflict, dataValueSet.getDataValues()); - if (dataValues.isEmpty()) { - setStateOnlyForConflicts = Boolean.FALSE; - } - dataValueConflicts.addAll(dataValues); - } - setDataValueStates(dataValueSet, dataValueConflicts, setStateOnlyForConflicts); - } - } - - private void setDataValueStates(DataValueSet dataValueSet, - Set dataValueConflicts, - boolean setStateOnlyForConflicts) { - if (setStateOnlyForConflicts) { - Iterator i = dataValueSet.getDataValues().iterator(); - while (i.hasNext()) { - if (dataValueConflicts.contains(i.next())) { - i.remove(); - } - } - setStateToDataValues(State.WARNING, dataValueConflicts); - } - setStateToDataValues(State.SYNCED, dataValueSet.getDataValues()); - } - - private List getDataValues(ImportConflict importConflict, Collection dataValues) - throws IllegalArgumentException { - String patternStr = "(?<=:\\s)[a-zA-Z0-9]{11}"; - Pattern pattern = Pattern.compile(patternStr); - Matcher matcher = pattern.matcher(importConflict.value()); - - List foundDataValues = new ArrayList<>(); - - if (matcher.find()) { - String value = importConflict.object(); - String dataElementUid = matcher.group(0); - for (DataValue dataValue : dataValues) { - if (dataValue.value().equals(value) && dataValue.dataElement().equals(dataElementUid)) { - foundDataValues.add(dataValue); - } - } - } - - return foundDataValues; - } - - private void setStateToDataValues(State state, Collection dataValues) { - for (DataValue dataValue : dataValues) { - if (dataValueStore.isDataValueBeingUpload(dataValue)) { - if (state == State.SYNCED && dataValueStore.isDeleted(dataValue)) { - dataValueStore.deleteWhere(dataValue); - } else { - dataValueStore.setState(dataValue, state); - } - } - } - } -} diff --git a/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.kt b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.kt new file mode 100644 index 0000000000..3be37abbba --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/datavalue/internal/DataValueImportHandler.kt @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2004-2021, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.datavalue.internal + +import dagger.Reusable +import java.util.ArrayList +import java.util.HashSet +import java.util.regex.Pattern +import javax.inject.Inject +import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.datavalue.DataValue +import org.hisp.dhis.android.core.imports.ImportStatus +import org.hisp.dhis.android.core.imports.internal.DataValueImportSummary +import org.hisp.dhis.android.core.imports.internal.ImportConflict + +@Reusable +internal class DataValueImportHandler @Inject constructor( + private val dataValueStore: DataValueStore +) { + + fun handleImportSummary( + dataValueSet: DataValueSet?, + dataValueImportSummary: DataValueImportSummary? + ) { + if (dataValueSet == null || dataValueImportSummary == null) { + return + } + + val state = when (dataValueImportSummary.importStatus()) { + ImportStatus.ERROR -> State.ERROR + ImportStatus.WARNING -> State.WARNING + else -> State.SYNCED + } + + if (state == State.WARNING) { + handleDataValueWarnings(dataValueSet, dataValueImportSummary) + } else { + setStateToDataValues(state, dataValueSet.dataValues) + } + } + + private fun handleDataValueWarnings( + dataValueSet: DataValueSet, + dataValueImportSummary: DataValueImportSummary + ) { + getValuesWithConflicts(dataValueSet, dataValueImportSummary)?.let { dataValueConflicts -> + setDataValueStates(dataValueSet, dataValueConflicts) + } ?: setStateToDataValues(State.WARNING, dataValueSet.dataValues) + } + + private fun getValuesWithConflicts( + dataValueSet: DataValueSet, + dataValueImportSummary: DataValueImportSummary + ): Set? { + val dataValueConflicts: MutableSet = HashSet() + dataValueImportSummary.importConflicts()?.forEach { importConflict -> + getDataValues(importConflict, dataValueSet.dataValues).let { dataValues -> + if (dataValues.isEmpty()) { + return null + } + dataValueConflicts.addAll(dataValues) + } + } + return dataValueConflicts + } + + private fun setDataValueStates( + dataValueSet: DataValueSet, + dataValueConflicts: Set + ) { + val syncedValues = dataValueSet.dataValues.filter { dataValue -> + !dataValueConflicts.contains(dataValue) + } + setStateToDataValues(State.WARNING, dataValueConflicts) + setStateToDataValues(State.SYNCED, syncedValues) + } + + private fun getDataValues( + importConflict: ImportConflict, + dataValues: Collection + ): List { + val patternStr = "(?<=:\\s)[a-zA-Z0-9]{11}" + val pattern = Pattern.compile(patternStr) + val matcher = pattern.matcher(importConflict.value()) + + val foundDataValues: MutableList = ArrayList() + + if (matcher.find()) { + val value = importConflict.`object`() + val dataElementUid = matcher.group(0) + for (dataValue in dataValues) { + if (dataValue.value() == value && dataValue.dataElement() == dataElementUid) { + foundDataValues.add(dataValue) + } + } + } + return foundDataValues + } + + private fun setStateToDataValues(state: State, dataValues: Collection) { + for (dataValue in dataValues) { + if (dataValueStore.isDataValueBeingUpload(dataValue)) { + if (state == State.SYNCED && dataValueStore.isDeleted(dataValue)) { + dataValueStore.deleteWhere(dataValue) + } else { + dataValueStore.setState(dataValue, state) + } + } + } + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentCollectionRepository.java index 87a5ca8514..666221b5cf 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/enrollment/EnrollmentCollectionRepository.java @@ -107,11 +107,11 @@ public StringFilterConnector byProgram() { } public DateFilterConnector byEnrollmentDate() { - return cf.date(Columns.ENROLLMENT_DATE); + return cf.simpleDate(Columns.ENROLLMENT_DATE); } public DateFilterConnector byIncidentDate() { - return cf.date(Columns.INCIDENT_DATE); + return cf.simpleDate(Columns.INCIDENT_DATE); } public BooleanFilterConnector byFollowUp() { diff --git a/core/src/main/java/org/hisp/dhis/android/core/event/EventCollectionRepository.java b/core/src/main/java/org/hisp/dhis/android/core/event/EventCollectionRepository.java index d48199161c..e1cd072580 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/event/EventCollectionRepository.java +++ b/core/src/main/java/org/hisp/dhis/android/core/event/EventCollectionRepository.java @@ -156,15 +156,15 @@ public StringFilterConnector byOrganisationUnitUid() } public DateFilterConnector byEventDate() { - return cf.date(Columns.EVENT_DATE); + return cf.simpleDate(Columns.EVENT_DATE); } public DateFilterConnector byCompleteDate() { - return cf.date(Columns.COMPLETE_DATE); + return cf.simpleDate(Columns.COMPLETE_DATE); } public DateFilterConnector byDueDate() { - return cf.date(Columns.DUE_DATE); + return cf.simpleDate(Columns.DUE_DATE); } public EnumFilterConnector byState() { diff --git a/core/src/main/java/org/hisp/dhis/android/core/period/internal/AbstractPeriodGenerator.java b/core/src/main/java/org/hisp/dhis/android/core/period/internal/AbstractPeriodGenerator.java index a18bac2622..fb09de539c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/period/internal/AbstractPeriodGenerator.java +++ b/core/src/main/java/org/hisp/dhis/android/core/period/internal/AbstractPeriodGenerator.java @@ -64,25 +64,14 @@ public final List generatePeriods(int start, int end) throws RuntimeExce List periods = new ArrayList<>(); setCalendarToStartTimeOfADay(calendar); moveToStartOfCurrentPeriod(); - movePeriods(start); + moveToTheFirstPeriod(start); for (int i = 0; i < end - start; i++) { - Date startDate = calendar.getTime(); - String periodId = generateId(); - - this.movePeriods(1); - calendar.add(Calendar.MILLISECOND, -1); - Date endDate = calendar.getTime(); - - Period period = Period.builder() - .periodType(periodType) - .startDate(startDate) - .periodId(periodId) - .endDate(endDate) - .build(); + Period period = generatePeriod(calendar.getTime(), 0); periods.add(period); - calendar.add(Calendar.MILLISECOND, 1); + calendar.setTime(period.startDate()); + movePeriods(1); } return periods; } @@ -91,10 +80,7 @@ public final List generatePeriods(int start, int end) throws RuntimeExce public final Period generatePeriod(Date date, int periodOffset) { this.calendar = (Calendar) initialCalendar.clone(); - calendar.setTime(date); - setCalendarToStartTimeOfADay(calendar); - moveToStartOfCurrentPeriod(); - this.movePeriods(periodOffset); + moveToStartOfThePeriodOfADayWithOffset(date, periodOffset); Date startDate = calendar.getTime(); String periodId = generateId(); @@ -102,6 +88,8 @@ public final Period generatePeriod(Date date, int periodOffset) { calendar.add(Calendar.MILLISECOND, -1); Date endDate = calendar.getTime(); + moveToStartOfThePeriodOfADayWithOffset(date, periodOffset); + return Period.builder() .periodType(periodType) .startDate(startDate) @@ -137,6 +125,28 @@ public List generatePeriodsInYear(int yearOffset) { return periods; } + private void moveToTheFirstPeriod(int start) { + int periods = 0; + while (periods < Math.abs(start)) { + Period period = generatePeriod(calendar.getTime(), 0); + if (start > 0) { + calendar.setTime(period.startDate()); + movePeriods(1); + } else { + calendar.setTime(period.startDate()); + calendar.add(Calendar.MILLISECOND, -1); + } + periods++; + } + } + + private void moveToStartOfThePeriodOfADayWithOffset(Date date, int periodOffset) { + this.calendar.setTime(date); + setCalendarToStartTimeOfADay(calendar); + moveToStartOfCurrentPeriod(); + this.movePeriods(periodOffset); + } + static void setCalendarToStartTimeOfADay(Calendar calendar) { calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); diff --git a/core/src/main/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelper.java b/core/src/main/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelper.kt similarity index 61% rename from core/src/main/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelper.java rename to core/src/main/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelper.kt index 242d215ffb..a929c44765 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelper.java +++ b/core/src/main/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelper.kt @@ -25,40 +25,30 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.hisp.dhis.android.core.period.internal -package org.hisp.dhis.android.core.period.internal; +import org.hisp.dhis.android.core.arch.dateformat.internal.SafeDateFormat +import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder +import org.hisp.dhis.android.core.period.DatePeriod -import org.hisp.dhis.android.core.arch.db.querybuilders.internal.WhereClauseBuilder; -import org.hisp.dhis.android.core.common.BaseIdentifiableObject; -import org.hisp.dhis.android.core.period.DatePeriod; +internal object InPeriodQueryHelper { -import java.util.List; - -public final class InPeriodQueryHelper { - - private InPeriodQueryHelper(){ - } - - public static String buildInPeriodsQuery(String key, List datePeriods) { - WhereClauseBuilder builder = new WhereClauseBuilder(); - for (int i = 0; i < datePeriods.size(); i++) { - builder.appendComplexQuery(buildWhereClause(key, datePeriods, i)); - - if (i != datePeriods.size() - 1) { + fun buildInPeriodsQuery(key: String, datePeriods: List, formatter: SafeDateFormat): String { + val builder = WhereClauseBuilder() + datePeriods.forEachIndexed { index, datePeriod -> + builder.appendComplexQuery(buildWhereClause(key, datePeriod, formatter)) + if (index != datePeriods.size - 1) { builder - .appendOperator(" OR "); + .appendOperator(" OR ") } } - - return builder.build(); + return builder.build() } - private static String buildWhereClause(String key, List datePeriods, int i) { - return new WhereClauseBuilder() - .appendKeyGreaterOrEqStringValue(key, - BaseIdentifiableObject.DATE_FORMAT.format(datePeriods.get(i).startDate())) - .appendKeyLessThanOrEqStringValue(key, - BaseIdentifiableObject.DATE_FORMAT.format(datePeriods.get(i).endDate())) - .build(); + private fun buildWhereClause(key: String, datePeriod: DatePeriod, formatter: SafeDateFormat): String { + return WhereClauseBuilder() + .appendKeyGreaterOrEqStringValue(key, formatter.format(datePeriod.startDate())) + .appendKeyLessThanOrEqStringValue(key, formatter.format(datePeriod.endDate())) + .build() } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramStage.java b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramStage.java index eafee35d3b..6e19424baf 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramStage.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramStage.java @@ -79,6 +79,10 @@ public abstract class ProgramStage extends BaseIdentifiableObject @JsonProperty() public abstract String executionDateLabel(); + @Nullable + @JsonProperty() + public abstract String dueDateLabel(); + @Nullable @JsonProperty() public abstract Boolean allowGenerateNextVisit(); @@ -210,6 +214,8 @@ public static abstract class Builder extends BaseIdentifiableObject.Builder byExecutionDateLa return cf.string(Columns.EXECUTION_DATE_LABEL); } + public StringFilterConnector byDueDateLabel() { + return cf.string(Columns.DUE_DATE_LABEL); + } + public BooleanFilterConnector byAllowGenerateNextVisit() { return cf.bool(Columns.ALLOW_GENERATE_NEXT_VISIT); } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramStageTableInfo.java b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramStageTableInfo.java index bed774eebd..bb464203be 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/ProgramStageTableInfo.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/ProgramStageTableInfo.java @@ -55,6 +55,7 @@ public static class Columns extends IdentifiableWithStyleColumns { public static final String DESCRIPTION = "description"; public static final String DISPLAY_DESCRIPTION = "displayDescription"; public static final String EXECUTION_DATE_LABEL = "executionDateLabel"; + public static final String DUE_DATE_LABEL = "dueDateLabel"; public static final String ALLOW_GENERATE_NEXT_VISIT = "allowGenerateNextVisit"; public static final String VALID_COMPLETE_ONLY = "validCompleteOnly"; public static final String REPORT_DATE_TO_USE = "reportDateToUse"; @@ -82,6 +83,7 @@ public String[] all() { DESCRIPTION, DISPLAY_DESCRIPTION, EXECUTION_DATE_LABEL, + DUE_DATE_LABEL, ALLOW_GENERATE_NEXT_VISIT, VALID_COMPLETE_ONLY, REPORT_DATE_TO_USE, diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageFields.java b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageFields.java index 75b6265a70..fdbe2baa8d 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageFields.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageFields.java @@ -65,6 +65,7 @@ public final class ProgramStageFields { fh.field(Columns.DESCRIPTION), fh.field(Columns.DISPLAY_DESCRIPTION), fh.field(Columns.EXECUTION_DATE_LABEL), + fh.field(Columns.DUE_DATE_LABEL), fh.field(Columns.ALLOW_GENERATE_NEXT_VISIT), fh.field(Columns.VALID_COMPLETE_ONLY), fh.field(Columns.REPORT_DATE_TO_USE), diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageStore.java b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageStore.java index 456347b57c..7ca63a8db2 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageStore.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/internal/ProgramStageStore.java @@ -28,6 +28,8 @@ package org.hisp.dhis.android.core.program.internal; +import androidx.annotation.NonNull; + import org.hisp.dhis.android.core.arch.db.access.DatabaseAdapter; import org.hisp.dhis.android.core.arch.db.stores.binders.internal.IdentifiableWithStyleStatementBinder; import org.hisp.dhis.android.core.arch.db.stores.binders.internal.StatementBinder; @@ -39,8 +41,6 @@ import org.hisp.dhis.android.core.program.ProgramStage; import org.hisp.dhis.android.core.program.ProgramStageTableInfo; -import androidx.annotation.NonNull; - public final class ProgramStageStore { private static StatementBinder BINDER = new IdentifiableWithStyleStatementBinder() { @@ -51,26 +51,27 @@ public void bindToStatement(@NonNull ProgramStage o, @NonNull StatementWrapper w w.bind(9, o.description()); w.bind(10, o.displayDescription()); w.bind(11, o.executionDateLabel()); - w.bind(12, o.allowGenerateNextVisit()); - w.bind(13, o.validCompleteOnly()); - w.bind(14, o.reportDateToUse()); - w.bind(15, o.openAfterEnrollment()); - w.bind(16, o.repeatable()); - w.bind(17, o.formType().name()); - w.bind(18, o.displayGenerateEventBox()); - w.bind(19, o.generatedByEnrollmentDate()); - w.bind(20, o.autoGenerateEvent()); - w.bind(21, o.sortOrder()); - w.bind(22, o.hideDueDate()); - w.bind(23, o.blockEntryForm()); - w.bind(24, o.minDaysFromStart()); - w.bind(25, o.standardInterval()); - w.bind(26, UidsHelper.getUidOrNull(o.program())); - w.bind(27, o.periodType()); - w.bind(28, o.access().data().write()); - w.bind(29, o.remindCompleted()); - w.bind(30, o.featureType()); - w.bind(31, o.enableUserAssignment()); + w.bind(12, o.dueDateLabel()); + w.bind(13, o.allowGenerateNextVisit()); + w.bind(14, o.validCompleteOnly()); + w.bind(15, o.reportDateToUse()); + w.bind(16, o.openAfterEnrollment()); + w.bind(17, o.repeatable()); + w.bind(18, o.formType().name()); + w.bind(19, o.displayGenerateEventBox()); + w.bind(20, o.generatedByEnrollmentDate()); + w.bind(21, o.autoGenerateEvent()); + w.bind(22, o.sortOrder()); + w.bind(23, o.hideDueDate()); + w.bind(24, o.blockEntryForm()); + w.bind(25, o.minDaysFromStart()); + w.bind(26, o.standardInterval()); + w.bind(27, UidsHelper.getUidOrNull(o.program())); + w.bind(28, o.periodType()); + w.bind(29, o.access().data().write()); + w.bind(30, o.remindCompleted()); + w.bind(31, o.featureType()); + w.bind(32, o.enableUserAssignment()); } }; diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemStageElement.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemStageElement.java index 80720c16f5..0eb9dccc9c 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemStageElement.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/dataitem/ProgramItemStageElement.java @@ -28,7 +28,6 @@ package org.hisp.dhis.android.core.program.programindicatorengine.internal.dataitem; -import org.hisp.dhis.android.core.common.AggregationType; 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; @@ -55,15 +54,8 @@ public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor if (eventList != null) { List candidates = getCandidates(eventList, dataElementId); - AggregationType aggregationType = visitor.getProgramIndicatorContext().programIndicator().aggregationType(); - if (!candidates.isEmpty()) { - if (AggregationType.LAST.equals(aggregationType) || - AggregationType.LAST_AVERAGE_ORG_UNIT.equals(aggregationType)) { - value = candidates.get(candidates.size() - 1).value(); - } else { - value = candidates.get(0).value(); - } + value = candidates.get(candidates.size() - 1).value(); } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/ProgramVariableItem.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/ProgramVariableItem.java index 8a45c62f5b..83a9b6e485 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/ProgramVariableItem.java +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/ProgramVariableItem.java @@ -46,6 +46,7 @@ import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.V_EVENT_COUNT; import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.V_EVENT_DATE; import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.V_INCIDENT_DATE; +import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.V_TEI_COUNT; import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.V_VALUE_COUNT; import static org.hisp.dhis.parser.expression.antlr.ExpressionParser.V_ZERO_POS_VALUE_COUNT; @@ -63,6 +64,7 @@ public class ProgramVariableItem extends ProgramExpressionItem { .put(V_CREATION_DATE, new VCreationDate()) .put(V_COMPLETED_DATE, new VCompletedDate()) + .put(V_TEI_COUNT, new VTeiCount()) .put(V_ENROLLMENT_STATUS, new VEnrollmentStatus()) .put(V_ENROLLMENT_COUNT, new VEnrollmentCount()) diff --git a/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VTeiCount.java b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VTeiCount.java new file mode 100644 index 0000000000..abab205989 --- /dev/null +++ b/core/src/main/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/variable/VTeiCount.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2004-2021, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.hisp.dhis.android.core.program.programindicatorengine.internal.variable; + +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; + +public class VTeiCount + implements ExpressionItem { + + @Override + public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor visitor) { + + int count = visitor.getProgramIndicatorContext().enrollment() == null ? 0 : 1; + + return String.valueOf(count); + } +} diff --git a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelper.kt b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelper.kt index 5342f13d5c..fd8e4773e7 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelper.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelper.kt @@ -163,18 +163,16 @@ internal class TrackedEntityInstanceLocalQueryHelper @Inject constructor( where.appendKeyStringValue(dot(enrollmentAlias, program), escapeQuotes(scope.program())) } if (scope.programDate() != null) { + val enrollmentDateStr = "date(${dot(enrollmentAlias, enrollmentDate)})" + dateFilterPeriodHelper.getStartDate(scope.programDate()!!)?.let { startDate -> - where.appendKeyGreaterOrEqStringValue( - dot(enrollmentAlias, enrollmentDate), - DateUtils.SIMPLE_DATE_FORMAT.format(startDate) - ) + val startDateStr = DateUtils.SIMPLE_DATE_FORMAT.format(startDate) + where.appendKeyGreaterOrEqStringValue(enrollmentDateStr, startDateStr) } dateFilterPeriodHelper.getEndDate(scope.programDate()!!)?.let { endDate -> - where.appendKeyLessThanOrEqStringValue( - dot(enrollmentAlias, enrollmentDate), - DateUtils.SIMPLE_DATE_FORMAT.format(endDate) - ) + val endDateStr = DateUtils.SIMPLE_DATE_FORMAT.format(endDate) + where.appendKeyLessThanOrEqStringValue(enrollmentDateStr, endDateStr) } } if (scope.enrollmentStatus() != null) { @@ -327,24 +325,37 @@ internal class TrackedEntityInstanceLocalQueryHelper @Inject constructor( for (eventStatus in statusList) { val statusWhere = WhereClauseBuilder() when (eventStatus) { - EventStatus.ACTIVE, EventStatus.COMPLETED, EventStatus.VISITED -> { - statusWhere.appendKeyStringValue(dot(eventAlias, EventTableInfo.Columns.STATUS), eventStatus) + EventStatus.ACTIVE -> { + appendEventDates(statusWhere, eventFilter, EventTableInfo.Columns.EVENT_DATE) + statusWhere.appendInKeyEnumValues( + dot(eventAlias, EventTableInfo.Columns.STATUS), + listOf(EventStatus.ACTIVE, EventStatus.SCHEDULE, EventStatus.OVERDUE) + ) + } + EventStatus.COMPLETED, EventStatus.VISITED -> { appendEventDates(statusWhere, eventFilter, EventTableInfo.Columns.EVENT_DATE) + statusWhere.appendKeyStringValue(dot(eventAlias, EventTableInfo.Columns.STATUS), eventStatus) } EventStatus.SCHEDULE -> { appendEventDates(statusWhere, eventFilter, EventTableInfo.Columns.DUE_DATE) statusWhere.appendIsNullValue(EventTableInfo.Columns.EVENT_DATE) - statusWhere.appendIsNotNullValue(dot(eventAlias, EventTableInfo.Columns.STATUS)) + statusWhere.appendInKeyEnumValues( + dot(eventAlias, EventTableInfo.Columns.STATUS), + listOf(EventStatus.SCHEDULE, EventStatus.OVERDUE) + ) statusWhere.appendKeyGreaterOrEqStringValue( - dot(eventAlias, EventTableInfo.Columns.DUE_DATE), nowStr + "date(${dot(eventAlias, EventTableInfo.Columns.DUE_DATE)})", nowStr ) } EventStatus.OVERDUE -> { appendEventDates(statusWhere, eventFilter, EventTableInfo.Columns.DUE_DATE) statusWhere.appendIsNullValue(EventTableInfo.Columns.EVENT_DATE) - statusWhere.appendIsNotNullValue(dot(eventAlias, EventTableInfo.Columns.STATUS)) + statusWhere.appendInKeyEnumValues( + dot(eventAlias, EventTableInfo.Columns.STATUS), + listOf(EventStatus.SCHEDULE, EventStatus.OVERDUE) + ) statusWhere.appendKeyLessThanStringValue( - dot(eventAlias, EventTableInfo.Columns.DUE_DATE), nowStr + "date(${dot(eventAlias, EventTableInfo.Columns.DUE_DATE)})", nowStr ) } EventStatus.SKIPPED -> { @@ -367,13 +378,14 @@ internal class TrackedEntityInstanceLocalQueryHelper @Inject constructor( refDate: String ) { if (eventFilter.eventDate() != null) { + val refDateStr = "date(${dot(eventAlias, refDate)})" dateFilterPeriodHelper.getStartDate(eventFilter.eventDate()!!)?.let { startDate -> - val dateStr = DateUtils.SIMPLE_DATE_FORMAT.format(startDate) - where.appendKeyGreaterOrEqStringValue(dot(eventAlias, refDate), dateStr) + val startDateStr = DateUtils.SIMPLE_DATE_FORMAT.format(startDate) + where.appendKeyGreaterOrEqStringValue(refDateStr, startDateStr) } dateFilterPeriodHelper.getEndDate(eventFilter.eventDate()!!)?.let { endDate -> - val dateStr = DateUtils.SIMPLE_DATE_FORMAT.format(endDate) - where.appendKeyLessThanOrEqStringValue(dot(eventAlias, refDate), dateStr) + val endDateStr = DateUtils.SIMPLE_DATE_FORMAT.format(endDate) + where.appendKeyLessThanOrEqStringValue(refDateStr, endDateStr) } } } diff --git a/core/src/main/java/org/hisp/dhis/android/core/user/openid/OpenIDConnectHandlerImpl.kt b/core/src/main/java/org/hisp/dhis/android/core/user/openid/OpenIDConnectHandlerImpl.kt index 267cc4fa87..afdff166eb 100644 --- a/core/src/main/java/org/hisp/dhis/android/core/user/openid/OpenIDConnectHandlerImpl.kt +++ b/core/src/main/java/org/hisp/dhis/android/core/user/openid/OpenIDConnectHandlerImpl.kt @@ -33,6 +33,7 @@ import android.content.Intent import dagger.Reusable import io.reactivex.Observable import io.reactivex.Single +import io.reactivex.schedulers.Schedulers import javax.inject.Inject import net.openid.appauth.* import org.hisp.dhis.android.core.user.User @@ -71,9 +72,11 @@ internal class OpenIDConnectHandlerImpl @Inject constructor( Single.error(ex) } else { val response = AuthorizationResponse.fromIntent(intent)!! - downloadToken(response.createTokenExchangeRequest()).map { - logInCall.blockingLogInOpenIDConnect(serverUrl, it) - } + downloadToken(response.createTokenExchangeRequest()) + .observeOn(Schedulers.io()) + .map { + logInCall.blockingLogInOpenIDConnect(serverUrl, it) + } } } else { Single.error(RuntimeException("Unexpected intent or request code")) diff --git a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/program/ProgramStageSamples.java b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/program/ProgramStageSamples.java index b112f51a00..51b31e2bca 100644 --- a/core/src/sharedTest/java/org/hisp/dhis/android/core/data/program/ProgramStageSamples.java +++ b/core/src/sharedTest/java/org/hisp/dhis/android/core/data/program/ProgramStageSamples.java @@ -51,6 +51,7 @@ public static ProgramStage getProgramStage() { .description("description") .displayDescription("display_description") .executionDateLabel("execution_date_label") + .dueDateLabel("due_date_label") .allowGenerateNextVisit(Boolean.FALSE) .validCompleteOnly(Boolean.TRUE) .reportDateToUse("report_date_to_use") diff --git a/core/src/sharedTest/resources/datavalueset/data_value_set_success.json b/core/src/sharedTest/resources/datavalueset/data_value_set_success.json new file mode 100644 index 0000000000..a36d88db9e --- /dev/null +++ b/core/src/sharedTest/resources/datavalueset/data_value_set_success.json @@ -0,0 +1,40 @@ +{ + "responseType": "ImportSummary", + "status": "SUCCESS", + "importOptions": { + "idSchemes": {}, + "dryRun": false, + "async": false, + "importStrategy": "CREATE_AND_UPDATE", + "mergeMode": "REPLACE", + "reportMode": "FULL", + "skipExistingCheck": false, + "sharing": false, + "skipNotifications": false, + "skipAudit": false, + "datasetAllowsPeriods": false, + "strictPeriods": false, + "strictDataElements": false, + "strictCategoryOptionCombos": false, + "strictAttributeOptionCombos": false, + "strictOrganisationUnits": false, + "requireCategoryOptionCombo": false, + "requireAttributeOptionCombo": false, + "skipPatternValidation": false, + "ignoreEmptyCollection": false, + "force": false, + "firstRowIsHeader": true, + "skipLastUpdated": false, + "mergeDataValues": false, + "skipCache": false + }, + "description": "Import process completed successfully", + "importCount": { + "imported": 0, + "updated": 3, + "ignored": 0, + "deleted": 0 + }, + "conflicts": [], + "dataSetComplete": "false" +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/datavalueset/data_value_set_warning.json b/core/src/sharedTest/resources/datavalueset/data_value_set_warning.json new file mode 100644 index 0000000000..dabfdd390a --- /dev/null +++ b/core/src/sharedTest/resources/datavalueset/data_value_set_warning.json @@ -0,0 +1,45 @@ +{ + "responseType": "ImportSummary", + "status": "WARNING", + "importOptions": { + "idSchemes": {}, + "dryRun": false, + "async": false, + "importStrategy": "CREATE_AND_UPDATE", + "mergeMode": "REPLACE", + "reportMode": "FULL", + "skipExistingCheck": false, + "sharing": false, + "skipNotifications": false, + "skipAudit": false, + "datasetAllowsPeriods": false, + "strictPeriods": false, + "strictDataElements": false, + "strictCategoryOptionCombos": false, + "strictAttributeOptionCombos": false, + "strictOrganisationUnits": false, + "requireCategoryOptionCombo": false, + "requireAttributeOptionCombo": false, + "skipPatternValidation": false, + "ignoreEmptyCollection": false, + "force": false, + "firstRowIsHeader": true, + "skipLastUpdated": false, + "mergeDataValues": false, + "skipCache": false + }, + "description": "Import process completed successfully", + "importCount": { + "imported": 0, + "updated": 0, + "ignored": 1, + "deleted": 0 + }, + "conflicts": [ + { + "object": "40L", + "value": "El valor de dato no es un entero, must match data element type: vANAXwtLwcT" + } + ], + "dataSetComplete": "false" +} \ No newline at end of file diff --git a/core/src/sharedTest/resources/program/program_stage.json b/core/src/sharedTest/resources/program/program_stage.json index b04ea17db7..8a1f35a98b 100644 --- a/core/src/sharedTest/resources/program/program_stage.json +++ b/core/src/sharedTest/resources/program/program_stage.json @@ -17,6 +17,7 @@ "enableUserAssignment": "true", "captureCoordinates": true, "formType": "DEFAULT", + "dueDateLabel": "Due date", "remindCompleted": false, "displayGenerateEventBox": false, "generatedByEnrollmentDate": false, diff --git a/core/src/sharedTest/resources/program/program_stages.json b/core/src/sharedTest/resources/program/program_stages.json index 36604f64f0..d0a3c3eb27 100644 --- a/core/src/sharedTest/resources/program/program_stages.json +++ b/core/src/sharedTest/resources/program/program_stages.json @@ -9,6 +9,7 @@ "displayDescription": "Display Description", "allowGenerateNextVisit": false, "executionDateLabel": "Visit date", + "dueDateLabel": "Due date", "validCompleteOnly": true, "displayName": "Antenatal care visit - Program rules demo", "openAfterEnrollment": false, @@ -193,6 +194,7 @@ "name": "Child care visit - demo", "allowGenerateNextVisit": true, "executionDateLabel": "Visit date 2", + "dueDateLabel": "Due date 2", "validCompleteOnly": false, "displayName": "Antenatal care visit - Program rules demo", "openAfterEnrollment": true, diff --git a/core/src/test/java/org/hisp/dhis/android/core/period/internal/BiWeeklyPeriodGeneratorShould.java b/core/src/test/java/org/hisp/dhis/android/core/period/internal/BiWeeklyPeriodGeneratorShould.java index 14e873c3d4..0c3ad616d3 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/period/internal/BiWeeklyPeriodGeneratorShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/period/internal/BiWeeklyPeriodGeneratorShould.java @@ -29,6 +29,8 @@ import com.google.common.collect.Lists; +import org.hisp.dhis.android.core.arch.dateformat.internal.SafeDateFormat; +import org.hisp.dhis.android.core.arch.helpers.DateUtils; import org.hisp.dhis.android.core.period.Period; import org.hisp.dhis.android.core.period.PeriodType; import org.junit.Test; @@ -39,6 +41,7 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.List; +import java.util.stream.Collectors; import static com.google.common.truth.Truth.assertThat; @@ -47,6 +50,8 @@ public class BiWeeklyPeriodGeneratorShould { protected final Calendar calendar; + private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + public BiWeeklyPeriodGeneratorShould() { this.calendar = Calendar.getInstance(); } @@ -76,6 +81,20 @@ public void generate_bi_weekly_periods() { assertThat(generatedPeriods).isEqualTo(Lists.newArrayList(period1, period2)); } + @Test + public void generate_all_bi_weekly_periods() { + calendar.set(2021, 2, 25); + + List generatedPeriods = new BiWeeklyPeriodGenerator(calendar).generatePeriods( + PeriodType.BiWeekly.getDefaultStartPeriods(), + PeriodType.BiWeekly.getDefaultEndPeriods()); + + List periodIds = generatedPeriods.stream().map(period -> period.periodId()).collect(Collectors.toList()); + + assertThat(periodIds.contains("2020BiW27")).isTrue(); + assertThat(generatedPeriods.size()).isEqualTo(13); + } + @Test public void generate_bi_weekly_periods_for_changing_year() { calendar.set(2016,11,31); @@ -133,18 +152,65 @@ public void generate_period_id_with_offset() throws ParseException { } @Test - public void generate_periods_in_this_year() { + public void generate_last_periods_in_53_weeks_year() { + calendar.set(2021, 0, 18); + SafeDateFormat dateFormat = DateUtils.SIMPLE_DATE_FORMAT; + + PeriodGenerator biWeeklyGenerator = new BiWeeklyPeriodGenerator(calendar); + List periods = biWeeklyGenerator.generatePeriods(-3, 0); + + Period first2021 = periods.get(periods.size() - 1); + Period last2020 = periods.get(periods.size() - 2); + + assertThat(first2021.periodId()).isEqualTo("2021BiW1"); + assertThat(dateFormat.format(first2021.startDate())).isEqualTo("2021-01-04"); + assertThat(dateFormat.format(first2021.endDate())).isEqualTo("2021-01-17"); + + assertThat(last2020.periodId()).isEqualTo("2020BiW27"); + assertThat(dateFormat.format(last2020.startDate())).isEqualTo("2020-12-28"); + assertThat(dateFormat.format(last2020.endDate())).isEqualTo("2021-01-10"); + } + + @Test + public void generate_last_periods_in_53_weeks_year_starting_past_year() { + calendar.set(2020, 11, 2); + SafeDateFormat dateFormat = DateUtils.SIMPLE_DATE_FORMAT; + + PeriodGenerator biWeeklyGenerator = new BiWeeklyPeriodGenerator(calendar); + List periods = biWeeklyGenerator.generatePeriods(1, 5); + + Period first2021 = periods.get(periods.size() - 2); + Period last2020 = periods.get(periods.size() - 3); + + assertThat(first2021.periodId()).isEqualTo("2021BiW1"); + assertThat(dateFormat.format(first2021.startDate())).isEqualTo("2021-01-04"); + assertThat(dateFormat.format(first2021.endDate())).isEqualTo("2021-01-17"); + + assertThat(last2020.periodId()).isEqualTo("2020BiW27"); + assertThat(dateFormat.format(last2020.startDate())).isEqualTo("2020-12-28"); + assertThat(dateFormat.format(last2020.endDate())).isEqualTo("2021-01-10"); + } + + @Test + public void generate_periods_in_this_year() throws ParseException { calendar.set(2020, 7, 29); PeriodGenerator generator = new BiWeeklyPeriodGenerator(calendar); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); List periods = generator.generatePeriodsInYear(0); - // TODO Related to https://jira.dhis2.org/browse/ANDROSDK-1315 - /* assertThat(periods.size()).isEqualTo(27); assertThat(periods.get(0).periodId()).isEqualTo("2020BiW1"); assertThat(periods.get(26).periodId()).isEqualTo("2020BiW27"); - */ + assertThat(periods.get(26).startDate()).isEqualTo(format.parse("2020-12-28")); + + calendar.set(2021, 4, 15); + generator = new BiWeeklyPeriodGenerator(calendar); + periods = generator.generatePeriodsInYear(0); + + assertThat(periods.size()).isEqualTo(26); + assertThat(periods.get(0).periodId()).isEqualTo("2021BiW1"); + assertThat(periods.get(0).startDate()).isEqualTo(format.parse("2021-1-4")); } @Test diff --git a/core/src/test/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelperShould.java b/core/src/test/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelperShould.java deleted file mode 100644 index bd9541895e..0000000000 --- a/core/src/test/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelperShould.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2004-2021, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.hisp.dhis.android.core.period.internal; - -import com.google.common.collect.Lists; - -import org.hisp.dhis.android.core.common.BaseIdentifiableObject; -import org.hisp.dhis.android.core.period.DatePeriod; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.Date; -import java.util.List; - -import static com.google.common.truth.Truth.assertThat; - -@RunWith(JUnit4.class) -public class InPeriodQueryHelperShould { - - @Test - public void generate_one_requested_period() throws Exception { - - Date date1 = BaseIdentifiableObject.DATE_FORMAT.parse("2001-12-24T12:24:25.203"); - Date date2 = BaseIdentifiableObject.DATE_FORMAT.parse("2002-12-24T12:24:25.203"); - Date date3 = BaseIdentifiableObject.DATE_FORMAT.parse("2003-12-24T12:24:25.203"); - Date date4 = BaseIdentifiableObject.DATE_FORMAT.parse("2004-12-24T12:24:25.203"); - Date date5 = BaseIdentifiableObject.DATE_FORMAT.parse("2005-12-24T12:24:25.203"); - Date date6 = BaseIdentifiableObject.DATE_FORMAT.parse("2006-12-24T12:24:25.203"); - - List datePeriods = Lists.newArrayList( - DatePeriod.create(date1, date2), - DatePeriod.create(date3, date4), - DatePeriod.create(date5, date6)); - - String inPeriodQuery = InPeriodQueryHelper.buildInPeriodsQuery("COL1", datePeriods); - - assertThat(inPeriodQuery).isEqualTo( - "(COL1 >= '2001-12-24T12:24:25.203' AND COL1 <= '2002-12-24T12:24:25.203') OR " + - "(COL1 >= '2003-12-24T12:24:25.203' AND COL1 <= '2004-12-24T12:24:25.203') OR " + - "(COL1 >= '2005-12-24T12:24:25.203' AND COL1 <= '2006-12-24T12:24:25.203')" - ); - } -} \ No newline at end of file diff --git a/core/src/test/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelperShould.kt b/core/src/test/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelperShould.kt new file mode 100644 index 0000000000..1f56e92383 --- /dev/null +++ b/core/src/test/java/org/hisp/dhis/android/core/period/internal/InPeriodQueryHelperShould.kt @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2004-2021, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.android.core.period.internal + +import com.google.common.truth.Truth +import org.hisp.dhis.android.core.arch.helpers.DateUtils +import org.hisp.dhis.android.core.period.DatePeriod +import org.hisp.dhis.android.core.period.internal.InPeriodQueryHelper.buildInPeriodsQuery +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class InPeriodQueryHelperShould { + + @Test + @Throws(Exception::class) + fun generate_one_requested_period() { + val formatter = DateUtils.DATE_FORMAT + + val date1 = formatter.parse("2001-12-24T12:24:25.203") + val date2 = formatter.parse("2002-12-24T12:24:25.203") + val date3 = formatter.parse("2003-12-24T12:24:25.203") + val date4 = formatter.parse("2004-12-24T12:24:25.203") + val date5 = formatter.parse("2005-12-24T12:24:25.203") + val date6 = formatter.parse("2006-12-24T12:24:25.203") + + val datePeriods: List = listOf( + DatePeriod.create(date1, date2), + DatePeriod.create(date3, date4), + DatePeriod.create(date5, date6) + ) + + val inPeriodQuery = buildInPeriodsQuery("COL1", datePeriods, formatter) + Truth.assertThat(inPeriodQuery).isEqualTo( + "(COL1 >= '2001-12-24T12:24:25.203' AND COL1 <= '2002-12-24T12:24:25.203') OR " + + "(COL1 >= '2003-12-24T12:24:25.203' AND COL1 <= '2004-12-24T12:24:25.203') OR " + + "(COL1 >= '2005-12-24T12:24:25.203' AND COL1 <= '2006-12-24T12:24:25.203')" + ) + } + + @Test + @Throws(Exception::class) + fun generate_one_requested_period_simple_date() { + val formatter = DateUtils.SIMPLE_DATE_FORMAT + + val date1 = formatter.parse("2001-12-24T12:24:25.203") + val date2 = formatter.parse("2002-12-24T12:24:25.203") + val date3 = formatter.parse("2003-12-24T12:24:25.203") + val date4 = formatter.parse("2004-12-24T12:24:25.203") + + val datePeriods: List = listOf( + DatePeriod.create(date1, date2), + DatePeriod.create(date3, date4) + ) + + val inPeriodQuery = buildInPeriodsQuery("date(COL1)", datePeriods, formatter) + Truth.assertThat(inPeriodQuery).isEqualTo( + "(date(COL1) >= '2001-12-24' AND date(COL1) <= '2002-12-24') OR " + + "(date(COL1) >= '2003-12-24' AND date(COL1) <= '2004-12-24')" + ) + } +} diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/ProgramStageShould.java b/core/src/test/java/org/hisp/dhis/android/core/program/ProgramStageShould.java index 1412fc990c..dbd77a57d5 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/program/ProgramStageShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/program/ProgramStageShould.java @@ -70,6 +70,7 @@ public void map_from_json_string() throws IOException, ParseException { assertThat(programStage.captureCoordinates()).isTrue(); assertThat(programStage.displayGenerateEventBox()).isFalse(); assertThat(programStage.executionDateLabel()).isNull(); + assertThat(programStage.dueDateLabel()).isEqualTo("Due date"); assertThat(programStage.formType()).isEqualTo(FormType.DEFAULT); assertThat(programStage.generatedByEnrollmentDate()).isFalse(); assertThat(programStage.hideDueDate()).isFalse(); diff --git a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutorShould.kt b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutorShould.kt index e460bc2a26..e2634b1931 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutorShould.kt +++ b/core/src/test/java/org/hisp/dhis/android/core/program/programindicatorengine/internal/ProgramIndicatorExecutorShould.kt @@ -33,7 +33,6 @@ import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever import java.text.ParseException import org.hisp.dhis.android.core.arch.db.stores.internal.IdentifiableObjectStore -import org.hisp.dhis.android.core.common.AggregationType import org.hisp.dhis.android.core.common.BaseIdentifiableObject import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.android.core.constant.Constant @@ -157,14 +156,8 @@ class ProgramIndicatorExecutorShould { fun evaluate_data_elements_in_stage() { val expression = "${de(programStage1, dataElementUid1)} + ${de(programStage2, dataElementUid2)}" whenever(dataValue1.value()) doReturn "4.5" - whenever(dataValue2_1.value()) doReturn "0.8" whenever(dataValue2_2.value()) doReturn "20.6" - whenever(programIndicator.aggregationType()) doReturn AggregationType.NONE - val resultNone = programIndicatorExecutor.getProgramIndicatorValue(expression) - assertThat(resultNone).isEqualTo("5.3") - - whenever(programIndicator.aggregationType()) doReturn AggregationType.LAST val resultLast = programIndicatorExecutor.getProgramIndicatorValue(expression) assertThat(resultLast).isEqualTo("25.1") } @@ -176,7 +169,7 @@ class ProgramIndicatorExecutorShould { `var`("value_count") ) whenever(dataValue1.value()) doReturn "4.5" - whenever(dataValue2_1.value()) doReturn "1.9" + whenever(dataValue2_2.value()) doReturn "1.9" val resultNone = programIndicatorExecutor.getProgramIndicatorValue(programIndicator.expression()) assertThat(resultNone).isEqualTo("3.2") @@ -189,7 +182,7 @@ class ProgramIndicatorExecutorShould { `var`("zero_pos_value_count") ) whenever(dataValue1.value()) doReturn "7.5" - whenever(dataValue2_1.value()) doReturn "-1.5" + whenever(dataValue2_2.value()) doReturn "-1.5" val resultNone = programIndicatorExecutor.getProgramIndicatorValue(programIndicator.expression()) assertThat(resultNone).isEqualTo("6") @@ -363,6 +356,15 @@ class ProgramIndicatorExecutorShould { assertThat(result).isEqualTo("5.3") } + @Test + @Throws(ParseException::class) + fun evaluate_tei_count() { + setExpression(`var`("tei_count")) + + val result = programIndicatorExecutor.getProgramIndicatorValue(programIndicator.expression()) + assertThat(result).isEqualTo("1") + } + // ------------------------------------------------------------------------- // Supportive methods // ------------------------------------------------------------------------- diff --git a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelperShould.java b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelperShould.java index fa44e80412..4ba92ce1f0 100644 --- a/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelperShould.java +++ b/core/src/test/java/org/hisp/dhis/android/core/trackedentity/search/TrackedEntityInstanceLocalQueryHelperShould.java @@ -100,8 +100,8 @@ public void build_sql_query_with_enrollment_date() throws ParseException { .build(); String sqlQuery = localQueryHelper.getSqlQuery(scope, Collections.emptySet(), 50); - assertThat(sqlQuery).contains("enrollmentDate >= '2019-04-15'"); - assertThat(sqlQuery).contains("enrollmentDate <= '2019-05-19'"); + assertThat(sqlQuery).contains("date(en.enrollmentDate) >= '2019-04-15'"); + assertThat(sqlQuery).contains("date(en.enrollmentDate) <= '2019-05-19'"); } @Test diff --git a/docs/content/developer/getting-started.md b/docs/content/developer/getting-started.md index 68534a3711..59be23b9fe 100644 --- a/docs/content/developer/getting-started.md +++ b/docs/content/developer/getting-started.md @@ -6,7 +6,7 @@ Include dependency in build.gradle. ```gradle dependencies { - implementation "org.hisp.dhis:android-core:1.4.0" + implementation "org.hisp.dhis:android-core:1.4.1" ... } ``` diff --git a/docs/dhis2_android_sdk_developer_guide_INDEX.md b/docs/dhis2_android_sdk_developer_guide_INDEX.md index 82f40e822e..770554bf77 100644 --- a/docs/dhis2_android_sdk_developer_guide_INDEX.md +++ b/docs/dhis2_android_sdk_developer_guide_INDEX.md @@ -3,11 +3,11 @@ title: 'DHIS 2 Android SDK Developer Guide' author: 'DHIS 2' date: year: 2021 -month: April +month: June keywords: [DHIS2, Android] commit: version: master -applicable_txt: 'Applicable to version 1.4.0' +applicable_txt: 'Applicable to version 1.4.1' ---