Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process skipped clients #506

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b5b5dbb
added broadcast reciever and service to reprocess the client
junaidwarsivd Jan 26, 2023
21b014b
added the corrections in the PR review
junaidwarsivd Jan 31, 2023
16470ec
added UnitTest for GizEventRepository
junaidwarsivd Jan 31, 2023
443625a
added more corrections to the PR changed the job trigger to isComplet…
junaidwarsivd Feb 1, 2023
b322055
corrected the condition
junaidwarsivd Feb 1, 2023
77e7122
removed unused imports
junaidwarsivd Feb 1, 2023
c405d49
reformatted the code
junaidwarsivd Feb 2, 2023
51d9247
updated according to the PR feedback
junaidwarsivd Feb 2, 2023
0402130
changed androidTestImplemenation to testImplementation
junaidwarsivd Feb 2, 2023
d3d2cfd
codacy issues removed
junaidwarsivd Feb 2, 2023
277e50c
added android 12 compatibility
junaidwarsivd Feb 10, 2023
89fb705
Code cleanup
ekigamba Mar 14, 2023
35ca072
Log skipped clients for reprocessing
ekigamba Mar 14, 2023
fa8589f
Process skipped clients events in-order of eventDate after processing…
ekigamba Mar 14, 2023
eb00e3d
Append APK file name with version and variant
ekigamba Dec 15, 2022
e2a4098
Separate oauth client id and secret for different variants
ekigamba Dec 15, 2022
0c4106b
Comment out code in client processor
ekigamba Dec 19, 2022
2f54b4b
Fix commented out code in client processor
ekigamba Mar 14, 2023
5c5cf23
Update ANC version to fix next button in form
ekigamba Mar 16, 2023
b4bb240
Fix OPD Close event processing
ekigamba Mar 20, 2023
6bd8dfa
Mark deceased children as closed in ec_client
ekigamba Mar 20, 2023
a834abb
Update README setup instructions
ekigamba Mar 21, 2023
07c67a8
Remove unused imports
ekigamba Mar 21, 2023
4626b37
Fix PNC registration form cannot move to the next page
ekigamba Mar 24, 2023
7e93ddf
Process death and OPD close events
ekigamba Mar 24, 2023
27b7787
Fix All Clients register search to remove dead clients
ekigamba Mar 24, 2023
502694f
Code cleanup
ekigamba Mar 24, 2023
eeab005
Update versionName and versionCode to 0.4.8 and 48
ekigamba Mar 24, 2023
57aa2d3
Fix Child register search showing deceased clients
ekigamba Mar 26, 2023
af4d112
Update versionName and versionCode to 0.4.9 and 49
ekigamba Mar 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ subprojects {
ext.androidToolsBuildGradle = '4.0.1'
ext.androidBuildToolsVersion = '29.0.3'
ext.androidMinSdkVersion = 18
ext.androidCompileSdkVersion = 29
ext.androidTargetSdkVersion = 29
ext.androidCompileSdkVersion = 31
ext.androidTargetSdkVersion = 31

ext.androidAnnotationsVersion = '3.0.1'
ext.androidAnnotationsAPIVersion = '3.0.1'
Expand Down
9 changes: 8 additions & 1 deletion opensrp-giz-malawi/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ android {
}
configurations.all {
exclude group: 'com.github.lecho', module: 'hellocharts-library'
exclude group: 'com.evernote', module: 'android-job'

}
}

Expand Down Expand Up @@ -376,7 +378,10 @@ dependencies {
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
implementation 'io.reactivex.rxjava2:rxjava:2.1.5'
implementation 'com.evernote:android-job:1.2.6'
implementation('com.github.devv911:android-job:1.4.5') {
exclude group: 'com.google.android', module: 'android'
}
implementation 'androidx.work:work-runtime:2.7.1'
implementation 'com.github.lecho:hellocharts-android:1.5.8@aar'
implementation 'id.zelory:compressor:2.1.1'
implementation('com.google.android.material:material:1.0.0') {
Expand Down Expand Up @@ -404,6 +409,8 @@ dependencies {

implementation "androidx.multidex:multidex:2.0.1"
testImplementation "androidx.work:work-testing:2.7.1"
testImplementation 'androidx.test:runner:1.3.0'
testImplementation 'androidx.test:core:1.3.0'


def robolectricVersion = '4.1'
Expand Down
24 changes: 13 additions & 11 deletions opensrp-giz-malawi/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
android:name=".activity.LoginActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Login"
android:exported="true"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down Expand Up @@ -280,17 +281,18 @@
android:theme="@style/ChildTheme.NoActionBar"
tools:replace="android:theme" />
<!-- END OF PNC ACTIVITIES -->
<service android:name="org.smartregister.immunization.service.intent.VaccineIntentService" />
<service android:name="org.smartregister.immunization.service.intent.RecurringIntentService" />
<service android:name="org.smartregister.immunization.service.intent.VaccineSchedulesUpdateIntentService" />
<service android:name="org.smartregister.growthmonitoring.service.intent.ZScoreRefreshIntentService" />
<service android:name="org.smartregister.growthmonitoring.service.intent.WeightIntentService" />
<service android:name="org.smartregister.growthmonitoring.service.intent.HeightIntentService" />
<service android:name="org.smartregister.sync.intent.PullUniqueIdsIntentService" />
<service android:name="org.smartregister.sync.intent.ValidateIntentService" />
<service android:name="org.smartregister.sync.intent.ExtendedSyncIntentService" />
<service android:name="org.smartregister.sync.intent.SettingsSyncIntentService" />
<service android:name="org.smartregister.sync.intent.SyncIntentService" />
<service android:name="org.smartregister.immunization.service.intent.VaccineIntentService" android:exported="true" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these services set as exported and why is this explicitly declared?

<service android:name="org.smartregister.immunization.service.intent.RecurringIntentService" android:exported="true" />
<service android:name="org.smartregister.immunization.service.intent.VaccineSchedulesUpdateIntentService" android:exported="true"/>
<service android:name="org.smartregister.growthmonitoring.service.intent.ZScoreRefreshIntentService" android:exported="true" />
<service android:name="org.smartregister.growthmonitoring.service.intent.WeightIntentService" android:exported="true"/>
<service android:name="org.smartregister.growthmonitoring.service.intent.HeightIntentService" android:exported="true"/>
<service android:name="org.smartregister.sync.intent.PullUniqueIdsIntentService" android:exported="true" />
<service android:name="org.smartregister.sync.intent.ValidateIntentService" android:exported="true"/>
<service android:name="org.smartregister.sync.intent.ExtendedSyncIntentService" android:exported="true" />
<service android:name="org.smartregister.sync.intent.SettingsSyncIntentService" android:exported="true" />
<service android:name="org.smartregister.sync.intent.SyncIntentService" android:exported="true" />
<service android:name=".service.ReProcessSyncIntentService" android:exported="true"/>

<uses-library
android:name="org.apache.http.legacy"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.smartregister.giz.application;

import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.text.TextUtils;
Expand Down Expand Up @@ -62,6 +63,7 @@
import org.smartregister.giz.job.GizMalawiJobCreator;
import org.smartregister.giz.processor.GizMalawiProcessorForJava;
import org.smartregister.giz.processor.TripleResultProcessor;
import org.smartregister.giz.recievers.GizReprocessSyncStatusReceiver;
import org.smartregister.giz.repository.ChildAlertUpdatedRepository;
import org.smartregister.giz.repository.ClientRegisterTypeRepository;
import org.smartregister.giz.repository.DailyTalliesRepository;
Expand Down Expand Up @@ -364,6 +366,8 @@ public void onCreate() {
initOfflineSchedules();

SyncStatusBroadcastReceiver.init(this);
this.registerReceiver(new GizReprocessSyncStatusReceiver(),
new IntentFilter(SyncStatusBroadcastReceiver.ACTION_SYNC_STATUS));
LocationHelper.init(GizUtils.ALLOWED_LEVELS, GizUtils.DEFAULT_LOCATION_LEVEL);
jsonSpecHelper = new JsonSpecHelper(this);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.smartregister.giz.job;

import android.content.Intent;

import androidx.annotation.NonNull;

import com.evernote.android.job.Job;

import org.jetbrains.annotations.NotNull;
import org.smartregister.AllConstants;
import org.smartregister.giz.service.ReProcessSyncIntentService;
import org.smartregister.job.BaseJob;

public class GIZClientReprocessJob extends BaseJob {

public static final String TAG = "GIZClientReprocessJob";

@NonNull
@NotNull
@Override
protected Result onRunJob(@NonNull @NotNull Job.Params params) {
Intent intent = new Intent(getApplicationContext(), ReProcessSyncIntentService.class);
getApplicationContext().startService(intent);
return params != null && params.getExtras().getBoolean(AllConstants.INTENT_KEY.TO_RESCHEDULE, false) ? Result.RESCHEDULE : Result.SUCCESS;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public Job create(@NonNull String tag) {
return new GizVaccineUpdateJob();
case ImageUploadServiceJob.TAG:
return new ImageUploadServiceJob();
case GIZClientReprocessJob.TAG:
return new GIZClientReprocessJob();

default:
Timber.w(GizMalawiJobCreator.class.getCanonicalName(), "%s is not declared in Job Creator", tag);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.smartregister.giz.recievers;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

import org.smartregister.giz.job.GIZClientReprocessJob;

import timber.log.Timber;

import static org.smartregister.receiver.SyncStatusBroadcastReceiver.EXTRA_COMPLETE_STATUS;

public class GizReprocessSyncStatusReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle data = intent.getExtras();
if (data != null) {
boolean isComplete = data.getBoolean(EXTRA_COMPLETE_STATUS, false);
if (isComplete) {
GIZClientReprocessJob.scheduleJobImmediately(GIZClientReprocessJob.TAG);
Timber.d("Sync complete");
}
}

Timber.d("broadcast Recieved");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the log is too vague and it would be easier to understand if it was a bit more specific on the broadcast that was received

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;

import org.smartregister.giz.util.GizUtils;
import org.smartregister.repository.BaseRepository;

import java.util.ArrayList;
import java.util.HashSet;

import timber.log.Timber;

public class GizEventRepository extends BaseRepository {

public boolean hasEvent(@NonNull String baseEntityId, @NonNull String eventType) {
Expand All @@ -21,4 +27,33 @@ public boolean hasEvent(@NonNull String baseEntityId, @NonNull String eventType)
}
return hasEvent;
}

public void processSkippedClients() {
String missingClientRegister = "SELECT DISTINCT baseEntityId, formSubmissionId AS formSubmissionId FROM event WHERE baseEntityId IN (SELECT baseEntityId FROM client WHERE baseEntityId NOT IN (SELECT DISTINCT base_entity_id FROM client_register_type)) AND eventType LIKE '%Registration%'";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure we need to alias formSubmissionId here

String missingECClient = "SELECT DISTINCT baseEntityId, formSubmissionId AS formSubmissionId FROM event WHERE baseEntityId IN (SELECT baseEntityId FROM client WHERE baseEntityId NOT IN (SELECT DISTINCT base_entity_id FROM ec_client)) AND eventType LIKE '%Registration%'";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check previous comment on formSubmissionId

HashSet<String> formSubmissionIDs = new HashSet<>();
addFormSubmissionIds(formSubmissionIDs, missingClientRegister);
addFormSubmissionIds(formSubmissionIDs, missingECClient);

try {
if (formSubmissionIDs.size() > 0)
GizUtils.initiateEventProcessing(new ArrayList<>(formSubmissionIDs));
} catch (Exception e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be generally catching Exception here

Timber.e(e);
}
}


public void addFormSubmissionIds(HashSet<String> ids, String query) {
SQLiteDatabase database = getReadableDatabase();
Cursor cursor = database.rawQuery(query, null);
int columnIndex = cursor.getColumnIndex("formSubmissionId");
if (cursor.getCount() > 0) {
cursor.moveToFirst();
do {
ids.add(cursor.getString(columnIndex));
} while (cursor.moveToNext());
}
cursor.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.smartregister.giz.service;

import android.app.IntentService;
import android.content.Intent;

import androidx.annotation.Nullable;

import org.smartregister.giz.application.GizMalawiApplication;

public class ReProcessSyncIntentService extends IntentService {

public static final String TAG = "ReProcessSyncIntentService";

public ReProcessSyncIntentService()
{
super(TAG);
}

public ReProcessSyncIntentService(String name) {
super(name);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
GizMalawiApplication.getInstance().gizEventRepository().processSkippedClients();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.smartregister.giz.repository;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.smartregister.giz.util.GizUtils;
import org.smartregister.repository.Repository;
import org.smartregister.view.activity.DrishtiApplication;

import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase;
import java.util.HashSet;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

@RunWith(PowerMockRunner.class)
@PrepareForTest({DrishtiApplication.class})
public class GizEventRepositoryTest {

private GizEventRepository repository;
private static final String BASE_ENTITY_ID = "base_entity_id";
private static final String EVENT_TYPE = "event_type";
private static final String FORM_SUBMISSION_ID = "form_submission_id";

@Mock
private SQLiteDatabase sqLiteDatabase;

@Mock
private GizUtils gizUtils;

@Mock
private Cursor cursor;

@Mock
private Repository baseRepository;

@Mock
private DrishtiApplication drishtiApplication;

@Before
public void setUp() {
repository = new GizEventRepository();
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(DrishtiApplication.class);
when(DrishtiApplication.getInstance()).thenReturn(drishtiApplication);
when(drishtiApplication.getRepository()).thenReturn(baseRepository);
when(baseRepository.getReadableDatabase()).thenReturn(sqLiteDatabase);
}

@Test
public void testHasEvent(){
when(sqLiteDatabase.query(
"event",
new String[]{"baseEntityId"},
"baseEntityId = ? and eventType = ? ",
new String[]{BASE_ENTITY_ID, EVENT_TYPE},
null,
null,
null,
"1"
)).thenReturn(cursor);

when(cursor.getCount()).thenReturn(1);

boolean hasEvent = repository.hasEvent(BASE_ENTITY_ID, EVENT_TYPE);

assertTrue(hasEvent);
}



@Test
public void testAddFormSubmissionIds() {
when(baseRepository.getReadableDatabase()).thenReturn(sqLiteDatabase);
when(sqLiteDatabase.rawQuery("query", null)).thenReturn(cursor);
when(cursor.getCount()).thenReturn(1);
when(cursor.getColumnIndex("formSubmissionId")).thenReturn(0);
when(cursor.moveToFirst()).thenReturn(true);
when(cursor.getString(0)).thenReturn("formSubmissionId");

HashSet<String> formSubmissionIds = new HashSet<>();
repository.addFormSubmissionIds(formSubmissionIds, "query");

assertFalse(formSubmissionIds.isEmpty());
}
}