Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into migrate-crashlytics
Browse files Browse the repository at this point in the history
  • Loading branch information
ndegwamartin committed Oct 11, 2022
2 parents 2c94dec + 602e44b commit 2974f25
Show file tree
Hide file tree
Showing 22 changed files with 566 additions and 20 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ _**void generateKey(String keyAlias)**_ For key generation using a Key Alias par

- NB: \* This class depends on `AndroidLegacyCryptography` class and the `AndroidMCryptography` class which both implement the above in different ways depending on the SDK version.
`AndroidLegacyCryptography` has method implementation that are used when the SDK version is less than API level 23

The sample app has examples of how these methods have been implemented. The code for it can be found in
the [MainActivity](https://github.com/opensrp/opensrp-client-core/blob/master/sample/src/main/java/org/smartregister/sample/MainActivity.java) class.

## 2. Data management

Expand Down
Binary file added opensrp-app/jacoco.exec
Binary file not shown.
4 changes: 4 additions & 0 deletions opensrp-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ dependencies {
compileOnly platform('com.google.firebase:firebase-bom:30.0.2')
compileOnly 'com.google.firebase:firebase-crashlytics'
compileOnly 'com.google.firebase:firebase-perf'

def work_version = "2.7.1"
implementation "androidx.work:work-runtime:$work_version"

// Add the dependency for the Performance Monitoring library

//Mockito
Expand Down
11 changes: 11 additions & 0 deletions opensrp-core/src/main/java/org/smartregister/AllConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ public class AllConstants {

public static final String GPS = "gps";

public static final String IDENTIFIERS = "identifiers";

public interface FORCED_LOGOUT {
String MIN_ALLOWED_APP_VERSION_SETTING = "min_allowed_app_version_setting";
Expand Down Expand Up @@ -591,4 +592,14 @@ public class KEY {
public static final String EVENTS = "events";
public static final String CLIENTS = "clients";
}

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";
}

}
10 changes: 10 additions & 0 deletions opensrp-core/src/main/java/org/smartregister/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,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;
Expand Down Expand Up @@ -230,6 +231,7 @@ public class Context {
private ManifestRepository manifestRepository;
private ClientFormRepository clientFormRepository;
private ClientRelationshipRepository clientRelationshipRepository;
private ZeirIdCleanupRepository zeirIdCleanupRepository;

/////////////////////////////////////////////////

Expand Down Expand Up @@ -1251,5 +1253,13 @@ public ClientRelationshipRepository getClientRelationshipRepository() {
return clientRelationshipRepository;
}


public ZeirIdCleanupRepository zeirIdCleanupRepository() {
if (zeirIdCleanupRepository == null) {
zeirIdCleanupRepository = new ZeirIdCleanupRepository();
}
return zeirIdCleanupRepository;
}

///////////////////////////////////////////////////////////////////////////////
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ public byte[] decrypt(byte[] encrypted, String keyAlias) {

Cipher c = Cipher.getInstance(AES_MODE);
c.init(Cipher.DECRYPT_MODE, getKey(keyAlias), new GCMParameterSpec(128, INITIALIZATION_VECTOR));
byte[] decodedBytes = c.doFinal(encrypted);
return decodedBytes;
return c.doFinal(encrypted);
} catch (Exception e) {

Timber.e(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -2346,4 +2356,94 @@ public List<Event> getEventsByTaskIds(Set<String> 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<String, String> 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<String, String> 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<EventClient> 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;
}

}
Original file line number Diff line number Diff line change
@@ -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<String, String> getClientsWithDuplicateZeirIds() {
Map<String, String> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

import android.content.Intent;

import androidx.annotation.VisibleForTesting;

import org.json.JSONArray;
Expand All @@ -27,7 +26,6 @@ public class PullUniqueIdsIntentService extends BaseSyncIntentService {
public static final String IDENTIFIERS = "identifiers";
private UniqueIdRepository uniqueIdRepo;


public PullUniqueIdsIntentService() {
super("PullUniqueOpenMRSUniqueIdsService");
}
Expand Down Expand Up @@ -99,5 +97,4 @@ public int onStartCommand(Intent intent, int flags, int startId) {
protected HTTPAgent getHttpAgent() {
return CoreLibrary.getInstance().context().getHttpAgent();
}

}
Loading

0 comments on commit 2974f25

Please sign in to comment.