diff --git a/build.gradle b/build.gradle index f38fd9b309..33a2c066ed 100644 --- a/build.gradle +++ b/build.gradle @@ -53,9 +53,8 @@ allprojects { maven { url "https://cloudant.github.io/cloudant-sync-eap/repository" } maven { url 'https://maven.fabric.io/public' } maven { url "https://s3.amazonaws.com/repo.commonsware.com" } - maven { url "https://dl.bintray.com/ona/rdt-capture" } + maven { url 'https://nexus.pentaho.org/content/groups/omni/' } maven { url 'https://dl.bintray.com/ibm-watson-health/ibm-fhir-server-releases' } - maven { url "https://dl.bintray.com/ona/kujaku" } } } diff --git a/opensrp-chw/build.gradle b/opensrp-chw/build.gradle index 31c73b299b..b6c1b539a9 100644 --- a/opensrp-chw/build.gradle +++ b/opensrp-chw/build.gradle @@ -82,6 +82,8 @@ android { buildConfigField "boolean", "IS_SYNC_SETTINGS", "false" buildConfigField "String", "THINKMD_BASE_URL", '"https://app.africa.thinkmd.tech"' buildConfigField "String", "THINKMD_END_POINT", '"/#/start"' + buildConfigField "int", "MAX_CONNECTION_TIMEOUT", '1' + buildConfigField "int", "MAX_READ_TIMEOUT", '1' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { @@ -235,8 +237,8 @@ android { chad { dimension = 'baseDimension' applicationIdSuffix ".chad" - versionCode 1 - versionName "1.0.1" + versionCode 2 + versionName "1.0.2" buildConfigField "String", 'opensrp_url', '"https://wcaro-td.smartregister.org/opensrp/"' buildConfigField "String", 'guidebooks_url', '"https://opensrp.s3.amazonaws.com/media/chad/"' buildConfigField "String", 'opensrp_url_debug', '"https://wcaro-stage.smartregister.org/opensrp/"' @@ -246,13 +248,15 @@ android { buildConfigField "String", 'DEFAULT_LOCATION', '"Village"' buildConfigField "String", 'DEFAULT_LOCATION_DEBUG', '"CHA"' buildConfigField "int", "DATABASE_VERSION", '14' + buildConfigField "int", "MAX_CONNECTION_TIMEOUT", '5' + buildConfigField "int", "MAX_READ_TIMEOUT", '5' } drc { resConfigs "en", "fr" dimension = 'baseDimension' applicationIdSuffix ".drc" - versionCode 6 - versionName "1.0.11" + versionCode 10 + versionName "1.0.15" buildConfigField "String", 'opensrp_url', '"https://wcaro-cd.smartregister.org/opensrp/"' buildConfigField "String", 'guidebooks_url', '"https://opensrp.s3.amazonaws.com/media/drc/"' buildConfigField "String", 'opensrp_url_preview', '"https://wcaro-cd-preview.smartregister.org/opensrp/"' @@ -262,6 +266,8 @@ android { buildConfigField "String[]", "ALLOWED_LOCATION_LEVELS_DEBUG", '{"Pays" , "Province(DPS)" , "Zone de Sante","Aire de Sante" , "CAC" ,"VILLAGE/COMMUNAUTE"}' buildConfigField "String", 'DEFAULT_LOCATION_DEBUG', '"VILLAGE/COMMUNAUTE"' buildConfigField "String", 'DEFAULT_LOCATION', '"VILLAGE/COMMUNAUTE"' + buildConfigField "int", "MAX_CONNECTION_TIMEOUT", '5' + buildConfigField "int", "MAX_READ_TIMEOUT", '5' buildConfigField "int", "DATABASE_VERSION", '11' } guinea { @@ -287,13 +293,13 @@ android { togo { dimension = 'baseDimension' applicationIdSuffix ".togo" - versionCode 22 - versionName "1.2.4" + versionCode 23 + versionName "1.2.5" buildConfigField "int", "OPENMRS_UNIQUE_ID_INITIAL_BATCH_SIZE", '1000' buildConfigField "int", "OPENMRS_UNIQUE_ID_BATCH_SIZE", '500' buildConfigField "String", 'opensrp_url', '"https://wcaro-tg.smartregister.org/opensrp/"' buildConfigField "String", 'guidebooks_url', '"https://opensrp.s3.amazonaws.com/media/togo/"' - buildConfigField "String", 'opensrp_url_debug', '"https://wcaro-togo-preview.smartregister.org/opensrp/"' + buildConfigField "String", 'opensrp_url_debug', '"https://wcaro-stage.smartregister.org/opensrp/"' buildConfigField "String[]", "LOCATION_HIERACHY", '{"National", "Regional" , "District" , "Formation sanitaire", "Supervisor", "Village" }' buildConfigField "String[]", "ALLOWED_LOCATION_LEVELS_DEBUG", '{"National", "Regional" , "District" , "Formation sanitaire", "Supervisor", "Village"}' buildConfigField "String[]", "ALLOWED_LOCATION_LEVELS", '{"National", "Regional" , "District" , "Formation sanitaire", "Supervisor", "Village"}' @@ -338,7 +344,7 @@ android { } dependencies { - implementation('org.smartregister:opensrp-client-chw-core:2.1.2-SNAPSHOT@aar') { + implementation('org.smartregister:opensrp-client-chw-core:2.1.3-SNAPSHOT@aar') { transitive = true exclude group: 'com.android.support', module: 'appcompat-v7' exclude group: 'androidx.legacy', module: 'legacy-support-v4' diff --git a/opensrp-chw/src/ba/java/org/smartregister/chw/custom_view/NavigationMenuFlv.java b/opensrp-chw/src/ba/java/org/smartregister/chw/custom_view/NavigationMenuFlv.java index 61ba6450bd..c0a5df02d6 100644 --- a/opensrp-chw/src/ba/java/org/smartregister/chw/custom_view/NavigationMenuFlv.java +++ b/opensrp-chw/src/ba/java/org/smartregister/chw/custom_view/NavigationMenuFlv.java @@ -44,6 +44,11 @@ public boolean hasCommunityResponders() { return true; } + @Override + public boolean hasSyncStatusProgressBar() { + return false; + } + @Override public Intent getStockReportIntent(Activity activity) { return new Intent(activity, CoreStockInventoryReportActivity.class); diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/application/ChwSyncConfiguration.java b/opensrp-chw/src/main/java/org/smartregister/chw/application/ChwSyncConfiguration.java index 81bd40e42c..fdf0589b1e 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/application/ChwSyncConfiguration.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/application/ChwSyncConfiguration.java @@ -8,6 +8,7 @@ import org.smartregister.chw.BuildConfig; import org.smartregister.chw.activity.LoginActivity; import org.smartregister.location.helper.LocationHelper; +import org.smartregister.repository.AllSharedPreferences; import org.smartregister.view.activity.BaseLoginActivity; import java.util.List; @@ -30,15 +31,21 @@ public SyncFilter getSyncFilterParam() { @Override public String getSyncFilterValue() { - String providerId = org.smartregister.Context.getInstance().allSharedPreferences().fetchRegisteredANM(); - String userLocationId = org.smartregister.Context.getInstance().allSharedPreferences().fetchUserLocalityId(providerId); + String providerId = allSharedPreferences().fetchRegisteredANM(); + String locationId = allSharedPreferences().fetchDefaultLocalityId(providerId); + if(StringUtils.isBlank(locationId)) locationId = allSharedPreferences().fetchUserLocalityId(providerId); + List locationIds = LocationHelper.getInstance().locationsFromHierarchy(true, null); if (!isEmptyCollection(locationIds)) { - int index = locationIds.indexOf(userLocationId); + int index = locationIds.indexOf(locationId); List subLocationIds = locationIds.subList(index, locationIds.size()); return StringUtils.join(subLocationIds, ","); } - return userLocationId; + return locationId; + } + + private AllSharedPreferences allSharedPreferences(){ + return org.smartregister.Context.getInstance().allSharedPreferences(); } @Override @@ -73,7 +80,7 @@ public boolean updateClientDetailsTable() { @Override public boolean isSyncUsingPost() { - return !BuildConfig.DEBUG && ChwApplication.getApplicationFlavor().syncUsingPost(); + return ChwApplication.getApplicationFlavor().syncUsingPost(); } @Override @@ -106,8 +113,23 @@ public String getOauthClientSecret() { return BuildConfig.OAUTH_CLIENT_SECRET; } + @Override + public int getConnectTimeout() { + return BuildConfig.MAX_CONNECTION_TIMEOUT * 60000; + } + + @Override + public int getReadTimeout() { + return BuildConfig.MAX_READ_TIMEOUT * 60000; + } + @Override public Class getAuthenticationActivity() { return LoginActivity.class; } + + @Override + public boolean validateUserAssignments() { + return false; + } } diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/custom_view/DefaultNavigationMenuFlv.java b/opensrp-chw/src/main/java/org/smartregister/chw/custom_view/DefaultNavigationMenuFlv.java index 6699180c2b..fa390d1597 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/custom_view/DefaultNavigationMenuFlv.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/custom_view/DefaultNavigationMenuFlv.java @@ -17,6 +17,11 @@ public List> getSupportedLanguages() { return Arrays.asList(Pair.of("English", Locale.ENGLISH), Pair.of("Français", Locale.FRENCH)); } + @Override + public boolean hasSyncStatusProgressBar() { + return true; + } + @Override public HashMap getTableMapValues() { return new HashMap<>(); diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/dao/EventDao.java b/opensrp-chw/src/main/java/org/smartregister/chw/dao/EventDao.java index 813ce7bc22..b1db063555 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/dao/EventDao.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/dao/EventDao.java @@ -6,6 +6,7 @@ import org.smartregister.chw.application.ChwApplication; import org.smartregister.clientandeventmodel.Event; import org.smartregister.dao.AbstractDao; +import org.smartregister.repository.BaseRepository; import org.smartregister.sync.helper.ECSyncHelper; import java.util.ArrayList; @@ -15,6 +16,28 @@ public class EventDao extends AbstractDao { + /*** + * This method forces the device to resync all the data without an event id + * NB: Critical bug detected on Sync + */ + public static void markEventsForReUpload() { + updateDB("update client set syncStatus = '" + BaseRepository.TYPE_Unsynced + "' where baseEntityId in (select baseEntityId from event where ifnull(eventId,'') = '') "); + updateDB("update ImageList set syncStatus = '" + BaseRepository.TYPE_Unsynced + "' where entityId in (select baseEntityId from event where ifnull(eventId,'') = '') "); + updateDB("update event set syncStatus = '" + BaseRepository.TYPE_Unsynced + "' where ifnull(eventId,'') = '' "); + } + + /*** + * Computes the minimum server Id before any possibly unsynced event found in the database. + * This method should return a NULL value if all events are synced. + * @return + */ + public static Long getMinimumVerifiedServerVersion() { + String sql = "select max(serverVersion) serverVersion from event where dateCreated <= (select min(dateCreated) minDate from event where ifnull(eventId,'') = '' and syncStatus = 'Synced') "; + DataMap dataMap = c -> { + return getCursorLongValue(c, "serverVersion"); + }; + return AbstractDao.readSingleValue(sql, dataMap); + } public static List getEvents(String baseEntityID, String eventType, int limit) { String sql = "select json from event where baseEntityId = '" + baseEntityID + "' COLLATE NOCASE and eventType = '" + eventType + "' COLLATE NOCASE order by updatedAt desc limit " + limit; diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/sync/ChwSyncIntentService.java b/opensrp-chw/src/main/java/org/smartregister/chw/sync/ChwSyncIntentService.java index ac65c3db81..92b4535b25 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/sync/ChwSyncIntentService.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/sync/ChwSyncIntentService.java @@ -1,11 +1,30 @@ package org.smartregister.chw.sync; +import org.smartregister.chw.dao.EventDao; import org.smartregister.sync.intent.SyncIntentService; public class ChwSyncIntentService extends SyncIntentService { + @Override + protected void handleSync() { + // fetch the last downloaded serverVersion before any unsyced data + Long serverVersion = EventDao.getMinimumVerifiedServerVersion(); + if (serverVersion != null) + org.smartregister.util.Utils.getAllSharedPreferences().saveLastSyncDate(serverVersion); + + // flag all contentious events as unsynced + EventDao.markEventsForReUpload(); + super.handleSync(); + } + @Override public int getEventPullLimit() { return 1000; } + + + @Override + protected Integer getEventBatchSize(){ + return 250; + } } diff --git a/opensrp-chw/src/test/java/org/smartregister/chw/dao/EventDaoTest.java b/opensrp-chw/src/test/java/org/smartregister/chw/dao/EventDaoTest.java index 94afbbe300..45e3d6bf18 100644 --- a/opensrp-chw/src/test/java/org/smartregister/chw/dao/EventDaoTest.java +++ b/opensrp-chw/src/test/java/org/smartregister/chw/dao/EventDaoTest.java @@ -13,6 +13,7 @@ import org.robolectric.RobolectricTestRunner; import org.smartregister.clientandeventmodel.Event; import org.smartregister.dao.AbstractDao; +import org.smartregister.repository.BaseRepository; import org.smartregister.repository.Repository; import java.util.Arrays; @@ -37,7 +38,7 @@ public void setUp() { } @Test - public void getEvents() { + public void getEventsReturnsCorrectEvents() { MatrixCursor matrixCursor = new MatrixCursor(new String[]{"json"}); matrixCursor.addRow(new Object[]{eventJson}); Mockito.doReturn(matrixCursor).when(database).rawQuery(Mockito.any(), Mockito.any()); @@ -48,7 +49,7 @@ public void getEvents() { } @Test - public void getLatestEvent() { + public void getLatestEventReturnsCorrectEvent() { MatrixCursor matrixCursor = new MatrixCursor(new String[]{"json"}); matrixCursor.addRow(new Object[]{eventJson}); Mockito.doReturn(matrixCursor).when(database).rawQuery(Mockito.any(), Mockito.any()); @@ -56,5 +57,23 @@ public void getLatestEvent() { Assert.assertNotNull(event); } + @Test + public void markEventsForReUploadExecutesCorrectUpdateQueries() { + Mockito.doReturn(database).when(repository).getWritableDatabase(); + EventDao.markEventsForReUpload(); + Mockito.verify(database).rawExecSQL("update client set syncStatus = '" + BaseRepository.TYPE_Unsynced + "' where baseEntityId in (select baseEntityId from event where ifnull(eventId,'') = '') "); + Mockito.verify(database).rawExecSQL("update ImageList set syncStatus = '" + BaseRepository.TYPE_Unsynced + "' where entityId in (select baseEntityId from event where ifnull(eventId,'') = '') "); + Mockito.verify(database).rawExecSQL("update event set syncStatus = '" + BaseRepository.TYPE_Unsynced + "' where ifnull(eventId,'') = '' "); + } + + @Test + public void getMinimumVerifiedServerVersionReturnsCorrectVersion() { + MatrixCursor matrixCursor = new MatrixCursor(new String[]{"serverVersion"}); + matrixCursor.addRow(new Object[]{12324567L}); + Mockito.doReturn(matrixCursor).when(database).rawQuery(Mockito.any(), Mockito.any()); + Long serverVersion = EventDao.getMinimumVerifiedServerVersion(); + Assert.assertNotNull(serverVersion); + Assert.assertTrue(serverVersion.equals(12324567L)); + } } \ No newline at end of file