From a139f341b78047d8ef9a45303d0069e1ab177e1f Mon Sep 17 00:00:00 2001 From: zzainulabidin Date: Thu, 3 Feb 2022 15:41:53 +0500 Subject: [PATCH 1/4] Add Migration for provider_id into family table --- opensrp-chw/build.gradle | 2 +- .../FamilyTableDatabaseMigrationUtils.java | 83 +++++++++++++++++++ .../src/togo/assets/ec_client_fields.json | 7 ++ .../chw/repository/ChwRepositoryFlv.java | 13 +++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/util/FamilyTableDatabaseMigrationUtils.java diff --git a/opensrp-chw/build.gradle b/opensrp-chw/build.gradle index 2c1704d343..27547bcce4 100644 --- a/opensrp-chw/build.gradle +++ b/opensrp-chw/build.gradle @@ -307,7 +307,7 @@ android { buildConfigField "String", 'DEFAULT_LOCATION', '"Village"' buildConfigField "int", "MAX_CONNECTION_TIMEOUT", '10' buildConfigField "int", "MAX_READ_TIMEOUT", '10' - buildConfigField "int", "DATABASE_VERSION", '21' + buildConfigField "int", "DATABASE_VERSION", '22' } liberia { dimension = 'baseDimension' diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/util/FamilyTableDatabaseMigrationUtils.java b/opensrp-chw/src/main/java/org/smartregister/chw/util/FamilyTableDatabaseMigrationUtils.java new file mode 100644 index 0000000000..9a248e25ea --- /dev/null +++ b/opensrp-chw/src/main/java/org/smartregister/chw/util/FamilyTableDatabaseMigrationUtils.java @@ -0,0 +1,83 @@ +package org.smartregister.chw.util; + +import net.sqlcipher.Cursor; +import net.sqlcipher.database.SQLiteDatabase; + +import org.apache.commons.lang3.StringUtils; +import org.json.JSONException; +import org.json.JSONObject; +import org.smartregister.dao.AbstractDao; + +import java.util.ArrayList; + +import timber.log.Timber; + +public class FamilyTableDatabaseMigrationUtils extends AbstractDao { + + public static void fillFamilyTableWithProviderIds(SQLiteDatabase db) { + ArrayList baseEntityIds = getBaseEntityIdsFromFamilyTable(db); + ArrayList jsonLists = getJSONListAgainstBaseEntityIds(db, baseEntityIds); + if (db != null && db.isOpen()) + db.rawExecSQL(buildUpdateQueryForFamilyTableWithBaseEntityIds(jsonLists)); + } + + private static String getDemo() { + return "UPDATE ec_family SET provider_id = (case when base_entity_id = '047e55ec-3710-49bf-8fbd-aa620c687dd8' then 'demo' when base_entity_id = 'fc0d4818-5040-47d4-8077-d7f4c3e021b8' then 'demo1' end)"; + } + + public static String buildUpdateQueryForFamilyTableWithBaseEntityIds(ArrayList jsonLists) { + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append("UPDATE ec_family SET provider_id = (case "); + for (String item : jsonLists) { + try { + JSONObject jsonObject = new JSONObject(item); + String baseEntityId = jsonObject.getString("baseEntityId"); + String providerId = jsonObject.getString("providerId"); + + queryBuilder.append("when base_entity_id = '") + .append(baseEntityId) + .append("' then '").append(providerId).append("' "); + } catch (JSONException e) { + e.printStackTrace(); + } + } + queryBuilder.append(" end)"); + return queryBuilder.toString(); + } + + private static ArrayList getJSONListAgainstBaseEntityIds(SQLiteDatabase db, ArrayList baseEntityIds) { + String _BaseEntityIDs = "('" + StringUtils.join(baseEntityIds, "','") + "')"; + ArrayList JSONLists = new ArrayList<>(); + try { + String query = "SELECT json FROM event WHERE baseEntityId IN " + _BaseEntityIDs; + Cursor cursor = db.rawQuery(query, null); + if (cursor != null) { + while (cursor.moveToNext()) { + JSONLists.add(cursor.getString(cursor.getColumnIndex("json"))); + } + } + return JSONLists; + } catch (Exception ex) { + Timber.e(ex); + } + return JSONLists; + } + + private static ArrayList getBaseEntityIdsFromFamilyTable(SQLiteDatabase db) { + ArrayList baseEntityIds = new ArrayList<>(); + try { + String query = "SELECT base_entity_id FROM ec_family"; + Cursor cursor = db.rawQuery(query, null); + if (cursor != null) { + while (cursor.moveToNext()) { + baseEntityIds.add(cursor.getString(cursor.getColumnIndex("base_entity_id"))); + } + } + return baseEntityIds; + } catch (Exception ex) { + Timber.e(ex); + } + return baseEntityIds; + } + +} diff --git a/opensrp-chw/src/togo/assets/ec_client_fields.json b/opensrp-chw/src/togo/assets/ec_client_fields.json index 447be43358..3dd6dc0b35 100644 --- a/opensrp-chw/src/togo/assets/ec_client_fields.json +++ b/opensrp-chw/src/togo/assets/ec_client_fields.json @@ -10,6 +10,13 @@ "field": "baseEntityId" } }, + { + "column_name": "provider_id", + "type": "Event", + "json_mapping": { + "field": "providerId" + } + }, { "column_name": "unique_id", "type": "Client", diff --git a/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java b/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java index e7fbba4475..4c788870ca 100644 --- a/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java +++ b/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java @@ -21,6 +21,7 @@ import org.smartregister.chw.dao.WashCheckDao; import org.smartregister.chw.domain.PNCHealthFacilityVisitSummary; import org.smartregister.chw.util.Constants; +import org.smartregister.chw.util.FamilyTableDatabaseMigrationUtils; import org.smartregister.chw.util.PNCVisitUtil; import org.smartregister.chw.util.RepositoryUtils; import org.smartregister.chw.util.RepositoryUtilsFlv; @@ -109,6 +110,9 @@ public static void onUpgrade(Context context, SQLiteDatabase db, int oldVersion, case 21: upgradeToVersion21(db); break; + case 22: + upgradeToVersion22(db); + break; default: break; } @@ -424,4 +428,13 @@ private static void upgradeToVersion20(SQLiteDatabase db) { private static void upgradeToVersion21(SQLiteDatabase db) { RepositoryUtils.updateNullEventIds(db); } + + private static void upgradeToVersion22(SQLiteDatabase db) { + try { + db.execSQL("ALTER TABLE ec_family ADD COLUMN provider_id VARCHAR;"); + FamilyTableDatabaseMigrationUtils.fillFamilyTableWithProviderIds(db); + } catch (Exception e) { + Timber.e(e, "upgradeToVersion6"); + } + } } From 4baedf7b99a642d2a01a6cbae89334017cf3bf7f Mon Sep 17 00:00:00 2001 From: zzainulabidin Date: Thu, 3 Feb 2022 17:51:54 +0500 Subject: [PATCH 2/4] Remove unused methods; update log message --- .../chw/util/FamilyTableDatabaseMigrationUtils.java | 3 --- .../org/smartregister/chw/repository/ChwRepositoryFlv.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/util/FamilyTableDatabaseMigrationUtils.java b/opensrp-chw/src/main/java/org/smartregister/chw/util/FamilyTableDatabaseMigrationUtils.java index 9a248e25ea..61bab9207f 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/util/FamilyTableDatabaseMigrationUtils.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/util/FamilyTableDatabaseMigrationUtils.java @@ -21,9 +21,6 @@ public static void fillFamilyTableWithProviderIds(SQLiteDatabase db) { db.rawExecSQL(buildUpdateQueryForFamilyTableWithBaseEntityIds(jsonLists)); } - private static String getDemo() { - return "UPDATE ec_family SET provider_id = (case when base_entity_id = '047e55ec-3710-49bf-8fbd-aa620c687dd8' then 'demo' when base_entity_id = 'fc0d4818-5040-47d4-8077-d7f4c3e021b8' then 'demo1' end)"; - } public static String buildUpdateQueryForFamilyTableWithBaseEntityIds(ArrayList jsonLists) { StringBuilder queryBuilder = new StringBuilder(); diff --git a/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java b/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java index 4c788870ca..5b025af7ef 100644 --- a/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java +++ b/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java @@ -434,7 +434,7 @@ private static void upgradeToVersion22(SQLiteDatabase db) { db.execSQL("ALTER TABLE ec_family ADD COLUMN provider_id VARCHAR;"); FamilyTableDatabaseMigrationUtils.fillFamilyTableWithProviderIds(db); } catch (Exception e) { - Timber.e(e, "upgradeToVersion6"); + Timber.e(e, "upgradeToVersion22"); } } } From 0883685c5b265b283aa062ebb9cd14c5b5c2769a Mon Sep 17 00:00:00 2001 From: zzainulabidin Date: Thu, 17 Mar 2022 17:37:54 +0500 Subject: [PATCH 3/4] Add indicator queries; Add 3rd tab for supervisor indicators --- ...rvisor-reporting-indicator-definitions.yml | 64 +++++++++ .../chw/activity/JobAidsActivity.java | 5 +- .../SupervisorIndicatorsFragment.java | 130 ++++++++++++++++++ ...SupervisorIndicatorsFragmentPresenter.java | 58 ++++++++ .../chw/reporting/ChwReport.java | 14 +- .../chw/util/ReportingConstants.java | 6 + .../src/main/res/layout/activity_job_aids.xml | 6 + opensrp-chw/src/main/res/values/strings.xml | 2 + .../chw/repository/ChwRepositoryFlv.java | 3 +- 9 files changed, 284 insertions(+), 4 deletions(-) create mode 100644 opensrp-chw/src/main/assets/config/supervisor-reporting-indicator-definitions.yml create mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorIndicatorsFragment.java create mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/presenter/SupervisorIndicatorsFragmentPresenter.java diff --git a/opensrp-chw/src/main/assets/config/supervisor-reporting-indicator-definitions.yml b/opensrp-chw/src/main/assets/config/supervisor-reporting-indicator-definitions.yml new file mode 100644 index 0000000000..36f26d6adc --- /dev/null +++ b/opensrp-chw/src/main/assets/config/supervisor-reporting-indicator-definitions.yml @@ -0,0 +1,64 @@ +indicators: + - key: "supervisor_indicator_1_1" + description: "Task Completion Rate for Entire Catchment Area" + indicatorQuery: "SELECT + strftime ( '%m-%Y', sc.created_at ) AS date, + count( DISTINCT provider_id ) AS total_providers, + IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0') as providers_open_task, + IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0') as providers_close_task + FROM schedule_service sc + INNER JOIN ec_family_member fm ON sc.base_entity_id = fm.base_entity_id COLLATE NOCASE + INNER JOIN ec_family f ON fm.relational_id = f.base_entity_id COLLATE NOCASE + GROUP BY strftime ( '%Y-%m', sc.created_at ) + ORDER BY sc.created_at ASC" + + - key: "supervisor_indicator_2_1" + description: "Sync Completion Rate for Entire Catchment Area" + indicatorQuery: "SELECT count(distinct json_extract(json, '$.providerId')) FROM event where syncStatus = 'Synced'" + + - key: "supervisor_indicator_2_2" + description: "Sync Not Completed Rate for Entire Catchment Area" + indicatorQuery: "SELECT count(distinct json_extract(json, '$.providerId')) FROM event where syncStatus = 'Unsynced'" + + - key: "supervisor_indicator_3" + description: "Reporting (Sync Completion) Rate for Entire Catchment Area" + indicatorQuery: "SELECT strftime ( '%m-%Y', sc.created_at ) AS date, + provider_id AS providers, + count(f.base_entity_id) as households, + -- IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0') as open_tasks, + -- IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0') as close_task, + (IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0'))/((count( DISTINCT provider_id )))*100 as open_percetage + -- (IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0'))/((count( DISTINCT provider_id )))*100 as close_percetage + FROM schedule_service sc + INNER JOIN ec_family_member fm ON sc.base_entity_id = fm.base_entity_id COLLATE NOCASE + INNER JOIN ec_family f ON fm.relational_id = f.base_entity_id COLLATE NOCASE + WHERE strftime('%Y-%m',sc.created_at) = strftime('%Y-%m',date('now')) + GROUP BY strftime ( '%Y-%m', sc.created_at ) , provider_id + ORDER BY sc.created_at ASC" + + - key: "supervisor_indicator_4" + description: "Absolute count of tasks remaining incomplete for the month" + indicatorQuery: "SELECT + strftime ( '%m-%Y', sc.created_at ) AS date, + count(f.base_entity_id) as households, + provider_id AS total_providers, + IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0') as open_tasks, + IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0') as close_task, + (IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0'))/((count( DISTINCT provider_id )))*100 as open_percetage, + (IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0'))/((count( DISTINCT provider_id )))*100 as close_percetage + FROM + schedule_service sc + INNER JOIN ec_family_member fm ON sc.base_entity_id = fm.base_entity_id COLLATE NOCASE + INNER JOIN ec_family f ON fm.relational_id = f.base_entity_id COLLATE NOCASE + GROUP BY + strftime ( '%Y-%m', sc.created_at ) , provider_id + ORDER BY + sc.created_at ASC" + + - key: "supervisor_indicator_5" + description: "CHW by date of last sync" + indicatorQuery: "SELECT + strftime('%Y-%m-%d',date(max(updatedAt))) as last_sync_date, + json_extract(json, '$.providerId') as providerId FROM event + where syncStatus = 'Synced' + group by providerId" \ No newline at end of file diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/activity/JobAidsActivity.java b/opensrp-chw/src/main/java/org/smartregister/chw/activity/JobAidsActivity.java index 368a42a7f7..b5ce49a259 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/activity/JobAidsActivity.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/activity/JobAidsActivity.java @@ -28,6 +28,7 @@ import org.smartregister.chw.core.job.ChwIndicatorGeneratingJob; import org.smartregister.chw.fragment.JobAidsDashboardFragment; import org.smartregister.chw.fragment.GuideBooksFragment; +import org.smartregister.chw.fragment.SupervisorIndicatorsFragment; import org.smartregister.chw.listener.JobsAidsBottomNavigationListener; import org.smartregister.chw.util.Utils; import org.smartregister.helper.BottomNavigationHelper; @@ -86,6 +87,8 @@ public Fragment getItem(int position) { case 0: return JobAidsDashboardFragment.newInstance(); case 1: + return SupervisorIndicatorsFragment.newInstance(); + case 2: return GuideBooksFragment.newInstance(); default: return JobAidsDashboardFragment.newInstance(); @@ -95,7 +98,7 @@ public Fragment getItem(int position) { @Override public int getCount() { // Show 3 total pages. - return 2; + return 3; } @Override diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorIndicatorsFragment.java b/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorIndicatorsFragment.java new file mode 100644 index 0000000000..c3c9b5a120 --- /dev/null +++ b/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorIndicatorsFragment.java @@ -0,0 +1,130 @@ +package org.smartregister.chw.fragment; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.AsyncTaskLoader; +import androidx.loader.content.Loader; + +import org.smartregister.chw.R; +import org.smartregister.chw.presenter.JobAidsDashboardFragmentPresenter; +import org.smartregister.chw.reporting.ChwReport; +import org.smartregister.reporting.contract.ReportContract; +import org.smartregister.reporting.domain.IndicatorTally; + +import java.util.List; +import java.util.Map; + +public class SupervisorIndicatorsFragment extends Fragment implements ReportContract.View, LoaderManager.LoaderCallbacks>> { + public static final String TAG = "SupervisorIndicatorsFragment"; + + private static ReportContract.Presenter presenter; + private ViewGroup visualizationsViewGroup; + private ProgressBar progressBar; + private List> indicatorTallies; + + public SupervisorIndicatorsFragment() { + // Required empty public constructor + } + + public static SupervisorIndicatorsFragment newInstance() { + SupervisorIndicatorsFragment fragment = new SupervisorIndicatorsFragment(); + Bundle args = new Bundle(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + presenter = new JobAidsDashboardFragmentPresenter(this); + loadIndicatorTallies(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // Inflate the layout for this fragment + View rootView = inflater.inflate(R.layout.fragment_job_aids_dashboard, container, false); + progressBar = rootView.findViewById(R.id.progress_bar); + visualizationsViewGroup = rootView.findViewById(R.id.dashboard_content); + return rootView; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + } + + @Override + public void onDetach() { + super.onDetach(); + } + + public void loadIndicatorTallies() { + getLoaderManager().initLoader(0, null, this).forceLoad(); + } + + @NonNull + @Override + public Loader>> onCreateLoader(int i, @Nullable Bundle bundle) { + return new ReportIndicatorsLoader(getContext()); + } + + @Override + public void onLoadFinished(@NonNull Loader>> loader, List> indicatorTallies) { + setIndicatorTallies(indicatorTallies); + refreshUI(); + } + + @Override + public void onLoaderReset(@NonNull Loader>> loader) { + // Clean up or release resources + } + + @Override + public void refreshUI() { + buildVisualization(visualizationsViewGroup); + progressBar.setVisibility(View.GONE); + } + + @Override + public void buildVisualization(ViewGroup viewGroup) { + //Refresh view with new indicators + viewGroup.removeAllViews(); + ChwReport.showSupervisorIndicatorVisualisations(viewGroup, indicatorTallies, getActivity()); + } + + public List> getIndicatorTallies() { + return this.indicatorTallies; + } + + public void setIndicatorTallies(List> indicatorTallies) { + this.indicatorTallies = indicatorTallies; + } + + private static class ReportIndicatorsLoader extends AsyncTaskLoader>> { + + private ReportIndicatorsLoader(Context context) { + super(context); + } + + @Nullable + @Override + public List> loadInBackground() { + return presenter.fetchIndicatorsDailytallies(); + } + } +} \ No newline at end of file diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SupervisorIndicatorsFragmentPresenter.java b/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SupervisorIndicatorsFragmentPresenter.java new file mode 100644 index 0000000000..7b7ac5b835 --- /dev/null +++ b/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SupervisorIndicatorsFragmentPresenter.java @@ -0,0 +1,58 @@ +package org.smartregister.chw.presenter; + +import org.smartregister.reporting.contract.ReportContract; +import org.smartregister.reporting.domain.BaseReportIndicatorsModel; +import org.smartregister.reporting.domain.IndicatorQuery; +import org.smartregister.reporting.domain.IndicatorTally; +import org.smartregister.reporting.domain.ReportIndicator; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Map; + +public class JobAidsDashboardFragmentPresenter implements ReportContract.Presenter { + + private WeakReference viewWeakReference; + private ReportContract.Model model; + + public JobAidsDashboardFragmentPresenter(ReportContract.View view) { + this.viewWeakReference = new WeakReference<>(view); + this.model = new BaseReportIndicatorsModel(); + } + + @Override + public void onResume() { + getView().refreshUI(); + } + + @Override + public List> fetchIndicatorsDailytallies() { + return model.getIndicatorsDailyTallies(); + } + + @Override + public void addIndicators(List indicatorList) { + for (ReportIndicator indicator : indicatorList) { + model.addIndicator(indicator); + } + } + + @Override + public void addIndicatorQueries(List indicatorQueryList) { + for (IndicatorQuery indicatorQuery : indicatorQueryList) { + model.addIndicatorQuery(indicatorQuery); + } + } + + @Override + public void scheduleRecurringTallyJob() { + // Handled LoginInteractor#scheduleJobsPeriodically that schedules all CHW related jobs + } + + public ReportContract.View getView() { + if (viewWeakReference != null) { + return viewWeakReference.get(); + } + return null; + } +} diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/reporting/ChwReport.java b/opensrp-chw/src/main/java/org/smartregister/chw/reporting/ChwReport.java index 167c76403f..5c7958768d 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/reporting/ChwReport.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/reporting/ChwReport.java @@ -1,5 +1,8 @@ package org.smartregister.chw.reporting; +import static org.smartregister.chw.util.ReportingConstants.SupervisorIndicatorKeys.COUNT_PROVIDER_SYNCED_COMPLETED; +import static org.smartregister.chw.util.ReportingConstants.SupervisorIndicatorKeys.COUNT_PROVIDER_SYNCED_PENDING; + import android.app.Activity; import android.view.View; import android.view.ViewGroup; @@ -30,7 +33,7 @@ public class ChwReport { */ public static void showIndicatorVisualisations(ViewGroup mainLayout, List> indicatorTallies, Activity context) { // Display order as determined in https://docs.google.com/spreadsheets/d/1q9YiWqjLiToTd0--Q8CbwhBwwcNDspbDxVrUfDm8VGU/edit#gid=315573423 - // ChwChartListener chwChartListener = new ChwChartListener(context); + // ChwChartListener chwChartListener = new ChwChartListener(context); int indicator1String = ChwApplication.getApplicationFlavor().showChildrenUnder5() ? R.string.total_under_5_children_label : R.string.total_under_2_children_label; NumericDisplayModel indicator1 = ReportingUtil.getIndicatorDisplayModel(ReportContract.IndicatorView.CountType.LATEST_COUNT, ReportingConstants.ChildIndicatorKeys.COUNT_CHILDREN_UNDER_5, indicator1String, indicatorTallies); appendView(mainLayout, new NumericIndicatorView(mainLayout.getContext(), indicator1)); @@ -62,7 +65,7 @@ public static void showIndicatorVisualisations(ViewGroup mainLayout, List> indicatorTallies, Activity context) { + // indicator 2 (Reporting (Sync Completion) Rate for Entire Catchment Area) + PieChartSlice pnc_indicator_2_1 = ReportingUtil.getPieChartSlice(ReportContract.IndicatorView.CountType.LATEST_COUNT, COUNT_PROVIDER_SYNCED_COMPLETED, "Sync Completed", mainLayout.getContext().getResources().getColor(R.color.pie_chart_yes_green), indicatorTallies, COUNT_PROVIDER_SYNCED_COMPLETED); + PieChartSlice pnc_indicator_2_2 = ReportingUtil.getPieChartSlice(ReportContract.IndicatorView.CountType.LATEST_COUNT, COUNT_PROVIDER_SYNCED_PENDING, "Sync Pending", mainLayout.getContext().getResources().getColor(R.color.pie_chart_no_red), indicatorTallies, COUNT_PROVIDER_SYNCED_PENDING); + appendView(mainLayout, new PieChartIndicatorView(mainLayout.getContext(), ReportingUtil.getPieChartDisplayModel(ReportingUtil.addPieChartSlices(pnc_indicator_2_1, pnc_indicator_2_2), R.string.supervisor_indicators_2, null, null))); + } + public static void createPncReportViews(ViewGroup mainLayout, List> indicatorTallies) { PieChartSlice pnc_indicator_1_1 = ReportingUtil.getPieChartSlice(ReportContract.IndicatorView.CountType.LATEST_COUNT, ReportingConstants.PncIndicatorKeysHelper.COUNT_WOMEN_DELIVERED_IN_HF, mainLayout.getContext().getResources().getString(R.string.yes), mainLayout.getContext().getResources().getColor(R.color.pie_chart_yes_green), indicatorTallies, ReportingConstants.PncIndicatorKeysHelper.COUNT_WOMEN_DELIVERED_IN_HF); PieChartSlice pnc_indicator_1_2 = ReportingUtil.getPieChartSlice(ReportContract.IndicatorView.CountType.LATEST_COUNT, ReportingConstants.PncIndicatorKeysHelper.COUNT_WOMEN_DELIVERED_ELSEWHERE, mainLayout.getContext().getResources().getString(R.string.no), mainLayout.getContext().getResources().getColor(R.color.pie_chart_no_red), indicatorTallies, ReportingConstants.PncIndicatorKeysHelper.COUNT_WOMEN_DELIVERED_ELSEWHERE); diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/util/ReportingConstants.java b/opensrp-chw/src/main/java/org/smartregister/chw/util/ReportingConstants.java index 0356edb83d..33ebb2d0d2 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/util/ReportingConstants.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/util/ReportingConstants.java @@ -47,6 +47,12 @@ interface AncIndicatorKeys { String COUNT_WOMEN_OVERDUE_IPTPSP = "anc_report_indicator_8_2"; } + interface SupervisorIndicatorKeys { + String TASK_COMPLETION_RATE = "supervisor_indicator_1_1"; + String COUNT_PROVIDER_SYNCED_COMPLETED = "supervisor_indicator_2_1"; + String COUNT_PROVIDER_SYNCED_PENDING = "supervisor_indicator_2_2"; + } + interface ChildIndicatorKeys { String COUNT_CHILDREN_UNDER_5 = "CHW_001"; String DECEASED_CHILDREN_0_11_MONTHS = "CHW_002"; diff --git a/opensrp-chw/src/main/res/layout/activity_job_aids.xml b/opensrp-chw/src/main/res/layout/activity_job_aids.xml index 502b295583..cc7c2d7abf 100644 --- a/opensrp-chw/src/main/res/layout/activity_job_aids.xml +++ b/opensrp-chw/src/main/res/layout/activity_job_aids.xml @@ -85,6 +85,12 @@ android:layout_height="wrap_content" android:text="@string/tab_text_1" /> + + Vitamin A DASHBOARD GUIDEBOOKS + SUPERVISOR Settings Hello World from section: %1$d Updating.... @@ -293,6 +294,7 @@ Postpartum women\'s chosen family planning methods (last 6 months) Newborns (0–28 days) who died in the last year Women up to date with their PNC health facility visits + Sync Completion Rate for Entire Catchment Area PHYSICALLY CHALLENGED Maternal deaths diff --git a/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java b/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java index 5b025af7ef..88abd49a26 100644 --- a/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java +++ b/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java @@ -300,8 +300,9 @@ private static void initializeIndicatorDefinitions(ReportingLibrary reportingLib String childIndicatorsConfigFile = "config/child-reporting-indicator-definitions.yml"; String ancIndicatorConfigFile = "config/anc-reporting-indicator-definitions.yml"; String pncIndicatorConfigFile = "config/pnc-reporting-indicator-definitions.yml"; + String supervisorIndicatorConfigFile = "config/supervisor-reporting-indicator-definitions.yml"; for (String configFile : Collections.unmodifiableList( - Arrays.asList(childIndicatorsConfigFile, ancIndicatorConfigFile, pncIndicatorConfigFile))) { + Arrays.asList(childIndicatorsConfigFile, ancIndicatorConfigFile, pncIndicatorConfigFile, supervisorIndicatorConfigFile))) { reportingLibrary.readConfigFile(configFile, sqLiteDatabase); } } From 9d62c94eedc43bd1c3a8b9d980d1c18a3bf68eb8 Mon Sep 17 00:00:00 2001 From: Allan Date: Tue, 19 Apr 2022 18:17:38 +0300 Subject: [PATCH 4/4] :construction: Implement supervisor indicators dashboard --- opensrp-chw/build.gradle | 22 +-- .../src/liberia/assets/ec_client_fields.json | 7 + .../chw/repository/ChwRepositoryFlv.java | 39 ++++-- ...rvisor-reporting-indicator-definitions.yml | 85 +++++------- .../chw/activity/JobAidsActivity.java | 13 +- .../chw/application/ChwApplication.java | 16 ++- .../application/DefaultChwApplicationFlv.java | 1 + .../fragment/JobAidsDashboardFragment.java | 2 +- .../fragment/SupervisorDashboardFragment.java | 117 ++++++++++++++++ .../SupervisorIndicatorsFragment.java | 130 ------------------ .../interactor/LoginJobSchedulerProvider.java | 12 +- .../smartregister/chw/job/ChwJobCreator.java | 3 + .../SuperVisorDashboardFragmentPresenter.java | 22 +++ ...SupervisorIndicatorsFragmentPresenter.java | 58 -------- .../processor/ChwMultiResultsProcessor.java | 96 +++++++++++++ .../chw/reporting/ChwReport.java | 31 ++++- .../chw/repository/ChwRepository.java | 10 ++ .../org/smartregister/chw/util/Constants.java | 4 + .../chw/util/ReportingConstants.java | 9 +- .../chw/util/SupervisorDashboardUtil.java | 97 +++++++++++++ .../src/main/res/layout/activity_job_aids.xml | 6 - ....xml => fragment_indicators_dashboard.xml} | 0 .../src/main/res/values-sw/strings.xml | 7 + opensrp-chw/src/main/res/values/strings.xml | 12 +- .../chw/repository/ChwRepositoryFlv.java | 17 +++ settings.gradle | 8 +- 26 files changed, 537 insertions(+), 287 deletions(-) create mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorDashboardFragment.java delete mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorIndicatorsFragment.java create mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/presenter/SuperVisorDashboardFragmentPresenter.java delete mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/presenter/SupervisorIndicatorsFragmentPresenter.java create mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/processor/ChwMultiResultsProcessor.java create mode 100644 opensrp-chw/src/main/java/org/smartregister/chw/util/SupervisorDashboardUtil.java rename opensrp-chw/src/main/res/layout/{fragment_job_aids_dashboard.xml => fragment_indicators_dashboard.xml} (100%) diff --git a/opensrp-chw/build.gradle b/opensrp-chw/build.gradle index 27547bcce4..c7cb877a22 100644 --- a/opensrp-chw/build.gradle +++ b/opensrp-chw/build.gradle @@ -293,13 +293,13 @@ android { togo { dimension = 'baseDimension' applicationIdSuffix ".togo" - versionCode 26 - versionName "1.2.7" + versionCode 27 + versionName "1.2.8" 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-stage.smartregister.org/opensrp/"' + buildConfigField "String", 'opensrp_url_debug', '"https://wcaro-tg.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"}' @@ -307,23 +307,23 @@ android { buildConfigField "String", 'DEFAULT_LOCATION', '"Village"' buildConfigField "int", "MAX_CONNECTION_TIMEOUT", '10' buildConfigField "int", "MAX_READ_TIMEOUT", '10' - buildConfigField "int", "DATABASE_VERSION", '22' + buildConfigField "int", "DATABASE_VERSION", '23' } liberia { dimension = 'baseDimension' - versionCode 8 - versionName "1.0.1" + versionCode 9 + versionName "1.1.0" buildConfigField "String", 'opensrp_url', '"https://wcaro-lr.smartregister.org/opensrp/"' buildConfigField "String", 'guidebooks_url', '"https://opensrp.s3.amazonaws.com/media/liberia/"' - buildConfigField "String", 'opensrp_url_debug', '"https://wcaro-stage.smartregister.org/opensrp/"' + buildConfigField "String", 'opensrp_url_debug', '"https://wcaro-lr-preview.smartregister.org/opensrp/"' buildConfigField "String[]", "LOCATION_HIERACHY", '{"Clinic" , "CHSS" , "CHA"}' buildConfigField "String[]", "ALLOWED_LOCATION_LEVELS", '{"Country" , "County" , "District", "Clinics", "CHSS", "Village"}' - buildConfigField "String[]", "ALLOWED_LOCATION_LEVELS_DEBUG", '{"Clinic" , "CHSS" , "CHA"}' + buildConfigField "String[]", "ALLOWED_LOCATION_LEVELS_DEBUG", '{"Country" , "County" , "District", "Clinics", "CHSS", "Village"}' buildConfigField "String", 'DEFAULT_LOCATION', '"Village"' - buildConfigField "String", 'DEFAULT_LOCATION_DEBUG', '"CHA"' + buildConfigField "String", 'DEFAULT_LOCATION_DEBUG', '"Village"' buildConfigField "int", "MAX_CONNECTION_TIMEOUT", '5' buildConfigField "int", "MAX_READ_TIMEOUT", '5' - buildConfigField "int", "DATABASE_VERSION", '8' + buildConfigField "int", "DATABASE_VERSION", '9' } lmh { dimension = 'baseDimension' @@ -348,7 +348,7 @@ android { } dependencies { - implementation('org.smartregister:opensrp-client-chw-core:2.1.3-SNAPSHOT@aar') { + implementation('org.smartregister:opensrp-client-chw-core:2.1.4-alpha-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/liberia/assets/ec_client_fields.json b/opensrp-chw/src/liberia/assets/ec_client_fields.json index 447be43358..4bfa0e18d0 100644 --- a/opensrp-chw/src/liberia/assets/ec_client_fields.json +++ b/opensrp-chw/src/liberia/assets/ec_client_fields.json @@ -108,6 +108,13 @@ "json_mapping": { "field": "entityType" } + }, + { + "column_name": "provider_id", + "type": "Event", + "json_mapping": { + "field": "providerId" + } } ] }, diff --git a/opensrp-chw/src/liberia/java/org/smartregister/chw/repository/ChwRepositoryFlv.java b/opensrp-chw/src/liberia/java/org/smartregister/chw/repository/ChwRepositoryFlv.java index d0b52a8cc2..2f6299e59e 100644 --- a/opensrp-chw/src/liberia/java/org/smartregister/chw/repository/ChwRepositoryFlv.java +++ b/opensrp-chw/src/liberia/java/org/smartregister/chw/repository/ChwRepositoryFlv.java @@ -5,6 +5,7 @@ import net.sqlcipher.database.SQLiteDatabase; import org.smartregister.chw.anc.repository.VisitRepository; +import org.smartregister.chw.application.ChwApplication; import org.smartregister.chw.util.RepositoryUtils; import org.smartregister.domain.db.Column; import org.smartregister.immunization.repository.RecurringServiceRecordRepository; @@ -49,6 +50,9 @@ public static void onUpgrade(Context context, SQLiteDatabase db, int oldVersion, case 8: upgradeToVersion8(db); break; + case 9: + upgradeToVersion9(db); + break; default: break; } @@ -91,14 +95,16 @@ private static void upgradeToVersion2(Context context, SQLiteDatabase db) { db.execSQL(VaccineRepository.UPDATE_TABLE_ADD_CHILD_LOCATION_ID_COL); db.execSQL(RecurringServiceRecordRepository.UPDATE_TABLE_ADD_CHILD_LOCATION_ID_COL); - // setup reporting - ReportingLibrary reportingLibrary = ReportingLibrary.getInstance(); - String childIndicatorsConfigFile = "config/child-reporting-indicator-definitions.yml"; - String ancIndicatorConfigFile = "config/anc-reporting-indicator-definitions.yml"; - String pncIndicatorConfigFile = "config/pnc-reporting-indicator-definitions.yml"; - for (String configFile : Collections.unmodifiableList( - Arrays.asList(childIndicatorsConfigFile, ancIndicatorConfigFile, pncIndicatorConfigFile))) { - reportingLibrary.readConfigFile(configFile, db); + // Set up reporting for CHW users only + if (!((ChwApplication)ChwApplication.getInstance()).isSupervisor()) { + ReportingLibrary reportingLibrary = ReportingLibrary.getInstance(); + String childIndicatorsConfigFile = "config/child-reporting-indicator-definitions.yml"; + String ancIndicatorConfigFile = "config/anc-reporting-indicator-definitions.yml"; + String pncIndicatorConfigFile = "config/pnc-reporting-indicator-definitions.yml"; + for (String configFile : Collections.unmodifiableList( + Arrays.asList(childIndicatorsConfigFile, ancIndicatorConfigFile, pncIndicatorConfigFile))) { + reportingLibrary.readConfigFile(configFile, db); + } } } catch (Exception e) { @@ -142,7 +148,6 @@ private static void upgradeToVersion6(SQLiteDatabase db) { } } - private static void upgradeToVersion7(SQLiteDatabase db) { try { db.execSQL("ALTER TABLE ec_family_member ADD COLUMN marital_status VARCHAR;"); @@ -152,6 +157,20 @@ private static void upgradeToVersion7(SQLiteDatabase db) { } private static void upgradeToVersion8(SQLiteDatabase db) { - RepositoryUtils.updateNullEventIds(db); + RepositoryUtils.updateNullEventIds(db); + } + + private static void upgradeToVersion9(SQLiteDatabase db) { + if (((ChwApplication) ChwApplication.getInstance()).isSupervisor()) { + try { + // setup reporting + ReportingLibrary reportingLibrary = ReportingLibrary.getInstance(); + String supervisorIndicatorDefinitionsFile = "config/supervisor-reporting-indicator-definitions.yml"; + reportingLibrary.readConfigFile(supervisorIndicatorDefinitionsFile, db); + } catch (Exception e) { + Timber.e(e, "upgradeToVersion9"); + } + } + } } \ No newline at end of file diff --git a/opensrp-chw/src/main/assets/config/supervisor-reporting-indicator-definitions.yml b/opensrp-chw/src/main/assets/config/supervisor-reporting-indicator-definitions.yml index 36f26d6adc..acd4105d40 100644 --- a/opensrp-chw/src/main/assets/config/supervisor-reporting-indicator-definitions.yml +++ b/opensrp-chw/src/main/assets/config/supervisor-reporting-indicator-definitions.yml @@ -1,64 +1,47 @@ indicators: - - key: "supervisor_indicator_1_1" - description: "Task Completion Rate for Entire Catchment Area" - indicatorQuery: "SELECT - strftime ( '%m-%Y', sc.created_at ) AS date, - count( DISTINCT provider_id ) AS total_providers, - IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0') as providers_open_task, - IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0') as providers_close_task - FROM schedule_service sc - INNER JOIN ec_family_member fm ON sc.base_entity_id = fm.base_entity_id COLLATE NOCASE - INNER JOIN ec_family f ON fm.relational_id = f.base_entity_id COLLATE NOCASE - GROUP BY strftime ( '%Y-%m', sc.created_at ) - ORDER BY sc.created_at ASC" + - key: "supervisor_catchment_task_completion_rate" + description: "Task Completion Rate for Entire Catchment Area" + indicatorQuery: "" - - key: "supervisor_indicator_2_1" + - key: "supervisor_synced_count" description: "Sync Completion Rate for Entire Catchment Area" indicatorQuery: "SELECT count(distinct json_extract(json, '$.providerId')) FROM event where syncStatus = 'Synced'" - - key: "supervisor_indicator_2_2" + - key: "supervisor_unsynced_count" description: "Sync Not Completed Rate for Entire Catchment Area" indicatorQuery: "SELECT count(distinct json_extract(json, '$.providerId')) FROM event where syncStatus = 'Unsynced'" - - key: "supervisor_indicator_3" - description: "Reporting (Sync Completion) Rate for Entire Catchment Area" - indicatorQuery: "SELECT strftime ( '%m-%Y', sc.created_at ) AS date, - provider_id AS providers, - count(f.base_entity_id) as households, - -- IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0') as open_tasks, - -- IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0') as close_task, - (IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0'))/((count( DISTINCT provider_id )))*100 as open_percetage - -- (IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0'))/((count( DISTINCT provider_id )))*100 as close_percetage - FROM schedule_service sc - INNER JOIN ec_family_member fm ON sc.base_entity_id = fm.base_entity_id COLLATE NOCASE - INNER JOIN ec_family f ON fm.relational_id = f.base_entity_id COLLATE NOCASE - WHERE strftime('%Y-%m',sc.created_at) = strftime('%Y-%m',date('now')) - GROUP BY strftime ( '%Y-%m', sc.created_at ) , provider_id - ORDER BY sc.created_at ASC" + - key: "supervisor_households_with_open_tasks_for_month" + description: "Households with open tasks remaining for the month by CHW" + indicatorQuery: "SELECT f.provider_id as provider, (select count(distinct ecf.base_entity_id) from ec_family ecf where ecf.provider_id = f.provider_id and ecf.is_closed = 0) + as total_households, count(distinct f.base_entity_id) as count + FROM schedule_service sc INNER JOIN ec_family_member fm ON sc.base_entity_id = fm.base_entity_id + COLLATE NOCASE INNER JOIN ec_family f ON fm.relational_id = f.base_entity_id COLLATE NOCASE + WHERE (sc.completion_date IS NULL OR sc.completion_date = '') AND + f.is_closed = 0 AND fm.is_closed = 0 + AND sc.created_at between date('now', 'start of month') and date('now') GROUP BY provider" + isMultiResult: true - - key: "supervisor_indicator_4" - description: "Absolute count of tasks remaining incomplete for the month" + - key: "supervisor_incomplete_tasks_for_month" + description: "Tasks remaining incomplete for the month by CHW" indicatorQuery: "SELECT - strftime ( '%m-%Y', sc.created_at ) AS date, - count(f.base_entity_id) as households, - provider_id AS total_providers, - IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0') as open_tasks, - IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0') as close_task, - (IIF(sc.completion_date IS NULL , count( DISTINCT provider_id ) , '0'))/((count( DISTINCT provider_id )))*100 as open_percetage, - (IIF(sc.completion_date IS NOT NULL , count( DISTINCT provider_id ) , '0'))/((count( DISTINCT provider_id )))*100 as close_percetage - FROM - schedule_service sc - INNER JOIN ec_family_member fm ON sc.base_entity_id = fm.base_entity_id COLLATE NOCASE - INNER JOIN ec_family f ON fm.relational_id = f.base_entity_id COLLATE NOCASE - GROUP BY - strftime ( '%Y-%m', sc.created_at ) , provider_id - ORDER BY - sc.created_at ASC" + CASE WHEN sc.completion_date IS NULL THEN 'open_tasks' + ELSE 'closed_tasks' END tasks, f.provider_id as provider, count(distinct f.base_entity_id) as households, count(*) + FROM + schedule_service sc + INNER JOIN ec_family_member fm ON sc.base_entity_id = fm.base_entity_id COLLATE NOCASE + INNER JOIN ec_family f ON fm.relational_id = f.base_entity_id COLLATE NOCASE + WHERE f.is_closed = 0 AND fm.is_closed = 0 AND sc.created_at between date('now', 'start of month') and date('now') + GROUP BY tasks, provider" + isMultiResult: true - - key: "supervisor_indicator_5" + - key: "supervisor_chw_last_sync" description: "CHW by date of last sync" indicatorQuery: "SELECT - strftime('%Y-%m-%d',date(max(updatedAt))) as last_sync_date, - json_extract(json, '$.providerId') as providerId FROM event - where syncStatus = 'Synced' - group by providerId" \ No newline at end of file + json_extract(json, '$.providerId') as provider, + strftime('%Y-%m-%d',date(max(updatedAt))) as last_sync_date, count(*) as count + FROM event + where syncStatus = 'Synced' + group by provider + order by last_sync_date desc" + isMultiResult: true \ No newline at end of file diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/activity/JobAidsActivity.java b/opensrp-chw/src/main/java/org/smartregister/chw/activity/JobAidsActivity.java index b5ce49a259..557c784992 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/activity/JobAidsActivity.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/activity/JobAidsActivity.java @@ -26,9 +26,9 @@ import org.smartregister.chw.R; import org.smartregister.chw.application.ChwApplication; import org.smartregister.chw.core.job.ChwIndicatorGeneratingJob; -import org.smartregister.chw.fragment.JobAidsDashboardFragment; import org.smartregister.chw.fragment.GuideBooksFragment; -import org.smartregister.chw.fragment.SupervisorIndicatorsFragment; +import org.smartregister.chw.fragment.JobAidsDashboardFragment; +import org.smartregister.chw.fragment.SupervisorDashboardFragment; import org.smartregister.chw.listener.JobsAidsBottomNavigationListener; import org.smartregister.chw.util.Utils; import org.smartregister.helper.BottomNavigationHelper; @@ -85,10 +85,11 @@ public SectionsPagerAdapter(FragmentManager fm) { public Fragment getItem(int position) { switch (position) { case 0: + if (((ChwApplication) ChwApplication.getInstance()).isSupervisor()) { + return SupervisorDashboardFragment.newInstance(); + } return JobAidsDashboardFragment.newInstance(); case 1: - return SupervisorIndicatorsFragment.newInstance(); - case 2: return GuideBooksFragment.newInstance(); default: return JobAidsDashboardFragment.newInstance(); @@ -98,13 +99,15 @@ public Fragment getItem(int position) { @Override public int getCount() { // Show 3 total pages. - return 3; + return 2; } @Override public int getItemPosition(Object object) { if (object instanceof JobAidsDashboardFragment) { ((JobAidsDashboardFragment) object).loadIndicatorTallies(); + } else if ((object instanceof SupervisorDashboardFragment)) { + ((SupervisorDashboardFragment) object).loadIndicatorTallies(); } return super.getItemPosition(object); } diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/application/ChwApplication.java b/opensrp-chw/src/main/java/org/smartregister/chw/application/ChwApplication.java index cf09385601..faca30cada 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/application/ChwApplication.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/application/ChwApplication.java @@ -1,5 +1,7 @@ package org.smartregister.chw.application; +import static org.koin.core.context.GlobalContext.getOrNull; + import android.Manifest; import android.content.Intent; import android.content.IntentFilter; @@ -9,6 +11,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.crashlytics.android.Crashlytics; import com.evernote.android.job.JobManager; import com.mapbox.mapboxsdk.Mapbox; import com.vijay.jsonwizard.NativeFormLibrary; @@ -54,6 +57,7 @@ import org.smartregister.chw.malaria.MalariaLibrary; import org.smartregister.chw.model.NavigationModelFlv; import org.smartregister.chw.pnc.PncLibrary; +import org.smartregister.chw.processor.ChwMultiResultsProcessor; import org.smartregister.chw.referral.ReferralLibrary; import org.smartregister.chw.repository.ChwRepository; import org.smartregister.chw.schedulers.ChwScheduleTaskExecutor; @@ -93,11 +97,10 @@ import java.util.Locale; import java.util.Map; +import io.fabric.sdk.android.Fabric; import io.ona.kujaku.KujakuLibrary; import timber.log.Timber; -import static org.koin.core.context.GlobalContext.getOrNull; - public class ChwApplication extends CoreChwApplication implements SyncStatusBroadcastReceiver.SyncStatusListener, P2pProcessingStatusBroadcastReceiver.StatusUpdate { private static Flavor flavor = new ChwApplicationFlv(); @@ -171,6 +174,7 @@ public CommonFtsObject getCommonFtsObject() { @Override public void onCreate() { + Fabric.with(this, new Crashlytics()); super.onCreate(); mInstance = this; @@ -186,7 +190,6 @@ public void onCreate() { NavigationMenu.setupNavigationMenu(this, new NavigationMenuFlv(), new NavigationModelFlv(), getRegisteredActivities(), flavor.hasP2P()); - initializeLibraries(); // init json helper @@ -256,8 +259,11 @@ private void initializeLibraries() { PncLibrary.init(context, getRepository(), BuildConfig.VERSION_CODE, BuildConfig.DATABASE_VERSION); MalariaLibrary.init(context, getRepository(), BuildConfig.VERSION_CODE, BuildConfig.DATABASE_VERSION); FpLibrary.init(context, getRepository(), BuildConfig.VERSION_CODE, BuildConfig.DATABASE_VERSION); + // Init Reporting library ReportingLibrary.init(context, getRepository(), null, BuildConfig.VERSION_CODE, BuildConfig.DATABASE_VERSION); + ReportingLibrary.getInstance().addMultiResultProcessor(new ChwMultiResultsProcessor()); + GrowthMonitoringConfig growthMonitoringConfig = new GrowthMonitoringConfig(); growthMonitoringConfig.setWeightForHeightZScoreFile("weight_for_height.csv"); GrowthMonitoringLibrary.init(context, getRepository(), BuildConfig.VERSION_CODE, BuildConfig.DATABASE_VERSION, growthMonitoringConfig); @@ -403,6 +409,9 @@ public void onVisitEvent(Visit visit) { ChildAlertService.updateAlerts(visit.getBaseEntityId()); } } + public boolean isSupervisor() { + return "Supervisor".equals(CoreLibrary.getInstance().context().allSharedPreferences().getUserPractitionerRole()); + } public AppExecutors getAppExecutors() { if (appExecutors == null) { @@ -614,6 +623,7 @@ public interface Flavor { Map getFTSSearchMap(); Map getFTSSortMap(); + } } \ No newline at end of file diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/application/DefaultChwApplicationFlv.java b/opensrp-chw/src/main/java/org/smartregister/chw/application/DefaultChwApplicationFlv.java index 81513cd0b0..b6645513b8 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/application/DefaultChwApplicationFlv.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/application/DefaultChwApplicationFlv.java @@ -299,4 +299,5 @@ public Map getFTSSortMap() { public boolean showsPhysicallyDisabledView() { return true; } + } diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/fragment/JobAidsDashboardFragment.java b/opensrp-chw/src/main/java/org/smartregister/chw/fragment/JobAidsDashboardFragment.java index 31da2f9d2d..9e9e44f202 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/fragment/JobAidsDashboardFragment.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/fragment/JobAidsDashboardFragment.java @@ -57,7 +57,7 @@ public void onCreate(Bundle savedInstanceState) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment - View rootView = inflater.inflate(R.layout.fragment_job_aids_dashboard, container, false); + View rootView = inflater.inflate(R.layout.fragment_indicators_dashboard, container, false); progressBar = rootView.findViewById(R.id.progress_bar); visualizationsViewGroup = rootView.findViewById(R.id.dashboard_content); return rootView; diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorDashboardFragment.java b/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorDashboardFragment.java new file mode 100644 index 0000000000..b4b70d7b3c --- /dev/null +++ b/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorDashboardFragment.java @@ -0,0 +1,117 @@ +package org.smartregister.chw.fragment; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import org.smartregister.chw.R; +import org.smartregister.chw.presenter.SuperVisorDashboardFragmentPresenter; +import org.smartregister.chw.reporting.ChwReport; +import org.smartregister.reporting.domain.IndicatorTally; + +import java.util.List; +import java.util.Map; + +import io.reactivex.Observable; +import io.reactivex.Observer; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +public class SupervisorDashboardFragment extends Fragment { + public static final String TAG = "SupervisorIndicatorsFragment"; + private SuperVisorDashboardFragmentPresenter presenter; + + private ViewGroup visualizationsViewGroup; + private ProgressBar progressBar; + + public SupervisorDashboardFragment() { + // Required empty public constructor + } + + public static SupervisorDashboardFragment newInstance() { + SupervisorDashboardFragment fragment = new SupervisorDashboardFragment(); + Bundle args = new Bundle(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + presenter = new SuperVisorDashboardFragmentPresenter(); + loadIndicatorTallies(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // Inflate the layout for this fragment + View rootView = inflater.inflate(R.layout.fragment_indicators_dashboard, container, false); + progressBar = rootView.findViewById(R.id.progress_bar); + visualizationsViewGroup = rootView.findViewById(R.id.dashboard_content); + return rootView; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + } + + @Override + public void onDetach() { + super.onDetach(); + } + + public void loadIndicatorTallies() { + Observable>> observable = Observable.create(e -> { + List> indicatorTallies = presenter.getLatestIndicatorTallies(); + e.onNext(indicatorTallies); + e.onComplete(); + }); + + final Disposable[] disposable = new Disposable[1]; + observable.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer>>() { + @Override + public void onSubscribe(Disposable d) { + disposable[0] = d; + } + + @Override + public void onNext(List> indicatorTallies) { + showIndicatorVisualizations(indicatorTallies); + } + + @Override + public void onError(Throwable e) { + Timber.e(e); + } + + @Override + public void onComplete() { + disposable[0].dispose(); + disposable[0] = null; + } + }); + } + + public void showIndicatorVisualizations(List> indicatorTallies) { + visualizationsViewGroup.removeAllViews(); + ChwReport.showSupervisorIndicatorVisualisations(visualizationsViewGroup, indicatorTallies, getActivity()); + progressBar.setVisibility(View.GONE); + } +} \ No newline at end of file diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorIndicatorsFragment.java b/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorIndicatorsFragment.java deleted file mode 100644 index c3c9b5a120..0000000000 --- a/opensrp-chw/src/main/java/org/smartregister/chw/fragment/SupervisorIndicatorsFragment.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.smartregister.chw.fragment; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ProgressBar; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.AsyncTaskLoader; -import androidx.loader.content.Loader; - -import org.smartregister.chw.R; -import org.smartregister.chw.presenter.JobAidsDashboardFragmentPresenter; -import org.smartregister.chw.reporting.ChwReport; -import org.smartregister.reporting.contract.ReportContract; -import org.smartregister.reporting.domain.IndicatorTally; - -import java.util.List; -import java.util.Map; - -public class SupervisorIndicatorsFragment extends Fragment implements ReportContract.View, LoaderManager.LoaderCallbacks>> { - public static final String TAG = "SupervisorIndicatorsFragment"; - - private static ReportContract.Presenter presenter; - private ViewGroup visualizationsViewGroup; - private ProgressBar progressBar; - private List> indicatorTallies; - - public SupervisorIndicatorsFragment() { - // Required empty public constructor - } - - public static SupervisorIndicatorsFragment newInstance() { - SupervisorIndicatorsFragment fragment = new SupervisorIndicatorsFragment(); - Bundle args = new Bundle(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - presenter = new JobAidsDashboardFragmentPresenter(this); - loadIndicatorTallies(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - View rootView = inflater.inflate(R.layout.fragment_job_aids_dashboard, container, false); - progressBar = rootView.findViewById(R.id.progress_bar); - visualizationsViewGroup = rootView.findViewById(R.id.dashboard_content); - return rootView; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - } - - @Override - public void onDetach() { - super.onDetach(); - } - - public void loadIndicatorTallies() { - getLoaderManager().initLoader(0, null, this).forceLoad(); - } - - @NonNull - @Override - public Loader>> onCreateLoader(int i, @Nullable Bundle bundle) { - return new ReportIndicatorsLoader(getContext()); - } - - @Override - public void onLoadFinished(@NonNull Loader>> loader, List> indicatorTallies) { - setIndicatorTallies(indicatorTallies); - refreshUI(); - } - - @Override - public void onLoaderReset(@NonNull Loader>> loader) { - // Clean up or release resources - } - - @Override - public void refreshUI() { - buildVisualization(visualizationsViewGroup); - progressBar.setVisibility(View.GONE); - } - - @Override - public void buildVisualization(ViewGroup viewGroup) { - //Refresh view with new indicators - viewGroup.removeAllViews(); - ChwReport.showSupervisorIndicatorVisualisations(viewGroup, indicatorTallies, getActivity()); - } - - public List> getIndicatorTallies() { - return this.indicatorTallies; - } - - public void setIndicatorTallies(List> indicatorTallies) { - this.indicatorTallies = indicatorTallies; - } - - private static class ReportIndicatorsLoader extends AsyncTaskLoader>> { - - private ReportIndicatorsLoader(Context context) { - super(context); - } - - @Nullable - @Override - public List> loadInBackground() { - return presenter.fetchIndicatorsDailytallies(); - } - } -} \ No newline at end of file diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/interactor/LoginJobSchedulerProvider.java b/opensrp-chw/src/main/java/org/smartregister/chw/interactor/LoginJobSchedulerProvider.java index 7f5637d6ea..e24dd783ec 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/interactor/LoginJobSchedulerProvider.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/interactor/LoginJobSchedulerProvider.java @@ -15,6 +15,7 @@ import org.smartregister.job.PlanIntentServiceJob; import org.smartregister.job.PullUniqueIdsServiceJob; import org.smartregister.job.SyncLocationsByLevelAndTagsServiceJob; +import org.smartregister.job.SyncPractitionersByIdAndRoleJob; import org.smartregister.job.SyncServiceJob; import org.smartregister.job.SyncTaskServiceJob; @@ -52,7 +53,12 @@ public void scheduleJobsPeriodically() { StockUsageReportJob.scheduleJob(StockUsageReportJob.TAG, TimeUnit.MINUTES.toMinutes(BuildConfig.STOCK_USAGE_REPORT_MINUTES), getFlexValue(BuildConfig.STOCK_USAGE_REPORT_MINUTES)); if (BuildConfig.USE_UNIFIED_REFERRAL_APPROACH) - DocumentConfigurationServiceJob.scheduleJob(DocumentConfigurationServiceJob.TAG,TimeUnit.MINUTES.toMinutes(BuildConfig.DATA_SYNC_DURATION_MINUTES), getFlexValue(BuildConfig.DATA_SYNC_DURATION_MINUTES)); + DocumentConfigurationServiceJob.scheduleJob(DocumentConfigurationServiceJob.TAG, TimeUnit.MINUTES.toMinutes(BuildConfig.DATA_SYNC_DURATION_MINUTES), getFlexValue(BuildConfig.DATA_SYNC_DURATION_MINUTES)); + + if (((ChwApplication) ChwApplication.getInstance()).isSupervisor()) { + SyncPractitionersByIdAndRoleJob.scheduleJob(SyncPractitionersByIdAndRoleJob.TAG, TimeUnit.MINUTES.toMinutes(BuildConfig.DATA_SYNC_DURATION_MINUTES), getFlexValue(BuildConfig + .DATA_SYNC_DURATION_MINUTES)); + } } @Override @@ -79,6 +85,10 @@ public void scheduleJobsImmediately() { if (ChwApplication.getApplicationFlavor().hasServiceReport()) ChwIndicatorGeneratingJob.scheduleJobImmediately(ChwIndicatorGeneratingJob.TAG); + + if (((ChwApplication) ChwApplication.getInstance()).isSupervisor()) { + SyncPractitionersByIdAndRoleJob.scheduleJobImmediately(SyncPractitionersByIdAndRoleJob.TAG); + } } @Override diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/job/ChwJobCreator.java b/opensrp-chw/src/main/java/org/smartregister/chw/job/ChwJobCreator.java index 3bd73769b6..ec22ea9098 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/job/ChwJobCreator.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/job/ChwJobCreator.java @@ -19,6 +19,7 @@ import org.smartregister.job.P2pServiceJob; import org.smartregister.job.PullUniqueIdsServiceJob; import org.smartregister.job.SyncLocationsByLevelAndTagsServiceJob; +import org.smartregister.job.SyncPractitionersByIdAndRoleJob; import org.smartregister.job.SyncServiceJob; import org.smartregister.job.SyncTaskServiceJob; import org.smartregister.job.ValidateSyncDataServiceJob; @@ -64,6 +65,8 @@ public Job create(@NonNull String tag) { return new StockUsageReportJob(); case DocumentConfigurationServiceJob.TAG: return new DocumentConfigurationServiceJob(DocumentConfigurationIntentService.class); + case SyncPractitionersByIdAndRoleJob.TAG: + return new SyncPractitionersByIdAndRoleJob(); //TODO uncomment to enable plans /*case PlanIntentServiceJob.TAG: return new PlanIntentServiceJob();*/ diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SuperVisorDashboardFragmentPresenter.java b/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SuperVisorDashboardFragmentPresenter.java new file mode 100644 index 0000000000..e6c0b455c5 --- /dev/null +++ b/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SuperVisorDashboardFragmentPresenter.java @@ -0,0 +1,22 @@ +package org.smartregister.chw.presenter; + +import org.smartregister.reporting.contract.ReportContract; +import org.smartregister.reporting.domain.BaseReportIndicatorsModel; +import org.smartregister.reporting.domain.IndicatorTally; + +import java.util.List; +import java.util.Map; + + +public class SuperVisorDashboardFragmentPresenter { + private ReportContract.Model model; + + public SuperVisorDashboardFragmentPresenter() { + this.model = new BaseReportIndicatorsModel(); + } + + public List> getLatestIndicatorTallies() { + return model.getLatestIndicatorTallies(); + } + +} diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SupervisorIndicatorsFragmentPresenter.java b/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SupervisorIndicatorsFragmentPresenter.java deleted file mode 100644 index 7b7ac5b835..0000000000 --- a/opensrp-chw/src/main/java/org/smartregister/chw/presenter/SupervisorIndicatorsFragmentPresenter.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.smartregister.chw.presenter; - -import org.smartregister.reporting.contract.ReportContract; -import org.smartregister.reporting.domain.BaseReportIndicatorsModel; -import org.smartregister.reporting.domain.IndicatorQuery; -import org.smartregister.reporting.domain.IndicatorTally; -import org.smartregister.reporting.domain.ReportIndicator; - -import java.lang.ref.WeakReference; -import java.util.List; -import java.util.Map; - -public class JobAidsDashboardFragmentPresenter implements ReportContract.Presenter { - - private WeakReference viewWeakReference; - private ReportContract.Model model; - - public JobAidsDashboardFragmentPresenter(ReportContract.View view) { - this.viewWeakReference = new WeakReference<>(view); - this.model = new BaseReportIndicatorsModel(); - } - - @Override - public void onResume() { - getView().refreshUI(); - } - - @Override - public List> fetchIndicatorsDailytallies() { - return model.getIndicatorsDailyTallies(); - } - - @Override - public void addIndicators(List indicatorList) { - for (ReportIndicator indicator : indicatorList) { - model.addIndicator(indicator); - } - } - - @Override - public void addIndicatorQueries(List indicatorQueryList) { - for (IndicatorQuery indicatorQuery : indicatorQueryList) { - model.addIndicatorQuery(indicatorQuery); - } - } - - @Override - public void scheduleRecurringTallyJob() { - // Handled LoginInteractor#scheduleJobsPeriodically that schedules all CHW related jobs - } - - public ReportContract.View getView() { - if (viewWeakReference != null) { - return viewWeakReference.get(); - } - return null; - } -} diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/processor/ChwMultiResultsProcessor.java b/opensrp-chw/src/main/java/org/smartregister/chw/processor/ChwMultiResultsProcessor.java new file mode 100644 index 0000000000..17dcfb4124 --- /dev/null +++ b/opensrp-chw/src/main/java/org/smartregister/chw/processor/ChwMultiResultsProcessor.java @@ -0,0 +1,96 @@ +package org.smartregister.chw.processor; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import org.smartregister.chw.util.Constants; +import org.smartregister.reporting.domain.CompositeIndicatorTally; +import org.smartregister.reporting.domain.IndicatorTally; +import org.smartregister.reporting.domain.MultiValueIndicatorTally; +import org.smartregister.reporting.exception.MultiResultProcessorException; +import org.smartregister.reporting.processor.MultiResultProcessor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This processor is able to processor queries where the first, second and third column returned are indicator groupings + * eg. vaccine as the first column, gender as the second column and provider as the third column while the fourth column contains the count. + * The three indicator groups should be a string or have default affinity to {@link android.database.Cursor}.FIELD_TYPE_STRING + * as described https://www.sqlite.org/datatype3.html. + *

+ * The fourth column should have affinity to either Cursor.FIELD_TYPE_INTEGER or Cursor.FIELD_TYPE_FLOAT in SQLite + *

+ * Created by Allan Onchuru - aonchuru@ona.io on 31-March-2022 + */ + +public class ChwMultiResultsProcessor implements MultiResultProcessor { + + @Override + public boolean canProcess(int columns, @NonNull String[] columnNames) { + return (columns == 4 && columnNames.length == 4 && columnNames[3].contains("count")) || + (columns == 3 && columnNames.length == 3 && columnNames[2].contains("count")); + } + + @NonNull + @Override + public List processMultiResultTally(@NonNull CompositeIndicatorTally compositeIndicatorTally) throws MultiResultProcessorException { + ArrayList compositeTallies = new Gson().fromJson(compositeIndicatorTally.getValueSet(), new TypeToken>() { + }.getType()); + + // Column names + Object[] compositeTallyColumns = compositeTallies.get(0); + // Remove the column names from processing + compositeTallies.remove(0); + + List tallies = new ArrayList<>(); + + for (Object[] compositeTally : compositeTallies) { + HashMap multiValuesMap = new HashMap<>(); + MultiValueIndicatorTally indicatorTally = new MultiValueIndicatorTally(); + indicatorTally.setCreatedAt(compositeIndicatorTally.getCreatedAt()); + indicatorTally.setGrouping(compositeIndicatorTally.getGrouping()); + + if (compositeTally.length == 4) { + indicatorTally.setIndicatorCode(compositeIndicatorTally.getIndicatorCode() + + Constants.MultiResultProcessor.GROUPING_SEPARATOR + compositeTally[0] + + Constants.MultiResultProcessor.GROUPING_SEPARATOR + compositeTally[1]); + // Add other values of interest + multiValuesMap.put(compositeTallyColumns[1].toString(), compositeTally[1].toString()); + multiValuesMap.put(compositeTallyColumns[2].toString(), compositeTally[2].toString()); + indicatorTally.setMultiValuesMap(multiValuesMap); + + Object indicatorValue = compositeTally[3]; + if (indicatorValue instanceof Integer) { + indicatorTally.setCount((int) indicatorValue); + } else if (indicatorValue instanceof Double) { + indicatorTally.setCount(((Double) indicatorValue).floatValue()); + } else { + throw new MultiResultProcessorException(indicatorValue, compositeIndicatorTally); + } + + tallies.add(indicatorTally); + } else if (compositeTally.length == 3) { + indicatorTally.setIndicatorCode(compositeIndicatorTally.getIndicatorCode() + + Constants.MultiResultProcessor.GROUPING_SEPARATOR + compositeTally[0]); + // Add other values of interest + multiValuesMap.put(compositeTallyColumns[1].toString(), compositeTally[1].toString()); + indicatorTally.setMultiValuesMap(multiValuesMap); + + Object indicatorValue = compositeTally[2]; + if (indicatorValue instanceof Integer) { + indicatorTally.setCount((int) indicatorValue); + } else if (indicatorValue instanceof Double) { + indicatorTally.setCount(((Double) indicatorValue).floatValue()); + } else { + throw new MultiResultProcessorException(indicatorValue, compositeIndicatorTally); + } + tallies.add(indicatorTally); + } + } + return tallies; + } +} diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/reporting/ChwReport.java b/opensrp-chw/src/main/java/org/smartregister/chw/reporting/ChwReport.java index 5c7958768d..5c420e217d 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/reporting/ChwReport.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/reporting/ChwReport.java @@ -11,14 +11,18 @@ import org.smartregister.chw.application.ChwApplication; import org.smartregister.chw.listener.ChwChartListener; import org.smartregister.chw.util.ReportingConstants; +import org.smartregister.chw.util.SupervisorDashboardUtil; import org.smartregister.reporting.contract.ReportContract; import org.smartregister.reporting.domain.IndicatorTally; import org.smartregister.reporting.domain.PieChartSlice; +import org.smartregister.reporting.domain.TabularVisualization; import org.smartregister.reporting.model.NumericDisplayModel; import org.smartregister.reporting.util.ReportingUtil; import org.smartregister.reporting.view.NumericIndicatorView; import org.smartregister.reporting.view.PieChartIndicatorView; +import org.smartregister.reporting.view.ReportingTableView; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -76,10 +80,29 @@ public static void showIndicatorVisualisations(ViewGroup mainLayout, List> indicatorTallies, Activity context) { - // indicator 2 (Reporting (Sync Completion) Rate for Entire Catchment Area) - PieChartSlice pnc_indicator_2_1 = ReportingUtil.getPieChartSlice(ReportContract.IndicatorView.CountType.LATEST_COUNT, COUNT_PROVIDER_SYNCED_COMPLETED, "Sync Completed", mainLayout.getContext().getResources().getColor(R.color.pie_chart_yes_green), indicatorTallies, COUNT_PROVIDER_SYNCED_COMPLETED); - PieChartSlice pnc_indicator_2_2 = ReportingUtil.getPieChartSlice(ReportContract.IndicatorView.CountType.LATEST_COUNT, COUNT_PROVIDER_SYNCED_PENDING, "Sync Pending", mainLayout.getContext().getResources().getColor(R.color.pie_chart_no_red), indicatorTallies, COUNT_PROVIDER_SYNCED_PENDING); - appendView(mainLayout, new PieChartIndicatorView(mainLayout.getContext(), ReportingUtil.getPieChartDisplayModel(ReportingUtil.addPieChartSlices(pnc_indicator_2_1, pnc_indicator_2_2), R.string.supervisor_indicators_2, null, null))); + // Sync completion rate ror entire catchment area + PieChartSlice chartSlice1 = ReportingUtil.getPieChartSlice(ReportContract.IndicatorView.CountType.LATEST_COUNT, COUNT_PROVIDER_SYNCED_COMPLETED, "Sync Completed", mainLayout.getContext().getResources().getColor(R.color.pie_chart_yes_green), indicatorTallies, COUNT_PROVIDER_SYNCED_COMPLETED); + PieChartSlice chartSlice2 = ReportingUtil.getPieChartSlice(ReportContract.IndicatorView.CountType.LATEST_COUNT, COUNT_PROVIDER_SYNCED_PENDING, "Sync Pending", mainLayout.getContext().getResources().getColor(R.color.pie_chart_no_red), indicatorTallies, COUNT_PROVIDER_SYNCED_PENDING); + appendView(mainLayout, new PieChartIndicatorView(mainLayout.getContext(), ReportingUtil.getPieChartDisplayModel(ReportingUtil.addPieChartSlices(chartSlice1, chartSlice2), R.string.catchment_sync_completion_rate_indicator, null, null))); + + // Households with open tasks + List tableHeaderList = Arrays.asList("", "", ""); + int tableTitleStringResource = R.string.households_with_open_tasks_indicator; + List houseHoldsData = SupervisorDashboardUtil.getHouseHoldsWithIncompleteTasksData(indicatorTallies, context); + TabularVisualization tabularVisualization = new TabularVisualization(tableTitleStringResource, tableHeaderList, houseHoldsData, true); + mainLayout.addView(new ReportingTableView(mainLayout.getContext(), tabularVisualization).createView()); + + // Tasks remaining incomplete for the month by CHW + tableTitleStringResource = R.string.incomplete_tasks_indicator; + List houseHoldsWithOpenTasksData = SupervisorDashboardUtil.getIncompleteTasksData(indicatorTallies, context); + tabularVisualization = new TabularVisualization(tableTitleStringResource, tableHeaderList, houseHoldsWithOpenTasksData, true); + mainLayout.addView(new ReportingTableView(mainLayout.getContext(), tabularVisualization).createView()); + + // Last Sync by Supervisor + tableTitleStringResource = R.string.last_sync_date_by_chw_indicator; + List chwLastSyncData = SupervisorDashboardUtil.getLastSyncByChwData(indicatorTallies); + tabularVisualization = new TabularVisualization(tableTitleStringResource, tableHeaderList, chwLastSyncData, true); + mainLayout.addView(new ReportingTableView(mainLayout.getContext(), tabularVisualization).createView()); } public static void createPncReportViews(ViewGroup mainLayout, List> indicatorTallies) { diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/repository/ChwRepository.java b/opensrp-chw/src/main/java/org/smartregister/chw/repository/ChwRepository.java index 063a0d9a01..6429f80f50 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/repository/ChwRepository.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/repository/ChwRepository.java @@ -6,8 +6,10 @@ import org.smartregister.AllConstants; import org.smartregister.chw.BuildConfig; +import org.smartregister.chw.application.ChwApplication; import org.smartregister.chw.core.application.CoreChwApplication; import org.smartregister.chw.core.repository.CoreChwRepository; +import org.smartregister.repository.PractitionerRepository; import timber.log.Timber; @@ -19,6 +21,14 @@ public ChwRepository(Context context, org.smartregister.Context openSRPContext) this.context = context; } + @Override + public void onCreate(SQLiteDatabase database) { + super.onCreate(database); + // Add Practitioner table + if (((ChwApplication) (ChwApplication.getInstance())).isSupervisor()) + PractitionerRepository.createTable(database); + } + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Timber.w(CoreChwRepository.class.getName(), "Upgrading database from version " diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/util/Constants.java b/opensrp-chw/src/main/java/org/smartregister/chw/util/Constants.java index 91668f2127..8224cb5573 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/util/Constants.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/util/Constants.java @@ -53,6 +53,10 @@ public static class AncHomeVisitUtil { public static String getDeliveryKitReceived() { return Utils.getLocalForm(DELIVERY_KIT_RECEIVED, JSON_FORM.locale, JSON_FORM.assetManager); } + } + public interface MultiResultProcessor { + String GROUPING_SEPARATOR = "_"; } + } diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/util/ReportingConstants.java b/opensrp-chw/src/main/java/org/smartregister/chw/util/ReportingConstants.java index 33ebb2d0d2..e6dcb59f1c 100644 --- a/opensrp-chw/src/main/java/org/smartregister/chw/util/ReportingConstants.java +++ b/opensrp-chw/src/main/java/org/smartregister/chw/util/ReportingConstants.java @@ -48,9 +48,12 @@ interface AncIndicatorKeys { } interface SupervisorIndicatorKeys { - String TASK_COMPLETION_RATE = "supervisor_indicator_1_1"; - String COUNT_PROVIDER_SYNCED_COMPLETED = "supervisor_indicator_2_1"; - String COUNT_PROVIDER_SYNCED_PENDING = "supervisor_indicator_2_2"; + String COUNT_PROVIDER_SYNCED_COMPLETED = "supervisor_synced_count"; + String COUNT_PROVIDER_SYNCED_PENDING = "supervisor_unsynced_count"; + String SUPERVISOR_HOUSEHOLDS_WITH_OPEN_TASKS = "supervisor_households_with_open_tasks_for_month"; + String SUPERVISOR_TASK_COMPLETION_OPEN_TASKS = "supervisor_incomplete_tasks_for_month_open_tasks"; + String SUPERVISOR_TASK_COMPLETION_CLOSED_TASKS = "supervisor_incomplete_tasks_for_month_closed_tasks"; + String SUPERVISOR_LAST_SYNC_BY_CHW = "supervisor_chw_last_sync"; } interface ChildIndicatorKeys { diff --git a/opensrp-chw/src/main/java/org/smartregister/chw/util/SupervisorDashboardUtil.java b/opensrp-chw/src/main/java/org/smartregister/chw/util/SupervisorDashboardUtil.java new file mode 100644 index 0000000000..e999946933 --- /dev/null +++ b/opensrp-chw/src/main/java/org/smartregister/chw/util/SupervisorDashboardUtil.java @@ -0,0 +1,97 @@ +package org.smartregister.chw.util; + +import android.app.Activity; + +import org.apache.commons.lang3.tuple.Pair; +import org.smartregister.chw.R; +import org.smartregister.reporting.domain.IndicatorTally; +import org.smartregister.reporting.domain.MultiValueIndicatorTally; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SupervisorDashboardUtil { + + public static List getHouseHoldsWithIncompleteTasksData(List> indicatorTallies, Activity context) { + List tableDataList = new ArrayList<>(); + int totalHouseholds; + int houseHoldsWithOpenTasks; + int ratio; + for (Map indicatorTallyMap : indicatorTallies) { + for (String key : indicatorTallyMap.keySet()) { + + if (key.contains(ReportingConstants.SupervisorIndicatorKeys.SUPERVISOR_HOUSEHOLDS_WITH_OPEN_TASKS)) { + totalHouseholds = (int)Double.parseDouble(((MultiValueIndicatorTally)indicatorTallyMap.get(key)).getMultiValuesMap().get("total_households")); + houseHoldsWithOpenTasks = indicatorTallyMap.get(key).getCount(); + ratio = (totalHouseholds > 0 && houseHoldsWithOpenTasks > 0) ? (houseHoldsWithOpenTasks / totalHouseholds) * 100 : 0; + tableDataList.add(getProvider(key)); + tableDataList.add(ratio + "%"); + tableDataList.add(String.format("%s %s", houseHoldsWithOpenTasks, context.getString(R.string.households))); + } + } + } + return tableDataList; + } + + public static List getIncompleteTasksData(List> indicatorTallies, Activity context) { + int closedTasksCount = 0; + int openTasksCount = 0; + String provider = null; + Map> chaMap = new HashMap<>(); + for (Map indicatorTallyMap : indicatorTallies) { + for (String key : indicatorTallyMap.keySet()) { + if (key.contains(ReportingConstants.SupervisorIndicatorKeys.SUPERVISOR_TASK_COMPLETION_OPEN_TASKS)) { + provider = getProvider(key); + openTasksCount = indicatorTallyMap.get(key).getCount(); + if (chaMap.containsKey(provider)) { + closedTasksCount = chaMap.get(provider).getLeft() != null ? chaMap.get(provider).getLeft() : 0; + } + + } else if (key.contains(ReportingConstants.SupervisorIndicatorKeys.SUPERVISOR_TASK_COMPLETION_CLOSED_TASKS)) { + provider = getProvider(key); + closedTasksCount = indicatorTallyMap.get(key).getCount(); + if (chaMap.containsKey(provider)) { + openTasksCount = chaMap.get(provider).getRight() != null ? chaMap.get(provider).getRight() : 0; + } + } + if (provider != null) + chaMap.put(provider, Pair.of(closedTasksCount, openTasksCount)); + } + } + int totalTasks; + int ratio; + List tableDataList = new ArrayList<>(); + for (Map.Entry> entry : chaMap.entrySet()) { + closedTasksCount = entry.getValue().getLeft(); + openTasksCount = entry.getValue().getRight(); + totalTasks = closedTasksCount + openTasksCount; + ratio = (closedTasksCount > 0 && openTasksCount > 0) ? (openTasksCount / totalTasks) * 100 : 0; + tableDataList.add(entry.getKey().toUpperCase()); + tableDataList.add(ratio + "%"); + tableDataList.add(String.format("%s %s",openTasksCount, context.getString(R.string.tasks))); + + } + return tableDataList; + } + + public static List getLastSyncByChwData(List> indicatorTallies) { + List tableDataList = new ArrayList<>(); + for (Map indicatorTallyMap : indicatorTallies) { + for (String key : indicatorTallyMap.keySet()) { + if (key.contains(ReportingConstants.SupervisorIndicatorKeys.SUPERVISOR_LAST_SYNC_BY_CHW)) { + tableDataList.add(getProvider(key).toUpperCase()); + tableDataList.add(""); // Empty center column (spacer) + tableDataList.add(((MultiValueIndicatorTally)indicatorTallyMap.get(key)).getMultiValuesMap().get("last_sync_date")); + } + } + } + return tableDataList; + } + + private static String getProvider(String indicatorKey) { + String[] indicatorKeywords = indicatorKey.split(Constants.MultiResultProcessor.GROUPING_SEPARATOR); + return indicatorKeywords[indicatorKeywords.length - 1]; + } +} diff --git a/opensrp-chw/src/main/res/layout/activity_job_aids.xml b/opensrp-chw/src/main/res/layout/activity_job_aids.xml index cc7c2d7abf..502b295583 100644 --- a/opensrp-chw/src/main/res/layout/activity_job_aids.xml +++ b/opensrp-chw/src/main/res/layout/activity_job_aids.xml @@ -85,12 +85,6 @@ android:layout_height="wrap_content" android:text="@string/tab_text_1" /> - - Kaya ya %1$s %2$s Imeumbwa: %s + + Sync Completion Rate for Entire Catchment Area + Households with open tasks remaining for the month by CHW + Tasks remaining incomplete for the month by CHW + Last sync by CHW + tasks + households Mabadiliko yoyote uliyofanya yatapotea diff --git a/opensrp-chw/src/main/res/values/strings.xml b/opensrp-chw/src/main/res/values/strings.xml index 88b0c1c628..29b0722674 100644 --- a/opensrp-chw/src/main/res/values/strings.xml +++ b/opensrp-chw/src/main/res/values/strings.xml @@ -37,7 +37,6 @@ Vitamin A DASHBOARD GUIDEBOOKS - SUPERVISOR Settings Hello World from section: %1$d Updating.... @@ -294,7 +293,16 @@ Postpartum women\'s chosen family planning methods (last 6 months) Newborns (0–28 days) who died in the last year Women up to date with their PNC health facility visits - Sync Completion Rate for Entire Catchment Area + + + Sync Completion Rate for Entire Catchment Area + Households with open tasks remaining for the month by CHW + Tasks remaining incomplete for the month by CHW + Last sync by CHW + tasks + households + + PHYSICALLY CHALLENGED Maternal deaths diff --git a/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java b/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java index 88abd49a26..4ab96fe462 100644 --- a/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java +++ b/opensrp-chw/src/togo/java/org/smartregister/chw/repository/ChwRepositoryFlv.java @@ -12,6 +12,7 @@ import org.smartregister.chw.anc.repository.VisitDetailsRepository; import org.smartregister.chw.anc.repository.VisitRepository; import org.smartregister.chw.anc.util.NCUtils; +import org.smartregister.chw.application.ChwApplication; import org.smartregister.chw.core.application.CoreChwApplication; import org.smartregister.chw.core.repository.ScheduleRepository; import org.smartregister.chw.core.rule.PNCHealthFacilityVisitRule; @@ -113,6 +114,9 @@ public static void onUpgrade(Context context, SQLiteDatabase db, int oldVersion, case 22: upgradeToVersion22(db); break; + case 23: + upgradeToVersion23(db); + break; default: break; } @@ -438,4 +442,17 @@ private static void upgradeToVersion22(SQLiteDatabase db) { Timber.e(e, "upgradeToVersion22"); } } + private static void upgradeToVersion23(SQLiteDatabase db) { + if (((ChwApplication) ChwApplication.getInstance()).isSupervisor()) { + try { + // setup reporting + ReportingLibrary reportingLibrary = ReportingLibrary.getInstance(); + String supervisorIndicatorDefinitionsFile = "config/supervisor-reporting-indicator-definitions.yml"; + reportingLibrary.readConfigFile(supervisorIndicatorDefinitionsFile, db); + } catch (Exception e) { + Timber.e(e, "upgradeToVersion23"); + } + } + + } } diff --git a/settings.gradle b/settings.gradle index 0fa09e59de..880fcd3dab 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,8 @@ include ':opensrp-chw' -//include ':opensrp-chw-core' -//project(":opensrp-chw-core").projectDir = new File("../opensrp-client-chw-core/opensrp-chw-core") \ No newline at end of file +/* +include ':opensrp-client-reporting' +project(":opensrp-client-reporting").projectDir = new File("../opensrp-client-reporting/opensrp-reporting") + +include ':opensrp-client-core' +project(":opensrp-client-core").projectDir = new File("../opensrp-client-core/opensrp-core")*/