diff --git a/opensrp-core/build.gradle b/opensrp-core/build.gradle index 4b0f88ead..8c95d3aea 100644 --- a/opensrp-core/build.gradle +++ b/opensrp-core/build.gradle @@ -256,6 +256,9 @@ dependencies { implementation 'de.hdodenhof:circleimageview:2.2.0' implementation 'xerces:xercesImpl:2.12.0' + def work_version = "2.7.1" + implementation "androidx.work:work-runtime:$work_version" + // Add the dependency for the Performance Monitoring library implementation 'com.google.firebase:firebase-perf:19.0.7' diff --git a/opensrp-core/src/main/java/org/smartregister/AllConstants.java b/opensrp-core/src/main/java/org/smartregister/AllConstants.java index 61e804c89..2679d9631 100644 --- a/opensrp-core/src/main/java/org/smartregister/AllConstants.java +++ b/opensrp-core/src/main/java/org/smartregister/AllConstants.java @@ -148,6 +148,8 @@ public class AllConstants { public static final String GPS = "gps"; + public static final String IDENTIFIERS = "identifiers"; + public static class Immunizations { public static final String BCG = "bcg"; @@ -586,4 +588,14 @@ public interface SyncInfo { String TASK_UNPROCESSED_EVENTS = "taskUnprocessedEvents"; String NULL_EVENT_SYNC_STATUS = "nullEventSyncStatus"; } + + public static class EventType { + public static final String BITRH_REGISTRATION = "Birth Registration"; + public static final String NEW_WOMAN_REGISTRATION = "New Woman Registration"; + } + + public static class Entity { + public static final String MOTHER = "mother"; + } + } diff --git a/opensrp-core/src/main/java/org/smartregister/Context.java b/opensrp-core/src/main/java/org/smartregister/Context.java index f5b91566a..002c71cd3 100755 --- a/opensrp-core/src/main/java/org/smartregister/Context.java +++ b/opensrp-core/src/main/java/org/smartregister/Context.java @@ -51,6 +51,7 @@ import org.smartregister.repository.TaskRepository; import org.smartregister.repository.TimelineEventRepository; import org.smartregister.repository.UniqueIdRepository; +import org.smartregister.repository.ZeirIdCleanupRepository; import org.smartregister.service.ANMService; import org.smartregister.service.ActionService; import org.smartregister.service.AlertService; @@ -229,6 +230,7 @@ public class Context { private ManifestRepository manifestRepository; private ClientFormRepository clientFormRepository; private ClientRelationshipRepository clientRelationshipRepository; + private ZeirIdCleanupRepository zeirIdCleanupRepository; private static final String SHARED_PREFERENCES_FILENAME = "%s_preferences"; @@ -1252,5 +1254,13 @@ public ClientRelationshipRepository getClientRelationshipRepository() { return clientRelationshipRepository; } + + public ZeirIdCleanupRepository zeirIdCleanupRepository() { + if (zeirIdCleanupRepository == null) { + zeirIdCleanupRepository = new ZeirIdCleanupRepository(); + } + return zeirIdCleanupRepository; + } + /////////////////////////////////////////////////////////////////////////////// } diff --git a/opensrp-core/src/main/java/org/smartregister/domain/DuplicateZeirIdStatus.java b/opensrp-core/src/main/java/org/smartregister/domain/DuplicateZeirIdStatus.java new file mode 100644 index 000000000..9046ede70 --- /dev/null +++ b/opensrp-core/src/main/java/org/smartregister/domain/DuplicateZeirIdStatus.java @@ -0,0 +1,14 @@ +package org.smartregister.domain; + +public enum DuplicateZeirIdStatus { + CLEANED("CLEANED"), PENDING("PENDING"); + private String value; + + DuplicateZeirIdStatus(String value) { + this.value = value; + } + + public String value() { + return value; + } +} diff --git a/opensrp-core/src/main/java/org/smartregister/job/DuplicateCleanerWorker.java b/opensrp-core/src/main/java/org/smartregister/job/DuplicateCleanerWorker.java new file mode 100644 index 000000000..19676b74e --- /dev/null +++ b/opensrp-core/src/main/java/org/smartregister/job/DuplicateCleanerWorker.java @@ -0,0 +1,33 @@ +package org.smartregister.job; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.work.WorkManager; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import org.smartregister.domain.DuplicateZeirIdStatus; +import org.smartregister.util.AppHealthUtils; + +import timber.log.Timber; + +public class DuplicateCleanerWorker extends Worker { + private Context mContext; + + public DuplicateCleanerWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + mContext = context; + } + + @NonNull + @Override + public Result doWork() { + DuplicateZeirIdStatus duplicateZeirIdStatus = AppHealthUtils.cleanUniqueZeirIds(); + Timber.i("Doing some cleaning work"); + if (duplicateZeirIdStatus != null && duplicateZeirIdStatus.equals(DuplicateZeirIdStatus.CLEANED)) + WorkManager.getInstance(mContext).cancelWorkById(this.getId()); + + return Result.success(); + } +} diff --git a/opensrp-core/src/main/java/org/smartregister/repository/EventClientRepository.java b/opensrp-core/src/main/java/org/smartregister/repository/EventClientRepository.java index f31b4c524..987e387d5 100644 --- a/opensrp-core/src/main/java/org/smartregister/repository/EventClientRepository.java +++ b/opensrp-core/src/main/java/org/smartregister/repository/EventClientRepository.java @@ -6,6 +6,7 @@ import static org.smartregister.AllConstants.ROWID; import android.content.ContentValues; +import android.content.Intent; import android.database.Cursor; import android.text.TextUtils; import android.util.Pair; @@ -25,19 +26,24 @@ import org.json.JSONException; import org.json.JSONObject; import org.smartregister.AllConstants; +import org.smartregister.Context; import org.smartregister.CoreLibrary; import org.smartregister.clientandeventmodel.DateUtil; import org.smartregister.domain.Client; import org.smartregister.domain.ClientRelationship; +import org.smartregister.domain.DuplicateZeirIdStatus; import org.smartregister.domain.Event; +import org.smartregister.domain.UniqueId; import org.smartregister.domain.db.Column; import org.smartregister.domain.db.ColumnAttribute; import org.smartregister.domain.db.EventClient; import org.smartregister.p2p.sync.data.JsonData; import org.smartregister.sync.intent.P2pProcessRecordsService; +import org.smartregister.sync.intent.PullUniqueIdsIntentService; import org.smartregister.util.DatabaseMigrationUtils; import org.smartregister.util.JsonFormUtils; import org.smartregister.util.Utils; +import org.smartregister.view.activity.DrishtiApplication; import java.lang.reflect.Type; import java.text.ParseException; @@ -65,6 +71,10 @@ public class EventClientRepository extends BaseRepository { public static final String VARCHAR = "VARCHAR"; + public static final String ZEIR_ID = "ZEIR_ID"; + + public static final String M_ZEIR_ID = "M_ZEIR_ID"; + protected Table clientTable; protected Table eventTable; @@ -2346,4 +2356,94 @@ public List getEventsByTaskIds(Set taskIds) { + event_column.taskId.name() + " IN (" + StringUtils.repeat("?", ",", taskIds.size()) + ")", taskIds.toArray(new String[0])); } + + public DuplicateZeirIdStatus cleanDuplicateMotherIds() throws Exception { + String username = Context.getInstance().userService().getAllSharedPreferences().fetchRegisteredANM(); + + UniqueIdRepository uniqueIdRepository = Context.getInstance().getUniqueIdRepository(); + + Map duplicates = Context.getInstance().zeirIdCleanupRepository().getClientsWithDuplicateZeirIds(); + long unusedIdsCount = uniqueIdRepository.countUnUsedIds(); + + Timber.e("%d duplicates for provider: %s - %s", duplicates.size(), username, duplicates.toString()); + + if (duplicates.size() > 0) { + Timber.e( + "%s: %d duplicates for provider: %s - %s\nUnused Unique IDs: %d", + this.getClass().getSimpleName(), + duplicates.size(), + username, + duplicates, + unusedIdsCount + ); + } + + for (Map.Entry duplicate : duplicates.entrySet()) { + String baseEntityId = duplicate.getKey(); + String zeirId = duplicate.getValue(); + + JSONObject clientJson = getClientByBaseEntityId(baseEntityId); + JSONObject identifiers = clientJson.getJSONObject(AllConstants.IDENTIFIERS); + + long unusedIds = uniqueIdRepository.countUnUsedIds(); + if (unusedIds <= 30) { // Mske sure we have enough unused IDs left + Timber.e("%s: No more unique IDs available to assign to %s - %s; provider: %s", this.getClass().getSimpleName(), baseEntityId, zeirId, username); + android.content.Context applicationContext = CoreLibrary.getInstance().context().applicationContext(); + applicationContext.startService(new Intent(applicationContext, PullUniqueIdsIntentService.class)); + } + + UniqueId uniqueId = uniqueIdRepository.getNextUniqueId(); + String newZeirId = uniqueId != null ? uniqueId.getOpenmrsId() : null; + + if (StringUtils.isBlank(newZeirId)) { + Timber.e("No unique ID found to assign to %s; provider: %s", baseEntityId, username); + return DuplicateZeirIdStatus.PENDING; + } + + String eventType = AllConstants.EventType.BITRH_REGISTRATION; + String clientType = clientJson.getString(AllConstants.CLIENT_TYPE); + + if (AllConstants.CHILD_TYPE.equals(clientType)) { + identifiers.put(ZEIR_ID, newZeirId.replaceAll("-", "")); + } else if (AllConstants.Entity.MOTHER.equals(clientType)) { + identifiers.put(M_ZEIR_ID, newZeirId); + eventType = AllConstants.EventType.NEW_WOMAN_REGISTRATION; + } + clientJson.put(AllConstants.IDENTIFIERS, identifiers); + + // Add events to process this + addorUpdateClient(baseEntityId, clientJson); + + // fetch the birth/new woman registration event + List registrationEvent = getEvents( + Collections.singletonList(baseEntityId), + Collections.singletonList(BaseRepository.TYPE_Synced), + Collections.singletonList(eventType) + ); + Event event = null; + if (!registrationEvent.isEmpty()) + event = registrationEvent.get(0).getEvent(); + + Client client = convert(clientJson, Client.class); + + // reprocess the event + DrishtiApplication.getInstance().getClientProcessor().processClient(Collections.singletonList(new EventClient(event, client))); + markClientValidationStatus(baseEntityId, false); + + uniqueIdRepository.close(newZeirId); + + Timber.e("%s: %s - %s updated to %s; provider: %s", this.getClass().getSimpleName(), baseEntityId, zeirId, newZeirId, username); + } + + if (duplicates.size() > 0) { + Timber.d("%s: Successfully processed %d duplicates for provider: %s - %s", + this.getClass().getSimpleName(), + duplicates.size(), + username, + duplicates + ); + } + return DuplicateZeirIdStatus.CLEANED; + } + } diff --git a/opensrp-core/src/main/java/org/smartregister/repository/ZeirIdCleanupRepository.java b/opensrp-core/src/main/java/org/smartregister/repository/ZeirIdCleanupRepository.java new file mode 100644 index 000000000..01418d143 --- /dev/null +++ b/opensrp-core/src/main/java/org/smartregister/repository/ZeirIdCleanupRepository.java @@ -0,0 +1,62 @@ +package org.smartregister.repository; + +import net.sqlcipher.Cursor; + +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +import timber.log.Timber; + +public class ZeirIdCleanupRepository extends BaseRepository { + + private static final String BASE_ENTITY_ID = "baseEntityId"; + + private static final String DUPLICATES_SQL = + "WITH duplicates AS ( " + + " WITH clients AS ( " + + " SELECT baseEntityId, COALESCE(json_extract(json, '$.identifiers.ZEIR_ID'), json_extract(json, '$.identifiers.M_ZEIR_ID')) zeir_id " + + " FROM client " + + " ) " + + " SELECT b.* FROM (SELECT baseEntityId, zeir_id FROM clients GROUP BY zeir_id HAVING count(zeir_id) > 1) a " + + " INNER JOIN clients b ON a.zeir_id=b.zeir_id " + + " UNION " + + " SELECT * FROM clients WHERE zeir_id IS NULL " + + ") " + + "SELECT baseEntityId, zeir_id, lag(zeir_id) over(order by zeir_id) AS prev_zeir_id FROM duplicates"; + + public Map getClientsWithDuplicateZeirIds() { + Map duplicates = new HashMap<>(); + + Cursor cursor = null; + try { + cursor = getWritableDatabase().rawQuery(DUPLICATES_SQL, new String[]{}); + + while (cursor.moveToNext()) { + String baseEntityId = cursor.getString(cursor.getColumnIndex(BASE_ENTITY_ID)); + String zeirId = cursor.getString(cursor.getColumnIndex("zeir_id")); + + duplicates.put(baseEntityId, zeirId); + + String prevZeirId = null; + try { + prevZeirId = cursor.getString(cursor.getColumnIndex("prev_zeir_id")); + } catch (NullPointerException e) { + Timber.e(e, "null prev_zeir_id"); + } + + if (StringUtils.isNotEmpty(prevZeirId) && (prevZeirId.equals(zeirId))) { + duplicates.put(baseEntityId, prevZeirId); + } + } + } catch (Exception e) { + Timber.e(e); + } finally { + if (cursor != null && !cursor.isClosed()) + cursor.close(); + } + + return duplicates; + } +} diff --git a/opensrp-core/src/main/java/org/smartregister/sync/intent/PullUniqueIdsIntentService.java b/opensrp-core/src/main/java/org/smartregister/sync/intent/PullUniqueIdsIntentService.java index 52edaa86c..06d7b1795 100644 --- a/opensrp-core/src/main/java/org/smartregister/sync/intent/PullUniqueIdsIntentService.java +++ b/opensrp-core/src/main/java/org/smartregister/sync/intent/PullUniqueIdsIntentService.java @@ -5,7 +5,6 @@ */ import android.content.Intent; - import androidx.annotation.VisibleForTesting; import org.json.JSONArray; @@ -27,7 +26,6 @@ public class PullUniqueIdsIntentService extends BaseSyncIntentService { public static final String IDENTIFIERS = "identifiers"; private UniqueIdRepository uniqueIdRepo; - public PullUniqueIdsIntentService() { super("PullUniqueOpenMRSUniqueIdsService"); } @@ -99,5 +97,4 @@ public int onStartCommand(Intent intent, int flags, int startId) { protected HTTPAgent getHttpAgent() { return CoreLibrary.getInstance().context().getHttpAgent(); } - } diff --git a/opensrp-core/src/main/java/org/smartregister/util/AppHealthUtils.java b/opensrp-core/src/main/java/org/smartregister/util/AppHealthUtils.java index 29e16e026..9e92d305c 100644 --- a/opensrp-core/src/main/java/org/smartregister/util/AppHealthUtils.java +++ b/opensrp-core/src/main/java/org/smartregister/util/AppHealthUtils.java @@ -11,6 +11,7 @@ import android.view.View; import android.widget.ArrayAdapter; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.view.ContextThemeWrapper; @@ -19,6 +20,7 @@ import org.smartregister.AllConstants; import org.smartregister.CoreLibrary; import org.smartregister.R; +import org.smartregister.domain.DuplicateZeirIdStatus; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -26,6 +28,8 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import timber.log.Timber; + /** * Created by ndegwamartin on 26/06/2021. */ @@ -115,6 +119,18 @@ public static void refreshFileSystem(Context context, @VisibleForTesting boolean } } + @Nullable + public static DuplicateZeirIdStatus cleanUniqueZeirIds(){ + try { + return CoreLibrary.getInstance().context().getEventClientRepository() + .cleanDuplicateMotherIds(); + } catch (Exception e) { + Timber.e(e); + } + + return null; + } + public interface HealthStatsView { void performDatabaseDownload(); diff --git a/opensrp-core/src/main/java/org/smartregister/view/fragment/BaseRegisterFragment.java b/opensrp-core/src/main/java/org/smartregister/view/fragment/BaseRegisterFragment.java index 4433f318e..3b6d64ad8 100644 --- a/opensrp-core/src/main/java/org/smartregister/view/fragment/BaseRegisterFragment.java +++ b/opensrp-core/src/main/java/org/smartregister/view/fragment/BaseRegisterFragment.java @@ -115,7 +115,6 @@ public DialogOption[] serviceModeOptions() { return new DialogOption[]{ }; } - @Override public DialogOption[] sortingOptions() { return new DialogOption[]{ diff --git a/opensrp-core/src/test/java/org/smartregister/repository/EventClientRepositoryTest.java b/opensrp-core/src/test/java/org/smartregister/repository/EventClientRepositoryTest.java index 59c6d951d..222fb8875 100644 --- a/opensrp-core/src/test/java/org/smartregister/repository/EventClientRepositoryTest.java +++ b/opensrp-core/src/test/java/org/smartregister/repository/EventClientRepositoryTest.java @@ -1,5 +1,17 @@ package org.smartregister.repository; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.smartregister.AllConstants.ROWID; +import static org.smartregister.repository.BaseRepository.TYPE_InValid; +import static org.smartregister.repository.BaseRepository.TYPE_Task_Unprocessed; +import static org.smartregister.repository.BaseRepository.TYPE_Unsynced; +import static org.smartregister.repository.BaseRepository.TYPE_Valid; + import android.content.ContentValues; import android.util.Pair; @@ -26,6 +38,7 @@ import org.smartregister.clientandeventmodel.DateUtil; import org.smartregister.domain.Event; import org.smartregister.domain.SyncStatus; +import org.smartregister.domain.DuplicateZeirIdStatus; import org.smartregister.domain.db.Column; import org.smartregister.domain.db.ColumnAttribute; import org.smartregister.domain.db.EventClient; @@ -44,15 +57,6 @@ import java.util.Set; import java.util.UUID; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.smartregister.AllConstants.ROWID; -import static org.smartregister.repository.BaseRepository.TYPE_InValid; -import static org.smartregister.repository.BaseRepository.TYPE_Task_Unprocessed; -import static org.smartregister.repository.BaseRepository.TYPE_Unsynced; -import static org.smartregister.repository.BaseRepository.TYPE_Valid; - /** * Created by onaio on 29/08/2017. */ @@ -740,4 +744,72 @@ public MatrixCursor getCursorMaxRowId() { return matrixCursor; } + @Test + public void testCleanDuplicateMotherIdsShouldFixAndMarkDuplicateClientsUnSynced() throws Exception { + String DUPLICATES_SQL = "WITH duplicates AS ( " + + " WITH clients AS ( " + + " SELECT baseEntityId, COALESCE(json_extract(json, '$.identifiers.ZEIR_ID'), json_extract(json, '$.identifiers.M_ZEIR_ID')) zeir_id " + + " FROM client " + + " ) " + + " SELECT b.* FROM (SELECT baseEntityId, zeir_id FROM clients GROUP BY zeir_id HAVING count(zeir_id) > 1) a " + + " INNER JOIN clients b ON a.zeir_id=b.zeir_id " + + " UNION " + + " SELECT * FROM clients WHERE zeir_id IS NULL " + + ") " + + "SELECT baseEntityId, zeir_id, lag(zeir_id) over(order by zeir_id) AS prev_zeir_id FROM duplicates"; + + when(sqliteDatabase.rawQuery(eq(DUPLICATES_SQL), any())).thenReturn(getDuplicateZeirIdsCursor()); + when(sqliteDatabase.rawQuery("SELECT COUNT (*) FROM unique_ids WHERE status=?", new String[]{"not_used"}) ).thenReturn(getUniqueIdCountCursor()); + when(sqliteDatabase.rawQuery("SELECT json FROM client WHERE baseEntityId = ? ", new String[]{"1b6fca83-26d0-46d2-bfba-254de5c4424a"}) ).thenReturn(getClientJsonObjectCursor()); + when(sqliteDatabase.query("unique_ids", new String[]{"_id", "openmrs_id", "status", "used_by", "synced_by", "created_at", "updated_at"}, "status = ?", new String[]{"not_used"}, null, null, "created_at ASC", "1")).thenReturn(getUniqueIdCursor()); + + DuplicateZeirIdStatus duplicateZeirIdStatus = eventClientRepository.cleanDuplicateMotherIds(); + Assert.assertEquals(DuplicateZeirIdStatus.CLEANED, duplicateZeirIdStatus); + verify(sqliteDatabase, times(1)).rawQuery(eq(DUPLICATES_SQL), any()); + verify(sqliteDatabase, times(1)).insert(eq("client"), eq(null), any()); + } + + public MatrixCursor getDuplicateZeirIdsCursor() { + MatrixCursor cursor = new MatrixCursor(new String[]{"baseEntityId", "zeir_id", "prev_zeir_id"}); + cursor.addRow(new Object[]{"1b6fca83-26d0-46d2-bfba-254de5c4424a", "11320561", "11320561"}); + return cursor; + } + + public MatrixCursor getUniqueIdCountCursor() { + MatrixCursor cursor = new MatrixCursor(new String[]{"count(*)"}); + cursor.addRow(new Object[]{"12"}); + return cursor; + } + + public MatrixCursor getUniqueIdCursor() { + MatrixCursor cursor = new MatrixCursor(new String[]{"_id", "openmrs_id", "status", "used_by", "synced_by", "created_at", "updated_at"}); + cursor.addRow(new Object[]{"1", "11432345", null, null, null, null, null}); + return cursor; + } + + public MatrixCursor getClientJsonObjectCursor() { + String clientString = "{\n" + + " \"type\": \"Client\",\n" + + " \"clientType\": \"mother\",\n" + + " \"dateCreated\": \"2019-11-21T15:29:36.799+07:00\",\n" + + " \"baseEntityId\": \"1b6fca83-26d0-46d2-bfba-254de5c4424a\",\n" + + " \"identifiers\": {\n" + + " \"M_ZEIR_ID\": \"1132056-1\"\n" + + " },\n" + + " \"firstName\": \"Test 2\",\n" + + " \"lastName\": \"Duplicate\",\n" + + " \"birthdate\": \"1970-01-01T14:00:00.000+07:00\",\n" + + " \"birthdateApprox\": true,\n" + + " \"deathdateApprox\": false,\n" + + " \"gender\": \"Male\",\n" + + " \"_id\": \"f187d396-c25e-4dd7-adc8-dc921c1f8ae4\",\n" + + " \"_rev\": \"v1\"\n" + + "}"; + + MatrixCursor cursor = new MatrixCursor(new String[]{"json"}); + cursor.addRow(new Object[]{clientString}); + return cursor; + } + + } \ No newline at end of file diff --git a/opensrp-core/src/test/java/org/smartregister/repository/ZeirIdCleanupRepositoryTest.java b/opensrp-core/src/test/java/org/smartregister/repository/ZeirIdCleanupRepositoryTest.java new file mode 100644 index 000000000..0117630be --- /dev/null +++ b/opensrp-core/src/test/java/org/smartregister/repository/ZeirIdCleanupRepositoryTest.java @@ -0,0 +1,73 @@ +package org.smartregister.repository; + +import static android.preference.PreferenceManager.getDefaultSharedPreferences; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import androidx.test.core.app.ApplicationProvider; + +import net.sqlcipher.MatrixCursor; +import net.sqlcipher.database.SQLiteDatabase; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.powermock.reflect.Whitebox; +import org.robolectric.util.ReflectionHelpers; +import org.smartregister.BaseRobolectricUnitTest; +import org.smartregister.CoreLibrary; +import org.smartregister.repository.ZeirIdCleanupRepository; +import org.smartregister.view.activity.DrishtiApplication; + +import java.util.Map; + +/** + * Created by ndegwamartin on 2019-12-02. + */ +public class ZeirIdCleanupRepositoryTest extends BaseRobolectricUnitTest { + @Mock + private Repository repository; + + @Mock + private SQLiteDatabase sqLiteDatabase; + + private ZeirIdCleanupRepository zeirIdCleanupRepository; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + AllSharedPreferences allSharedPreferences = new AllSharedPreferences(getDefaultSharedPreferences(ApplicationProvider.getApplicationContext())); + ReflectionHelpers.setField(CoreLibrary.getInstance().context(), "allSharedPreferences", allSharedPreferences); + + Whitebox.setInternalState(DrishtiApplication.getInstance(), "repository", repository); + when(repository.getReadableDatabase()).thenReturn(sqLiteDatabase); + when(repository.getWritableDatabase()).thenReturn(sqLiteDatabase); + + zeirIdCleanupRepository = new ZeirIdCleanupRepository(); + } + + @After + public void tearDown() { + Whitebox.setInternalState(DrishtiApplication.getInstance(), "repository", (Repository) null); + } + + @Test + public void getClientsWithDuplicateZeirIdsReturnsReturnsMap() { + when(sqLiteDatabase.rawQuery(anyString(), any())).thenReturn(getZuplicateZeirIdsCursor()); + Map duplicates = zeirIdCleanupRepository.getClientsWithDuplicateZeirIds(); + Assert.assertEquals(2, duplicates.size()); + } + + public MatrixCursor getZuplicateZeirIdsCursor() { + MatrixCursor cursor = new MatrixCursor(new String[]{"baseEntityId", "zeir_id", "prev_zeir_id"}); + cursor.addRow(new Object[]{"1b6fca83-26d0-46d2-bfba-254de5c4424a", "11320561", null}); + cursor.addRow(new Object[]{"951f9ecc-50cf-4af5-ba8f-f2ce18a108b2", "11320561", "11320561"}); + return cursor; + } + +} diff --git a/opensrp-app/src/test/java/org/smartregister/view/contract/ECDetailTest.java b/opensrp-core/src/test/java/org/smartregister/view/contract/ECDetailTest.java similarity index 84% rename from opensrp-app/src/test/java/org/smartregister/view/contract/ECDetailTest.java rename to opensrp-core/src/test/java/org/smartregister/view/contract/ECDetailTest.java index 5574dcd9c..9fb2f3155 100644 --- a/opensrp-app/src/test/java/org/smartregister/view/contract/ECDetailTest.java +++ b/opensrp-core/src/test/java/org/smartregister/view/contract/ECDetailTest.java @@ -1,6 +1,5 @@ package org.smartregister.view.contract; - import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -14,8 +13,8 @@ public class ECDetailTest { private String caseId = "1234-5678-1234"; @Before public void setup(){ - ecDetail = new ECDetail(caseId,"Kogelo","kisumu", "456", true, - "addres1", "sd-card/photos", new ArrayList(),null, null ); + ecDetail = new ECDetail(caseId,"Kogelo","kisumu", "456", true, + "addres1", "sd-card/photos", new ArrayList(),null, null ); } @Test diff --git a/sample/build.gradle b/sample/build.gradle index e94979783..ffdcc3d12 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -82,6 +82,7 @@ dependencies { transitive=true exclude group: 'com.ibm.fhir', module: 'fhir-model' } + implementation 'androidx.work:work-runtime:2.7.1' jarJar 'com.ibm.fhir:fhir-model:4.7.0' diff --git a/sample/src/main/java/org/smartregister/sample/interactor/LoginInteractor.java b/sample/src/main/java/org/smartregister/sample/interactor/LoginInteractor.java index 3cea406d0..9ddfd5d9f 100644 --- a/sample/src/main/java/org/smartregister/sample/interactor/LoginInteractor.java +++ b/sample/src/main/java/org/smartregister/sample/interactor/LoginInteractor.java @@ -1,8 +1,15 @@ package org.smartregister.sample.interactor; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; +import androidx.work.WorkRequest; + +import org.smartregister.job.DuplicateCleanerWorker; import org.smartregister.login.interactor.BaseLoginInteractor; import org.smartregister.view.contract.BaseLoginContract; +import java.util.concurrent.TimeUnit; + /** * Created by ndegwamartin on 08/05/2020. */ @@ -15,5 +22,8 @@ public LoginInteractor(BaseLoginContract.Presenter loginPresenter) { @Override protected void scheduleJobsPeriodically() { //Schedule your jobs here + WorkRequest cleanZeirIdsWorkRequest = new PeriodicWorkRequest.Builder(DuplicateCleanerWorker.class, 15, TimeUnit.MINUTES) + .build(); + WorkManager.getInstance(this.getApplicationContext()).enqueue(cleanZeirIdsWorkRequest); } }