diff --git a/gradle.properties b/gradle.properties index 56889dd3..06b3fe9a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=0.1.5-SNAPSHOT +VERSION_NAME=0.1.6-SNAPSHOT VERSION_CODE=1 GROUP=org.smartregister POM_SETTING_DESCRIPTION=OpenSRP Client CHW Anc Library diff --git a/opensrp-chw-anc/gradle.properties b/opensrp-chw-anc/gradle.properties index 5c957bcf..5987be7a 100644 --- a/opensrp-chw-anc/gradle.properties +++ b/opensrp-chw-anc/gradle.properties @@ -1,5 +1,5 @@ POM_SETTING_NAME=OpenSRP Client Chw Anc POM_SETTING_ARTIFACT_ID=opensrp-client-chw-anc POM_SETTING_PACKAGING=aar -VERSION_NAME=0.1.11-SNAPSHOT +VERSION_NAME=0.1.12-SNAPSHOT VERSION_CODE=1 \ No newline at end of file diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncHomeVisitActivity.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncHomeVisitActivity.java index fa24d9d2..e0377f2b 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncHomeVisitActivity.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncHomeVisitActivity.java @@ -211,9 +211,9 @@ public void redrawVisitUI() { boolean valid = actionList.size() > 0; for (Map.Entry entry : actionList.entrySet()) { BaseAncHomeVisitAction action = entry.getValue(); - if (!action.isOptional() - && action.getActionStatus() == BaseAncHomeVisitAction.Status.PENDING - && action.isValid() + if ( + (!action.isOptional() && (action.getActionStatus() == BaseAncHomeVisitAction.Status.PENDING && action.isValid())) + || !action.isEnabled() ) { valid = false; break; diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncMemberProfileActivity.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncMemberProfileActivity.java index e0e05689..e1cd2510 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncMemberProfileActivity.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncMemberProfileActivity.java @@ -106,12 +106,7 @@ protected void onCreation() { upArrow.setColorFilter(getResources().getColor(R.color.text_blue), PorterDuff.Mode.SRC_ATOP); actionBar.setHomeAsUpIndicator(upArrow); } - toolbar.setNavigationOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); + toolbar.setNavigationOnClickListener(v -> finish()); appBarLayout = findViewById(R.id.collapsing_toolbar_appbarlayout); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { appBarLayout.setOutlineProvider(null); @@ -373,7 +368,6 @@ public void setFamilyStatus(AlertStatus status) { } } - @Override public void setMemberName(String memberName) { text_view_anc_member_name.setText(memberName); diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncUpcomingServicesActivity.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncUpcomingServicesActivity.java index de07222a..b2123e44 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncUpcomingServicesActivity.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/activity/BaseAncUpcomingServicesActivity.java @@ -82,12 +82,7 @@ private void setUpActionBar() { upArrow.setColorFilter(getResources().getColor(R.color.text_blue), PorterDuff.Mode.SRC_ATOP); actionBar.setHomeAsUpIndicator(upArrow); } - toolbar.setNavigationOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); + toolbar.setNavigationOnClickListener(v -> finish()); } public void setUpView() { diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/adapter/BaseAncHomeVisitAdapter.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/adapter/BaseAncHomeVisitAdapter.java index dcc872e5..d99b9a4f 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/adapter/BaseAncHomeVisitAdapter.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/adapter/BaseAncHomeVisitAdapter.java @@ -86,21 +86,20 @@ public void onBindViewHolder(@NotNull MyViewHolder holder, int position) { holder.descriptionText.setVisibility(View.VISIBLE); holder.invalidText.setVisibility(View.GONE); holder.descriptionText.setText(ancHomeVisitAction.getSubTitle()); + + boolean isOverdue = ancHomeVisitAction.getScheduleStatus() == BaseAncHomeVisitAction.ScheduleStatus.OVERDUE && + ancHomeVisitAction.isEnabled(); + + holder.descriptionText.setTextColor( + isOverdue ? context.getResources().getColor(R.color.alert_urgent_red) : + context.getResources().getColor(android.R.color.darker_gray) + ); + } else { holder.descriptionText.setVisibility(View.GONE); holder.invalidText.setVisibility(View.VISIBLE); holder.invalidText.setText(Html.fromHtml("" + ancHomeVisitAction.getDisabledMessage() + "")); } - - holder.descriptionText.setVisibility(View.VISIBLE); - - boolean isOverdue = ancHomeVisitAction.getScheduleStatus() == BaseAncHomeVisitAction.ScheduleStatus.OVERDUE && - ancHomeVisitAction.isEnabled(); - - holder.descriptionText.setTextColor( - isOverdue ? context.getResources().getColor(R.color.alert_urgent_red) : - context.getResources().getColor(android.R.color.darker_gray) - ); } else { holder.descriptionText.setVisibility(View.GONE); } @@ -122,6 +121,10 @@ public void onBindViewHolder(@NotNull MyViewHolder holder, int position) { private int getCircleColor(BaseAncHomeVisitAction ancHomeVisitAction) { int color_res; + boolean valid = ancHomeVisitAction.isValid() && ancHomeVisitAction.isEnabled(); + if (!valid) + return R.color.transparent_gray; + switch (ancHomeVisitAction.getActionStatus()) { case PENDING: color_res = R.color.transparent_gray; @@ -140,8 +143,10 @@ private int getCircleColor(BaseAncHomeVisitAction ancHomeVisitAction) { } private void bindClickListener(View view, final BaseAncHomeVisitAction ancHomeVisitAction) { - if (!ancHomeVisitAction.isEnabled()) + if (!ancHomeVisitAction.isEnabled() || !ancHomeVisitAction.isValid()) { view.setOnClickListener(null); + return; + } view.setOnClickListener(v -> { if (StringUtils.isNotBlank(ancHomeVisitAction.getFormName())) { diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/VaccineDisplay.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/VaccineDisplay.java index 15782306..3dd83750 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/VaccineDisplay.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/VaccineDisplay.java @@ -8,6 +8,7 @@ public class VaccineDisplay { private VaccineWrapper vaccineWrapper; private Date startDate; private Date endDate; + private Date dateGiven; private Boolean isValid; public VaccineWrapper getVaccineWrapper() { @@ -34,6 +35,14 @@ public void setEndDate(Date endDate) { this.endDate = endDate; } + public Date getDateGiven() { + return dateGiven; + } + + public void setDateGiven(Date dateGiven) { + this.dateGiven = dateGiven; + } + public Boolean getValid() { return isValid; } diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/Visit.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/Visit.java index b67b8ac7..71efaf8e 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/Visit.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/Visit.java @@ -8,6 +8,7 @@ public class Visit { private String visitId; private String visitType; + private String parentVisitID; private String baseEntityId; private Date date; private Date updatedAt; @@ -35,6 +36,14 @@ public void setVisitType(String visitType) { this.visitType = visitType; } + public String getParentVisitID() { + return parentVisitID; + } + + public void setParentVisitID(String parentVisitID) { + this.parentVisitID = parentVisitID; + } + public String getBaseEntityId() { return baseEntityId; } diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/VisitDetail.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/VisitDetail.java index af022c9e..d756c23a 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/VisitDetail.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/domain/VisitDetail.java @@ -7,6 +7,7 @@ public class VisitDetail { private String visitId; private String baseEntityId; private String visitKey; + private String parentCode; private String details; // private String humanReadable; // private String jsonDetails; @@ -48,6 +49,14 @@ public void setVisitKey(String visitKey) { this.visitKey = visitKey; } + public String getParentCode() { + return parentCode; + } + + public void setParentCode(String parentCode) { + this.parentCode = parentCode; + } + public String getDetails() { return details; } diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/fragment/BaseAncHomeVisitFragment.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/fragment/BaseAncHomeVisitFragment.java index bb1cbbc5..1d20f04e 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/fragment/BaseAncHomeVisitFragment.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/fragment/BaseAncHomeVisitFragment.java @@ -205,8 +205,8 @@ private void prepareRadioView() { String key = object.getString(JsonFormConstants.KEY); rb.setTag(R.id.home_visit_radio_key, key); - rb.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (isChecked) + rb.setOnClickListener(v -> { + if (rb.isChecked()) onSelectOption(key); }); } catch (JSONException e) { @@ -342,6 +342,7 @@ public void setCount(String count) { public void setValue(String value) { if (getQuestionType() == QuestionType.BOOLEAN) { if (radioButtonNo != null && radioButtonYes != null) { + setYesNoListenersActive(false); if (value.equalsIgnoreCase("Yes")) { radioButtonYes.setChecked(true); radioButtonNo.setChecked(false); @@ -349,6 +350,7 @@ public void setValue(String value) { radioButtonYes.setChecked(false); radioButtonNo.setChecked(true); } + setYesNoListenersActive(true); } } else if (getQuestionType() == QuestionType.DATE_SELECTOR) { // datePicker.updateDate(); @@ -363,6 +365,9 @@ public void setValue(String value) { } } else if (getQuestionType() == QuestionType.RADIO) { // + if (radioGroupDynamic.getChildCount() == 0) + prepareRadioView(); + int count = radioGroupDynamic.getChildCount(); int x = 0; while (count > x) { @@ -376,6 +381,16 @@ public void setValue(String value) { } } + private void setYesNoListenersActive(boolean active) { + if (active) { + radioButtonYes.setOnClickListener(this); + radioButtonNo.setOnClickListener(this); + } else { + radioButtonYes.setOnClickListener(null); + radioButtonNo.setOnClickListener(null); + } + } + @Override public void setOptions(List options) { if (this.optionList == null) diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/fragment/BaseHomeVisitImmunizationFragment.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/fragment/BaseHomeVisitImmunizationFragment.java index 5f41fe9b..8765f0c0 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/fragment/BaseHomeVisitImmunizationFragment.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/fragment/BaseHomeVisitImmunizationFragment.java @@ -19,6 +19,7 @@ import org.smartregister.chw.anc.model.BaseHomeVisitImmunizationFragmentModel; import org.smartregister.chw.anc.presenter.BaseHomeVisitImmunizationFragmentPresenter; import org.smartregister.chw.anc.util.Constants; +import org.smartregister.chw.anc.util.JsonFormUtils; import org.smartregister.chw.anc.util.NCUtils; import org.smartregister.chw.opensrp_chw_anc.R; import org.smartregister.immunization.db.VaccineRepo; @@ -51,6 +52,7 @@ public class BaseHomeVisitImmunizationFragment extends BaseHomeVisitFragment imp private CheckBox checkBoxNoVaccinesDone; private DatePicker singleDatePicker; private Button saveButton; + private SimpleDateFormat dateFormat = new SimpleDateFormat(Constants.DATE_FORMATS.NATIVE_FORMS, Locale.getDefault()); public static BaseHomeVisitImmunizationFragment getInstance(final BaseAncHomeVisitContract.VisitView view, String baseEntityID, Map> details, List vaccineDisplays) { BaseHomeVisitImmunizationFragment fragment = new BaseHomeVisitImmunizationFragment(); @@ -60,6 +62,12 @@ public static BaseHomeVisitImmunizationFragment getInstance(final BaseAncHomeVis for (VaccineDisplay vaccineDisplay : vaccineDisplays) { fragment.vaccineDisplays.put(vaccineDisplay.getVaccineWrapper().getName(), vaccineDisplay); } + + if (details != null && details.size() > 0) { + fragment.jsonObject = NCUtils.getVisitJSONFromVisitDetails(baseEntityID, details, vaccineDisplays); + JsonFormUtils.populateForm(fragment.jsonObject, details); + } + return fragment; } @@ -85,7 +93,7 @@ public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, singleDatePicker = view.findViewById(R.id.earlier_date_picker); if (vaccineDisplays.size() > 0) - initializeDatePicker(singleDatePicker, vaccineDisplays.entrySet().iterator().next().getValue()); + initializeDatePicker(singleDatePicker, vaccineDisplays); checkBoxNoVaccinesDone = view.findViewById(R.id.select); checkBoxNoVaccinesDone.setOnClickListener(this); @@ -120,9 +128,13 @@ public void setVaccineDisplays(Map vaccineDisplays) { this.vaccineDisplays = vaccineDisplays; // redraw all vaccine views - if (vaccineDisplays.size() > 0) - initializeDatePicker(singleDatePicker, vaccineDisplays.entrySet().iterator().next().getValue()); - addVaccineViews(); + if (vaccineDisplays.size() > 0 && singleDatePicker != null) { + initializeDatePicker(singleDatePicker, vaccineDisplays); + addVaccineViews(); + } + + // reset the json payload if the vaccine view was updated manually + this.jsonObject = null; } @Override @@ -165,12 +177,41 @@ private void addVaccineViews() { } private void initializeDatePicker(@NotNull DatePicker datePicker, @NotNull VaccineDisplay vaccineDisplay) { - if (vaccineDisplay.getStartDate().getTime() > vaccineDisplay.getEndDate().getTime()) { - datePicker.setMinDate(vaccineDisplay.getStartDate().getTime()); - datePicker.setMaxDate(vaccineDisplay.getStartDate().getTime()); + Date startDate = vaccineDisplay.getStartDate(); + Date endDate = (vaccineDisplay.getEndDate() != null && vaccineDisplay.getEndDate().getTime() < new Date().getTime()) ? + vaccineDisplay.getEndDate() : new Date(); + + if (startDate.getTime() > endDate.getTime()) { + datePicker.setMinDate(endDate.getTime()); + datePicker.setMaxDate(endDate.getTime()); } else { - datePicker.setMinDate(vaccineDisplay.getStartDate().getTime()); - datePicker.setMaxDate(vaccineDisplay.getEndDate().getTime()); + datePicker.setMinDate(startDate.getTime()); + datePicker.setMaxDate(endDate.getTime()); + } + } + + private void initializeDatePicker(@NotNull DatePicker datePicker, @NotNull Map vaccineDisplays) { + //compute the start date and the end date + Date startDate = null; + Date endDate = new Date(); + for (Map.Entry entry : vaccineDisplays.entrySet()) { + VaccineDisplay display = entry.getValue(); + + // get the largest start date + if (startDate == null || display.getStartDate().getTime() < startDate.getTime()) + startDate = display.getStartDate(); + + // get the lowest end date + if (display.getEndDate() != null && display.getEndDate().getTime() < endDate.getTime()) + endDate = display.getEndDate(); + } + + if (startDate != null && startDate.getTime() > endDate.getTime()) { + datePicker.setMinDate(endDate.getTime()); + datePicker.setMaxDate(endDate.getTime()); + } else { + datePicker.setMinDate(startDate != null ? startDate.getTime() : endDate.getTime()); + datePicker.setMaxDate(endDate.getTime()); } } @@ -230,22 +271,29 @@ private void onSave() { // notify the view (write to json file then dismiss) Date vaccineDate = getDateFromDatePicker(singleDatePicker); - SimpleDateFormat dateFormat = new SimpleDateFormat(Constants.DATE_FORMATS.NATIVE_FORMS, Locale.getDefault()); HashMap vaccineDateMap = new HashMap<>(); boolean multiModeActive = multipleVaccineDatePickerView.getVisibility() == View.GONE; for (VaccineView vaccineView : vaccineViews) { - VaccineWrapper wrapper = vaccineDisplays.get(vaccineView.getVaccineName()).getVaccineWrapper(); + VaccineDisplay display = vaccineDisplays.get(vaccineView.getVaccineName()); + VaccineWrapper wrapper = display.getVaccineWrapper(); if (wrapper != null) { if (!checkBoxNoVaccinesDone.isChecked() && vaccineView.getCheckBox().isChecked()) { if (vaccineView.getDatePickerView() != null && multiModeActive) { - vaccineDateMap.put(wrapper, dateFormat.format(getDateFromDatePicker(vaccineView.getDatePickerView()))); + Date dateGiven = getDateFromDatePicker(vaccineView.getDatePickerView()); + vaccineDateMap.put(wrapper, dateFormat.format(dateGiven)); + display.setDateGiven(dateGiven); + display.setValid(true); } else if (vaccineDate != null) { vaccineDateMap.put(wrapper, dateFormat.format(vaccineDate)); + display.setDateGiven(vaccineDate); + display.setValid(true); } } else { vaccineDateMap.put(wrapper, Constants.HOME_VISIT.VACCINE_NOT_GIVEN); + display.setDateGiven(null); + display.setValid(false); } } } @@ -262,6 +310,14 @@ private void onSave() { } } + /** + * reset the view payload + */ + public void resetViewPayload() { + jsonObject = null; + visitView.onDialogOptionUpdated(""); + } + /** * executed to close the vaccine screen */ diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/interactor/BaseAncHomeVisitInteractor.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/interactor/BaseAncHomeVisitInteractor.java index 1c7c6e84..8b87d112 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/interactor/BaseAncHomeVisitInteractor.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/interactor/BaseAncHomeVisitInteractor.java @@ -16,6 +16,7 @@ import org.smartregister.chw.anc.domain.Visit; import org.smartregister.chw.anc.domain.VisitDetail; import org.smartregister.chw.anc.model.BaseAncHomeVisitAction; +import org.smartregister.chw.anc.repository.VisitRepository; import org.smartregister.chw.anc.util.AppExecutors; import org.smartregister.chw.anc.util.Constants; import org.smartregister.chw.anc.util.JsonFormUtils; @@ -86,49 +87,7 @@ public void submitVisit(final boolean editMode, final String memberID, final Map final Runnable runnable = () -> { boolean result = true; try { - - Map externalVisits = new HashMap<>(); - Map jsons = new HashMap<>(); - Map vaccineWrapperMap = new HashMap<>(); - Map serviceWrapperMap = new HashMap<>(); - - // aggregate forms to be processed - for (Map.Entry entry : map.entrySet()) { - String json = entry.getValue().getJsonPayload(); - if (StringUtils.isNotBlank(json)) { - - // do not process events that are meant to be in detached mode - // in a similar manner to the the aggregated events - if (entry.getValue().getProcessingMode() == BaseAncHomeVisitAction.ProcessingMode.DETACHED - || StringUtils.isNotBlank(entry.getValue().getBaseEntityID())) { - externalVisits.put(entry.getKey(), entry.getValue()); - continue; - } - - jsons.put(entry.getKey(), json); - JSONObject jsonObject = new JSONObject(json); - if (entry.getValue().getVaccineWrapper() != null) { - int position = 0; - for (VaccineWrapper v : entry.getValue().getVaccineWrapper()) { - vaccineWrapperMap.put(JsonFormUtils.getObjectKey(jsonObject, position), v); - position++; - } - } - - if (entry.getValue().getServiceWrapper() != null) { - int position = 0; - for (ServiceWrapper sw : entry.getValue().getServiceWrapper()) { - serviceWrapperMap.put(JsonFormUtils.getObjectKey(jsonObject, position), sw); - position++; - } - } - } - } - - Visit visit = saveVisit(editMode, memberID, getEncounterType(), jsons, vaccineWrapperMap, serviceWrapperMap); - if (visit != null) - saveDetachedEvents(visit, externalVisits); - + submitVisit(editMode, memberID, map, ""); } catch (Exception e) { Timber.e(e); result = false; @@ -141,63 +100,171 @@ public void submitVisit(final boolean editMode, final String memberID, final Map appExecutors.diskIO().execute(runnable); } + private void submitVisit(final boolean editMode, final String memberID, final Map map, String parentEventType) throws Exception { + + Map externalVisits = new HashMap<>(); + Map detachedVisits = new HashMap<>(); + Map jsons = new HashMap<>(); + Map vaccineWrapperMap = new HashMap<>(); + Map serviceWrapperMap = new HashMap<>(); + + // aggregate forms to be processed + for (Map.Entry entry : map.entrySet()) { + String json = entry.getValue().getJsonPayload(); + if (StringUtils.isNotBlank(json)) { + // do not process events that are meant to be in detached mode + // in a similar manner to the the aggregated events + BaseAncHomeVisitAction.ProcessingMode mode = entry.getValue().getProcessingMode(); + if (mode == BaseAncHomeVisitAction.ProcessingMode.DETACHED) { + + detachedVisits.put(entry.getKey(), entry.getValue()); + } else if (mode == BaseAncHomeVisitAction.ProcessingMode.SEPARATE && StringUtils.isBlank(parentEventType)) { + + externalVisits.put(entry.getKey(), entry.getValue()); + } else { + jsons.put(entry.getKey(), json); + JSONObject jsonObject = new JSONObject(json); + + extractVaccineWrappers(entry, vaccineWrapperMap, jsonObject); + extractServiceWrappers(entry, serviceWrapperMap, jsonObject); + } + } + } + + String type = StringUtils.isBlank(parentEventType) ? getEncounterType() : getEncounterType(); + + // persist to database + Visit visit = saveVisit(editMode, memberID, type, jsons, parentEventType); + if (visit != null) { + saveVisitDetails(visit, vaccineWrapperMap, serviceWrapperMap); + processExternalVisits(visit, externalVisits, memberID); + if (detachedVisits.size() > 0) + saveDetachedEvents(visit, detachedVisits); + } + } + + private void extractVaccineWrappers( + Map.Entry entry, + Map vaccineWrapperMap, + JSONObject jsonObject + ) { + if (entry.getValue().getVaccineWrapper() != null) { + int position = 0; + for (VaccineWrapper v : entry.getValue().getVaccineWrapper()) { + vaccineWrapperMap.put(JsonFormUtils.getObjectKey(jsonObject, position), v); + position++; + } + } + } + + private void extractServiceWrappers( + Map.Entry entry, + Map serviceWrapperMap, + JSONObject jsonObject + ) { + if (entry.getValue().getServiceWrapper() != null) { + int position = 0; + for (ServiceWrapper sw : entry.getValue().getServiceWrapper()) { + serviceWrapperMap.put(JsonFormUtils.getObjectKey(jsonObject, position), sw); + position++; + } + } + } + + /** + * recursively persist visits to the db + * + * @param visit + * @param externalVisits + * @param memberID + * @throws Exception + */ + private void processExternalVisits(Visit visit, Map externalVisits, String memberID) throws Exception { + if (visit != null && !externalVisits.isEmpty()) { + for (Map.Entry entry : externalVisits.entrySet()) { + Map subEvent = new HashMap<>(); + subEvent.put(entry.getKey(), entry.getValue()); + submitVisit(false, memberID, subEvent, visit.getVisitType()); + } + } + } + private Visit saveVisit(boolean editMode, String memberID, String encounterType, final Map jsonString, - Map vaccineWrapperMap, - Map serviceWrapperMap + String parentEventType ) throws Exception { AllSharedPreferences allSharedPreferences = AncLibrary.getInstance().context().allSharedPreferences(); - Event baseEvent = JsonFormUtils.processVisitJsonForm(allSharedPreferences, memberID, encounterType, jsonString, getTableName()); + + String derivedEncounterType = StringUtils.isBlank(parentEventType) ? encounterType : ""; + Event baseEvent = JsonFormUtils.processVisitJsonForm(allSharedPreferences, memberID, derivedEncounterType, jsonString, getTableName()); prepareEvent(baseEvent); + if (baseEvent != null) { baseEvent.setFormSubmissionId(JsonFormUtils.generateRandomUUIDString()); JsonFormUtils.tagEvent(allSharedPreferences, baseEvent); String visitID = (editMode) ? - AncLibrary.getInstance().visitRepository().getLatestVisit(memberID, getEncounterType()).getVisitId() : + visitRepository().getLatestVisit(memberID, getEncounterType()).getVisitId() : JsonFormUtils.generateRandomUUIDString(); // reset database - if (editMode) { - AncLibrary.getInstance().visitRepository().deleteVisit(visitID); - AncLibrary.getInstance().visitDetailsRepository().deleteVisitDetails(visitID); - } + if (editMode) + deleteOldVisit(visitID); Visit visit = NCUtils.eventToVisit(baseEvent, visitID); visit.setPreProcessedJson(new Gson().toJson(baseEvent)); - AncLibrary.getInstance().visitRepository().addVisit(visit); - - // create the visit details - if (visit.getVisitDetails() != null) { - Gson gson = Converters.registerDateTime(new GsonBuilder()).create(); - - for (Map.Entry> entry : visit.getVisitDetails().entrySet()) { - if (entry.getValue() != null) { - for (VisitDetail d : entry.getValue()) { - - VaccineWrapper vaccineWrapper = vaccineWrapperMap.get(d.getVisitKey()); - if (vaccineWrapper != null) { - String json = gson.toJson(vaccineWrapper); - d.setPreProcessedJson(json); - d.setPreProcessedType("vaccine"); - } - - ServiceWrapper serviceWrapper = serviceWrapperMap.get(d.getVisitKey()); - if (serviceWrapper != null) { - String json = gson.toJson(serviceWrapper); - d.setPreProcessedJson(json); - d.setPreProcessedType("service"); - } + if (StringUtils.isNotBlank(parentEventType)) + visit.setParentVisitID(visitRepository().getParentVisitEventID(visit.getBaseEntityId(), parentEventType, visit.getDate())); - AncLibrary.getInstance().visitDetailsRepository().addVisitDetails(d); + visitRepository().addVisit(visit); + return visit; + } + return null; + } + + private VisitRepository visitRepository() { + return AncLibrary.getInstance().visitRepository(); + } + + private void deleteOldVisit(String visitID) { + visitRepository().deleteVisit(visitID); + AncLibrary.getInstance().visitDetailsRepository().deleteVisitDetails(visitID); + + List childVisits = visitRepository().getChildEvents(visitID); + for (Visit v : childVisits) { + visitRepository().deleteVisit(v.getVisitId()); + AncLibrary.getInstance().visitDetailsRepository().deleteVisitDetails(v.getVisitId()); + } + } + + private void saveVisitDetails(Visit visit, Map vaccineWrapperMap, Map serviceWrapperMap) { + if (visit.getVisitDetails() != null) { + Gson gson = Converters.registerDateTime(new GsonBuilder()).create(); + + for (Map.Entry> entry : visit.getVisitDetails().entrySet()) { + if (entry.getValue() != null) { + for (VisitDetail d : entry.getValue()) { + + VaccineWrapper vaccineWrapper = vaccineWrapperMap.get(d.getVisitKey()); + if (vaccineWrapper != null) { + String json = gson.toJson(vaccineWrapper); + d.setPreProcessedJson(json); + d.setPreProcessedType("vaccine"); + } + + ServiceWrapper serviceWrapper = serviceWrapperMap.get(d.getVisitKey()); + if (serviceWrapper != null) { + String json = gson.toJson(serviceWrapper); + d.setPreProcessedJson(json); + d.setPreProcessedType("service"); } + + AncLibrary.getInstance().visitDetailsRepository().addVisitDetails(d); } } } - return visit; } - return null; } /** diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/model/BaseAncHomeVisitAction.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/model/BaseAncHomeVisitAction.java index e5cd6e00..c2c90737 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/model/BaseAncHomeVisitAction.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/model/BaseAncHomeVisitAction.java @@ -43,6 +43,7 @@ private BaseAncHomeVisitAction(Builder builder) throws ValidationException { this.baseEntityID = builder.baseEntityID; this.title = builder.title; this.subTitle = builder.subTitle; + this.disabledMessage = builder.disabledMessage; this.actionStatus = builder.actionStatus; this.scheduleStatus = builder.scheduleStatus; this.optional = builder.optional; @@ -56,7 +57,6 @@ private BaseAncHomeVisitAction(Builder builder) throws ValidationException { this.processingMode = builder.processingMode; this.jsonPayload = builder.jsonPayload; this.validator = builder.validator; - this.disabledMessage = builder.disabledMessage; validateMe(); initialize(); @@ -309,7 +309,7 @@ public enum ScheduleStatus {DUE, OVERDUE} /** * Detached processing generates separate event when form is submitted */ - public enum ProcessingMode {COMBINED, DETACHED} + public enum ProcessingMode {COMBINED, DETACHED, SEPARATE} public interface AncHomeVisitActionHelper { @@ -332,7 +332,6 @@ public interface AncHomeVisitActionHelper { */ void onPayloadReceived(String jsonPayload); - /** * executed after form is loaded on start * add functionality to evaluate the state of the view immediately the form is processed diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/repository/VisitDetailsRepository.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/repository/VisitDetailsRepository.java index 7ed2ed39..f0a8bd03 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/repository/VisitDetailsRepository.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/repository/VisitDetailsRepository.java @@ -21,6 +21,7 @@ public class VisitDetailsRepository extends BaseRepository { private static final String VISIT_DETAILS_ID = "visit_details_id"; private static final String VISIT_ID = "visit_id"; private static final String VISIT_KEY = "visit_key"; + private static final String PARENT_CODE = "parent_code"; private static final String DETAILS = "details"; private static final String HUMAN_READABLE = "human_readable_details"; private static final String JSON_DETAILS = "json_details"; @@ -35,6 +36,7 @@ public class VisitDetailsRepository extends BaseRepository { + VISIT_DETAILS_ID + " VARCHAR NULL, " + VISIT_ID + " VARCHAR NULL, " + VISIT_KEY + " VARCHAR NULL, " + + PARENT_CODE + " VARCHAR NULL, " + JSON_DETAILS + " VARCHAR NULL, " + PRE_PROCESSED_JSON + " VARCHAR NULL, " + PRE_PROCESSED_TYPE + " VARCHAR NULL, " @@ -50,7 +52,7 @@ public class VisitDetailsRepository extends BaseRepository { + ");"; - private String[] VISIT_DETAILS_COLUMNS = {VISIT_ID, VISIT_KEY, VISIT_DETAILS_ID, HUMAN_READABLE, JSON_DETAILS, PRE_PROCESSED_JSON, PRE_PROCESSED_TYPE, DETAILS, PROCESSED, UPDATED_AT, CREATED_AT}; + private String[] VISIT_DETAILS_COLUMNS = {VISIT_ID, VISIT_KEY, PARENT_CODE, VISIT_DETAILS_ID, HUMAN_READABLE, JSON_DETAILS, PRE_PROCESSED_JSON, PRE_PROCESSED_TYPE, DETAILS, PROCESSED, UPDATED_AT, CREATED_AT}; public VisitDetailsRepository(Repository repository) { super(repository); @@ -66,6 +68,7 @@ private ContentValues createValues(VisitDetail visitDetail) { values.put(VISIT_DETAILS_ID, visitDetail.getVisitDetailsId()); values.put(VISIT_ID, visitDetail.getVisitId()); values.put(VISIT_KEY, visitDetail.getVisitKey()); + values.put(PARENT_CODE, visitDetail.getParentCode()); values.put(JSON_DETAILS, visitDetail.getJsonDetails()); values.put(PRE_PROCESSED_JSON, visitDetail.getPreProcessedJson()); values.put(PRE_PROCESSED_TYPE, visitDetail.getPreProcessedType()); @@ -129,6 +132,7 @@ private List readVisitDetails(Cursor cursor) { visitDetail.setVisitId(cursor.getString(cursor.getColumnIndex(VISIT_ID))); visitDetail.setVisitDetailsId(cursor.getString(cursor.getColumnIndex(VISIT_DETAILS_ID))); visitDetail.setVisitKey(cursor.getString(cursor.getColumnIndex(VISIT_KEY))); + visitDetail.setParentCode(cursor.getString(cursor.getColumnIndex(PARENT_CODE))); visitDetail.setJsonDetails(cursor.getString(cursor.getColumnIndex(JSON_DETAILS))); visitDetail.setPreProcessedJson(cursor.getString(cursor.getColumnIndex(PRE_PROCESSED_JSON))); visitDetail.setPreProcessedType(cursor.getString(cursor.getColumnIndex(PRE_PROCESSED_TYPE))); @@ -145,7 +149,8 @@ private List readVisitDetails(Cursor cursor) { } catch (Exception e) { Timber.e(e); } finally { - cursor.close(); + if (cursor != null) + cursor.close(); } return visitDetailList; } diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/repository/VisitRepository.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/repository/VisitRepository.java index e36c0272..ee811637 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/repository/VisitRepository.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/repository/VisitRepository.java @@ -11,9 +11,11 @@ import org.smartregister.repository.BaseRepository; import org.smartregister.repository.Repository; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Locale; import timber.log.Timber; @@ -22,6 +24,7 @@ public class VisitRepository extends BaseRepository { public static final String VISIT_TABLE = "visits"; private static final String VISIT_ID = "visit_id"; private static final String VISIT_TYPE = "visit_type"; + private static final String PARENT_VISIT_ID = "parent_visit_id"; private static final String BASE_ENTITY_ID = "base_entity_id"; private static final String VISIT_DATE = "visit_date"; private static final String VISIT_JSON = "visit_json"; @@ -34,6 +37,7 @@ public class VisitRepository extends BaseRepository { "CREATE TABLE " + VISIT_TABLE + "(" + VISIT_ID + " VARCHAR NULL, " + VISIT_TYPE + " VARCHAR NULL, " + + PARENT_VISIT_ID + " VARCHAR NULL, " + BASE_ENTITY_ID + " VARCHAR NULL, " + VISIT_DATE + " VARCHAR NULL, " + VISIT_JSON + " VARCHAR NULL, " @@ -48,8 +52,11 @@ public class VisitRepository extends BaseRepository { + VISIT_TYPE + " COLLATE NOCASE , " + VISIT_DATE + " COLLATE NOCASE" + ");"; - private String[] VISIT_COLUMNS = {VISIT_ID, VISIT_TYPE, BASE_ENTITY_ID, VISIT_DATE, VISIT_JSON, PRE_PROCESSED, FORM_SUBMISSION_ID, PROCESSED, UPDATED_AT, CREATED_AT}; + public static final String PARENT_VISIT_ID_INDEX = "CREATE INDEX " + VISIT_TABLE + "_" + PARENT_VISIT_ID + "_index ON " + VISIT_TABLE + + "(" + PARENT_VISIT_ID + " COLLATE NOCASE );"; + + private String[] VISIT_COLUMNS = {VISIT_ID, VISIT_TYPE, PARENT_VISIT_ID, BASE_ENTITY_ID, VISIT_DATE, VISIT_JSON, PRE_PROCESSED, FORM_SUBMISSION_ID, PROCESSED, UPDATED_AT, CREATED_AT}; public VisitRepository(Repository repository) { super(repository); @@ -58,12 +65,14 @@ public VisitRepository(Repository repository) { public static void createTable(SQLiteDatabase database) { database.execSQL(CREATE_VISIT_TABLE); database.execSQL(BASE_ENTITY_ID_INDEX); + database.execSQL(PARENT_VISIT_ID_INDEX); } private ContentValues createValues(Visit visit) { ContentValues values = new ContentValues(); values.put(VISIT_ID, visit.getVisitId()); values.put(VISIT_TYPE, visit.getVisitType()); + values.put(PARENT_VISIT_ID, visit.getParentVisitID()); values.put(BASE_ENTITY_ID, visit.getBaseEntityId()); values.put(VISIT_DATE, visit.getDate() != null ? visit.getDate().getTime() : null); values.put(VISIT_JSON, visit.getJson()); @@ -87,6 +96,30 @@ public void addVisit(Visit visit, SQLiteDatabase database) { database.insert(VISIT_TABLE, null, createValues(visit)); } + public String getParentVisitEventID(String baseEntityID, String parentEventType, Date eventDate) { + String visitID = null; + Cursor cursor = null; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + String sql = "select " + VISIT_ID + " from visits where base_entity_id = ? COLLATE NOCASE and visit_type = ? COLLATE NOCASE strftime('%Y-%m-%d',visit_date / 1000, 'unixepoch') = ? "; + try { + cursor = getReadableDatabase().rawQuery(sql, new String[]{sdf.format(eventDate), parentEventType, baseEntityID}); + if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) { + while (!cursor.isAfterLast()) { + visitID = cursor.getString(cursor.getColumnIndex(VISIT_ID)); + cursor.moveToNext(); + } + } + } catch (Exception e) { + Timber.e(e); + } finally { + if (cursor != null) { + cursor.close(); + } + } + + return visitID; + } + public void deleteVisit(String visitID) { try { getWritableDatabase().delete(VISIT_TABLE, VISIT_ID + "= ?", new String[]{visitID}); @@ -118,6 +151,7 @@ private List readVisits(Cursor cursor) { Visit visit = new Visit(); visit.setVisitId(cursor.getString(cursor.getColumnIndex(VISIT_ID))); visit.setVisitType(cursor.getString(cursor.getColumnIndex(VISIT_TYPE))); + visit.setParentVisitID(cursor.getString(cursor.getColumnIndex(PARENT_VISIT_ID))); visit.setPreProcessedJson(cursor.getString(cursor.getColumnIndex(PRE_PROCESSED))); visit.setBaseEntityId(cursor.getString(cursor.getColumnIndex(BASE_ENTITY_ID))); visit.setDate(new Date(Long.parseLong(cursor.getString(cursor.getColumnIndex(VISIT_DATE))))); @@ -134,7 +168,8 @@ private List readVisits(Cursor cursor) { } catch (Exception e) { Timber.e(e); } finally { - cursor.close(); + if (cursor != null) + cursor.close(); } return visits; } @@ -189,13 +224,14 @@ public List getVisits(String baseEntityID, String visitType) { } return visits; } + public List getUniqueDayLatestThreeVisits(String baseEntityID, String visitType) { List visits = new ArrayList<>(); Cursor cursor = null; try { - String query = "select STRFTIME('%Y%m%d', datetime(("+VISIT_DATE+")/1000,'unixepoch')) as d,* from "+VISIT_TABLE+" where "+VISIT_TYPE+" = '"+visitType+"' AND " + - ""+BASE_ENTITY_ID+" = '"+baseEntityID+"' group by d order by "+VISIT_DATE+" desc limit 3"; - cursor = getReadableDatabase().rawQuery(query,null); + String query = "select STRFTIME('%Y%m%d', datetime((" + VISIT_DATE + ")/1000,'unixepoch')) as d,* from " + VISIT_TABLE + " where " + VISIT_TYPE + " = '" + visitType + "' AND " + + "" + BASE_ENTITY_ID + " = '" + baseEntityID + "' group by d order by " + VISIT_DATE + " desc limit 3"; + cursor = getReadableDatabase().rawQuery(query, null); visits = readVisits(cursor); } catch (Exception e) { Timber.e(e); @@ -206,11 +242,28 @@ public List getUniqueDayLatestThreeVisits(String baseEntityID, String vis } return visits; } - public List getVisitsByVisitId(String homeVisitId) { + + public List getVisitsByVisitId(String visitID) { + List visits = new ArrayList<>(); + Cursor cursor = null; + try { + cursor = getReadableDatabase().query(VISIT_TABLE, VISIT_COLUMNS, VISIT_ID + " = ? ", new String[]{visitID}, null, null, VISIT_DATE + " DESC ", null); + visits = readVisits(cursor); + } catch (Exception e) { + Timber.e(e); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return visits; + } + + public List getChildEvents(String visitID) { List visits = new ArrayList<>(); Cursor cursor = null; try { - cursor = getReadableDatabase().query(VISIT_TABLE, VISIT_COLUMNS, VISIT_ID + " = ? ", new String[]{homeVisitId}, null, null, VISIT_DATE + " DESC ", null); + cursor = getReadableDatabase().query(VISIT_TABLE, VISIT_COLUMNS, PARENT_VISIT_ID + " = ? ", new String[]{visitID}, null, null, VISIT_DATE + " DESC ", null); visits = readVisits(cursor); } catch (Exception e) { Timber.e(e); diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/JsonFormUtils.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/JsonFormUtils.java index a60e1656..6352db2a 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/JsonFormUtils.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/JsonFormUtils.java @@ -81,8 +81,9 @@ public static Event processVisitJsonForm(AllSharedPreferences allSharedPreferenc } JSONArray fields = new JSONArray(fields_obj); + String derivedEncounterType = StringUtils.isBlank(encounterType) ? getString(jsonForm, ENCOUNTER_TYPE) : encounterType; - return org.smartregister.util.JsonFormUtils.createEvent(fields, metadata, formTag(allSharedPreferences), entityId, encounterType, tableName); + return org.smartregister.util.JsonFormUtils.createEvent(fields, metadata, formTag(allSharedPreferences), entityId, derivedEncounterType, tableName); } public static Event prepareEvent(AllSharedPreferences allSharedPreferences, String entityId, String jsonString, String tableName) throws JSONException { @@ -101,7 +102,6 @@ public static Event prepareEvent(AllSharedPreferences allSharedPreferences, Stri return org.smartregister.util.JsonFormUtils.createEvent(fields, metadata, formTag(allSharedPreferences), entityId, encounterType, tableName); } - public static Event createUntaggedEvent(String baseEntityId, String eventType, String table) { try { @@ -279,7 +279,6 @@ public static String getCheckBoxValue(JSONObject jsonObject, String key) { } public static void populateForm(JSONObject jsonObject, Map> details) { - Timber.v("populateForm"); try { // x steps String count_str = jsonObject.getString(JsonFormConstants.COUNT); @@ -292,13 +291,18 @@ public static void populateForm(JSONObject jsonObject, Map= 0) { JSONObject jo = jsonArray.getJSONObject(field_count); - List detailList = details.get(jo.getString(JsonFormConstants.KEY)); + String key = jo.getString(JsonFormConstants.KEY); + List detailList = details.get(key); if (detailList != null) { if (jo.getString(JsonFormConstants.TYPE).equalsIgnoreCase(JsonFormConstants.CHECK_BOX)) { jo.put(JsonFormConstants.VALUE, getValue(jo, detailList)); } else { - jo.put(JsonFormConstants.VALUE, getValue(detailList.get(0))); + String value = getValue(detailList.get(0)); + if (key.contains("date")) { + value = NCUtils.getFormattedDate(NCUtils.getSaveDateFormat(), NCUtils.getSourceDateFormat(), value); + } + jo.put(JsonFormConstants.VALUE, value); } } diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/NCUtils.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/NCUtils.java index 7fa3766e..33a4aa4c 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/NCUtils.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/NCUtils.java @@ -30,6 +30,7 @@ import net.sqlcipher.database.SQLiteDatabase; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joda.time.DateTime; import org.joda.time.Days; @@ -40,6 +41,7 @@ import org.json.JSONObject; import org.smartregister.chw.anc.AncLibrary; import org.smartregister.chw.anc.contract.BaseAncWomanCallDialogContract; +import org.smartregister.chw.anc.domain.VaccineDisplay; import org.smartregister.chw.anc.domain.Visit; import org.smartregister.chw.anc.domain.VisitDetail; import org.smartregister.chw.opensrp_chw_anc.R; @@ -215,11 +217,11 @@ public static String getTodayDate() { return formatter.format(date); } - private static SimpleDateFormat getSourceDateFormat() { + public static SimpleDateFormat getSourceDateFormat() { return new SimpleDateFormat(AncLibrary.getInstance().getSourceDateFormat(), Locale.getDefault()); } - private static SimpleDateFormat getSaveDateFormat() { + public static SimpleDateFormat getSaveDateFormat() { return new SimpleDateFormat(AncLibrary.getInstance().getSaveDateFormat(), Locale.getDefault()); } @@ -292,16 +294,9 @@ public static Map> eventsObsToDetails(List obsLis detail.setVisitId(visitID); detail.setBaseEntityId(baseEntityID); detail.setVisitKey(obs.getFormSubmissionField()); - - if (detail.getVisitKey().contains("date")) { - // parse the - detail.setDetails(getFormattedDate(getSourceDateFormat(), getSaveDateFormat(), cleanString(obs.getValues().toString()))); - detail.setHumanReadable(getFormattedDate(getSourceDateFormat(), getSaveDateFormat(), cleanString(obs.getHumanReadableValues().toString()))); - } else { - detail.setDetails(cleanString(obs.getValues().toString())); - detail.setHumanReadable(cleanString(obs.getHumanReadableValues().toString())); - } - + detail.setParentCode(obs.getParentCode()); + detail.setDetails(getDetailsValue(detail, obs.getValues().toString())); + detail.setHumanReadable(getDetailsValue(detail, obs.getHumanReadableValues().toString())); detail.setJsonDetails(new JSONObject(JsonFormUtils.gson.toJson(obs)).toString()); detail.setProcessed(false); detail.setCreatedAt(new Date()); @@ -320,10 +315,20 @@ public static Map> eventsObsToDetails(List obsLis } public static String getFormattedDate(SimpleDateFormat source_sdf, SimpleDateFormat dest_sdf, String value) { + if (StringUtils.isBlank(value)) + return ""; + try { Date date = source_sdf.parse(value); return dest_sdf.format(date); } catch (Exception e) { + try { + // fallback for long datetypes + Date date = new Date(Long.parseLong(value)); + return dest_sdf.format(date); + } catch (NumberFormatException | NullPointerException nfe) { + Timber.e(e); + } Timber.e(e); } return value; @@ -338,11 +343,29 @@ public static void processAncHomeVisit(EventClient baseEvent) { processAncHomeVisit(baseEvent, null); } + public static void processSubHomeVisit(EventClient baseEvent, String parentEventType) { + processAncHomeVisit(baseEvent, null, parentEventType); + } + + public static void processSubHomeVisit(EventClient baseEvent, SQLiteDatabase database, String parentEventType) { + processAncHomeVisit(baseEvent, database, parentEventType); + } + public static void processAncHomeVisit(EventClient baseEvent, SQLiteDatabase database) { + processAncHomeVisit(baseEvent, database, null); + } + + public static void processAncHomeVisit(EventClient baseEvent, SQLiteDatabase database, String parentEventType) { try { Visit visit = AncLibrary.getInstance().visitRepository().getVisitByFormSubmissionID(baseEvent.getEvent().getFormSubmissionId()); if (visit == null) { visit = eventToVisit(baseEvent.getEvent()); + + if (StringUtils.isNotBlank(parentEventType) && !parentEventType.equalsIgnoreCase(visit.getVisitType())) { + String parentVisitID = AncLibrary.getInstance().visitRepository().getParentVisitEventID(visit.getBaseEntityId(), parentEventType, visit.getDate()); + visit.setParentVisitID(parentVisitID); + } + if (database != null) { AncLibrary.getInstance().visitRepository().addVisit(visit, database); } else { @@ -391,16 +414,9 @@ public static Visit eventToVisit(org.smartregister.domain.db.Event event) throws detail.setVisitDetailsId(org.smartregister.chw.anc.util.JsonFormUtils.generateRandomUUIDString()); detail.setVisitId(visit.getVisitId()); detail.setVisitKey(obs.getFormSubmissionField()); - - if (detail.getVisitKey().contains("date")) { - // parse the - detail.setDetails(getFormattedDate(getSourceDateFormat(), getSaveDateFormat(), cleanString(obs.getValues().toString()))); - detail.setHumanReadable(getFormattedDate(getSourceDateFormat(), getSaveDateFormat(), cleanString(obs.getHumanReadableValues().toString()))); - } else { - detail.setDetails(cleanString(obs.getValues().toString())); - detail.setHumanReadable(cleanString(obs.getHumanReadableValues().toString())); - } - + detail.setParentCode(obs.getParentCode()); + detail.setDetails(getDetailsValue(detail, obs.getValues().toString())); + detail.setHumanReadable(getDetailsValue(detail, obs.getHumanReadableValues().toString())); detail.setProcessed(true); detail.setCreatedAt(new Date()); detail.setUpdatedAt(new Date()); @@ -419,6 +435,17 @@ public static Visit eventToVisit(org.smartregister.domain.db.Event event) throws return visit; } + public static String getDetailsValue(VisitDetail detail, String val) { + String clean_val = cleanString(val); + if (detail.getVisitKey().contains("date")) { + return getFormattedDate(getSourceDateFormat(), getSaveDateFormat(), clean_val); + } else if ("vaccine".equalsIgnoreCase(detail.getParentCode()) && !Constants.HOME_VISIT.VACCINE_NOT_GIVEN.equalsIgnoreCase(clean_val)) { + return getFormattedDate(getSourceDateFormat(), getSaveDateFormat(), clean_val); + } + + return clean_val; + } + public static int getMemberProfileImageResourceIDentifier(String entityType) { return R.mipmap.ic_member; } @@ -432,7 +459,6 @@ public static String gestationAgeString(String lmp, Context context, boolean ful } public static void saveVaccineEvents(JSONArray fields, String baseID) { - for (int i = 0; i < vaccines.length; i++) { saveVaccineEvent(vaccines[i], getFieldJSONObject(fields, vaccines[i]), baseID); } @@ -531,8 +557,9 @@ public static JSONObject getVisitJSONFromWrapper(String entityID, Map entry : vaccineWrapperDateMap.entrySet()) { JSONObject field = new JSONObject(); field.put(JsonFormConstants.KEY, removeSpaces(entry.getKey().getName())); - field.put(JsonFormConstants.OPENMRS_ENTITY_PARENT, ""); + field.put(JsonFormConstants.OPENMRS_ENTITY_PARENT, "vaccine"); field.put(JsonFormConstants.OPENMRS_ENTITY, "concept"); + field.put(JsonFormConstants.TYPE, JsonFormConstants.EDIT_TEXT); field.put(JsonFormConstants.OPENMRS_ENTITY_ID, removeSpaces(entry.getKey().getName())); field.put(JsonFormConstants.VALUE, entry.getValue()); @@ -546,7 +573,81 @@ public static JSONObject getVisitJSONFromWrapper(String entityID, Map> detailsMap, List vaccineDisplays) { + try { + JSONObject jsonObject = JsonFormUtils.getFormAsJson(Constants.FORMS.IMMUNIZATIOIN_VISIT); + jsonObject.put("entity_id", entityID); + JSONArray jsonArray = jsonObject.getJSONObject(JsonFormConstants.STEP1).getJSONArray(JsonFormConstants.FIELDS); + + for (VaccineDisplay vaccineDisplay : vaccineDisplays) { + JSONObject field = new JSONObject(); + + String name = removeSpaces(vaccineDisplay.getVaccineWrapper().getName()); + String value = getText(detailsMap.get(name)); + + field.put(JsonFormConstants.KEY, name); + field.put(JsonFormConstants.OPENMRS_ENTITY_PARENT, "vaccine"); + field.put(JsonFormConstants.OPENMRS_ENTITY, "concept"); + field.put(JsonFormConstants.TYPE, JsonFormConstants.EDIT_TEXT); + field.put(JsonFormConstants.OPENMRS_ENTITY_ID, name); + field.put(JsonFormConstants.VALUE, value); + + jsonArray.put(field); + } + + return jsonObject; + } catch (Exception e) { + Timber.e(e); + } + return null; + } + public static String removeSpaces(String s) { return s.replace(" ", "_").toLowerCase(); } + + /** + * Extract value from VisitDetail + * + * @return + */ + @NotNull + public static String getText(@Nullable VisitDetail visitDetail) { + if (visitDetail == null) + return ""; + + String val = visitDetail.getHumanReadable(); + if (StringUtils.isNotBlank(val)) + return val.trim(); + + return (StringUtils.isNotBlank(visitDetail.getDetails())) ? visitDetail.getDetails().trim() : ""; + } + + @NotNull + public static String getText(@Nullable List visitDetails) { + if (visitDetails == null) + return ""; + + List vals = new ArrayList<>(); + for (VisitDetail vd : visitDetails) { + String val = getText(vd); + if (StringUtils.isNotBlank(val)) + vals.add(val); + } + + return toCSV(vals); + } + + public static String toCSV(List list) { + String result = ""; + if (list.size() > 0) { + StringBuilder sb = new StringBuilder(); + for (String s : list) { + sb.append(s).append(", "); + } + result = sb.deleteCharAt(sb.length() - 2).toString(); + } + return result; + } } diff --git a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/VisitUtils.java b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/VisitUtils.java index ff7b0884..7d854086 100644 --- a/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/VisitUtils.java +++ b/opensrp-chw-anc/src/main/java/org/smartregister/chw/anc/util/VisitUtils.java @@ -27,8 +27,10 @@ import org.smartregister.immunization.service.intent.VaccineIntentService; import org.smartregister.repository.AllSharedPreferences; +import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -95,7 +97,7 @@ public static void processVisits(VisitRepository visitRepository, VisitDetailsRe if (!v.getProcessed()) { // process details - processVisitDetails(visitDetailsRepository, v.getVisitId(), v.getBaseEntityId()); + processVisitDetails(v, visitDetailsRepository, v.getVisitId(), v.getBaseEntityId()); // persist to db Event baseEvent = new Gson().fromJson(v.getPreProcessedJson(), Event.class); @@ -115,7 +117,7 @@ public static void processVisits(VisitRepository visitRepository, VisitDetailsRe context.startService(new Intent(context, RecurringIntentService.class)); } - private static void processVisitDetails(VisitDetailsRepository visitDetailsRepository, String visitID, String baseEntityID) throws Exception { + private static void processVisitDetails(Visit visit, VisitDetailsRepository visitDetailsRepository, String visitID, String baseEntityID) throws Exception { List visitDetailList = visitDetailsRepository.getVisits(visitID); List vaccineWrappers = new ArrayList<>(); List serviceWrappers = new ArrayList<>(); @@ -143,6 +145,8 @@ private static void processVisitDetails(VisitDetailsRepository visitDetailsRepos break; } } + saveVisitDetailsAsVaccine(visitDetail, baseEntityID, visit.getDate()); + visitDetailsRepository.completeProcessing(visitDetail.getVisitDetailsId()); } } @@ -161,6 +165,43 @@ private static void processVisitDetails(VisitDetailsRepository visitDetailsRepos } } + private static Vaccine saveVisitDetailsAsVaccine(VisitDetail detail, String baseEntityID, Date eventDate) { + if (!"vaccine".equalsIgnoreCase(detail.getParentCode())) + return null; + + if (Constants.HOME_VISIT.VACCINE_NOT_GIVEN.equalsIgnoreCase(detail.getParentCode())) + return null; + + Date vacDate = getDateFromString(detail.getDetails()); + if (vacDate == null) return null; + + Vaccine vaccine = new Vaccine(); + vaccine.setBaseEntityId(baseEntityID); + vaccine.setName(detail.getVisitKey()); + vaccine.setDate(vacDate); + vaccine.setCreatedAt(eventDate); + + String lastChar = vaccine.getName().substring(vaccine.getName().length() - 1); + if (StringUtils.isNumeric(lastChar)) { + vaccine.setCalculation(Integer.valueOf(lastChar)); + } else { + vaccine.setCalculation(0); + } + + JsonFormUtils.tagSyncMetadata(NCUtils.context().allSharedPreferences(), vaccine); + getVaccineRepository().add(vaccine); // persist to local db + + return vaccine; + } + + public static Date getDateFromString(String dateStr) { + try { + return NCUtils.getSaveDateFormat().parse(dateStr); + } catch (ParseException e) { + return null; + } + } + public static void saveVaccines(List tags, String baseEntityID) { for (VaccineWrapper tag : tags) { if (tag.getUpdatedVaccineDate() == null) { @@ -179,7 +220,7 @@ public static void saveVaccines(List tags, String baseEntityID) if (StringUtils.isNumeric(lastChar)) { vaccine.setCalculation(Integer.valueOf(lastChar)); } else { - vaccine.setCalculation(-1); + vaccine.setCalculation(0); } JsonFormUtils.tagSyncMetadata(NCUtils.context().allSharedPreferences(), vaccine); @@ -190,6 +231,7 @@ public static void saveVaccines(List tags, String baseEntityID) /** * Check whether a visit occurred in the last 24 hours + * * @param lastVisit The Visit instance for which you wish to check * @return true or false based on whether the visit was between 24 hours */ diff --git a/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/adapter/BaseAncHomeVisitAdapterTest.java b/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/adapter/BaseAncHomeVisitAdapterTest.java index 42351e76..004ebd11 100644 --- a/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/adapter/BaseAncHomeVisitAdapterTest.java +++ b/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/adapter/BaseAncHomeVisitAdapterTest.java @@ -39,6 +39,8 @@ public void setUp() { public void testGetCircleColorComplete() throws Exception { BaseAncHomeVisitAdapter ancHomeVisitAdapter = new BaseAncHomeVisitAdapter(context, view, myDataset); BaseAncHomeVisitAction ancHomeVisitAction = Mockito.mock(BaseAncHomeVisitAction.class); + Mockito.doReturn(true).when(ancHomeVisitAction).isEnabled(); + Mockito.doReturn(true).when(ancHomeVisitAction).isValid(); Mockito.doReturn(BaseAncHomeVisitAction.Status.COMPLETED).when(ancHomeVisitAction).getActionStatus(); int res = Whitebox.invokeMethod(ancHomeVisitAdapter, "getCircleColor", ancHomeVisitAction); @@ -49,6 +51,8 @@ public void testGetCircleColorComplete() throws Exception { public void testGetCircleColorPending() throws Exception { BaseAncHomeVisitAdapter ancHomeVisitAdapter = new BaseAncHomeVisitAdapter(context, view, myDataset); BaseAncHomeVisitAction ancHomeVisitAction = Mockito.mock(BaseAncHomeVisitAction.class); + Mockito.doReturn(true).when(ancHomeVisitAction).isEnabled(); + Mockito.doReturn(true).when(ancHomeVisitAction).isValid(); Mockito.doReturn(BaseAncHomeVisitAction.Status.PENDING).when(ancHomeVisitAction).getActionStatus(); int res = Whitebox.invokeMethod(ancHomeVisitAdapter, "getCircleColor", ancHomeVisitAction); @@ -59,6 +63,8 @@ public void testGetCircleColorPending() throws Exception { public void testGetCircleColorPartial() throws Exception { BaseAncHomeVisitAdapter ancHomeVisitAdapter = new BaseAncHomeVisitAdapter(context, view, myDataset); BaseAncHomeVisitAction ancHomeVisitAction = Mockito.mock(BaseAncHomeVisitAction.class); + Mockito.doReturn(true).when(ancHomeVisitAction).isEnabled(); + Mockito.doReturn(true).when(ancHomeVisitAction).isValid(); Mockito.doReturn(BaseAncHomeVisitAction.Status.PARTIALLY_COMPLETED).when(ancHomeVisitAction).getActionStatus(); int res = Whitebox.invokeMethod(ancHomeVisitAdapter, "getCircleColor", ancHomeVisitAction); @@ -66,10 +72,12 @@ public void testGetCircleColorPartial() throws Exception { } @Test - public void testBindClickListener() throws Exception { + public void testBindClickListenerOnValidObject() throws Exception { BaseAncHomeVisitAdapter ancHomeVisitAdapter = new BaseAncHomeVisitAdapter(context, view, myDataset); View view = Mockito.mock(View.class); BaseAncHomeVisitAction ancHomeVisitAction = Mockito.mock(BaseAncHomeVisitAction.class); + Mockito.doReturn(true).when(ancHomeVisitAction).isValid(); + Mockito.doReturn(true).when(ancHomeVisitAction).isEnabled(); Whitebox.invokeMethod(ancHomeVisitAdapter, "bindClickListener", view, ancHomeVisitAction); diff --git a/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/NCUtilsTest.java b/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/NCUtilsTest.java new file mode 100644 index 00000000..0f59b203 --- /dev/null +++ b/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/NCUtilsTest.java @@ -0,0 +1,47 @@ +package org.smartregister.chw.anc.util; + +import org.junit.Assert; +import org.junit.Test; +import org.smartregister.chw.anc.domain.VisitDetail; + +import java.util.ArrayList; +import java.util.List; + +public class NCUtilsTest { + + @Test + public void testGetText() { + List details = new ArrayList<>(); + + VisitDetail visitDetail1 = new VisitDetail(); + visitDetail1.setHumanReadable("test1"); + details.add(visitDetail1); + + VisitDetail visitDetail2 = new VisitDetail(); + visitDetail2.setHumanReadable("test2"); + details.add(visitDetail2); + + VisitDetail visitDetail3 = new VisitDetail(); + details.add(visitDetail3); + + details.add(null); + + String val = NCUtils.getText(details).trim(); + String expected = "test1, test2"; + + Assert.assertEquals(expected, val); + } + + @Test + public void testGetTextOneParam() { + + VisitDetail visitDetail1 = new VisitDetail(); + visitDetail1.setHumanReadable("test1"); + + String val = NCUtils.getText(visitDetail1).trim(); + String expected = "test1"; + + Assert.assertEquals(expected, val); + } + +} diff --git a/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/VisitNCUtilsTest.java b/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/VisitUtilsTest.java similarity index 99% rename from opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/VisitNCUtilsTest.java rename to opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/VisitUtilsTest.java index b89d11ac..5ee3c2d1 100644 --- a/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/VisitNCUtilsTest.java +++ b/opensrp-chw-anc/src/test/java/org/smartregister/chw/anc/util/VisitUtilsTest.java @@ -39,7 +39,7 @@ import static org.junit.Assert.assertTrue; @PrepareForTest({AncLibrary.class, JsonFormUtils.class, ImmunizationLibrary.class}) -public class VisitNCUtilsTest { +public class VisitUtilsTest { @Rule public PowerMockRule rule = new PowerMockRule(); diff --git a/opensrp-chw-pnc/src/main/java/org/smartregister/chw/pnc/activity/BasePncMemberProfileActivity.java b/opensrp-chw-pnc/src/main/java/org/smartregister/chw/pnc/activity/BasePncMemberProfileActivity.java index d2974d18..f5a8bbfd 100644 --- a/opensrp-chw-pnc/src/main/java/org/smartregister/chw/pnc/activity/BasePncMemberProfileActivity.java +++ b/opensrp-chw-pnc/src/main/java/org/smartregister/chw/pnc/activity/BasePncMemberProfileActivity.java @@ -28,11 +28,8 @@ public static void startMe(Activity activity, MemberObject memberObject, String intent.putExtra(FAMILY_HEAD_NAME, familyHeadName); intent.putExtra(FAMILY_HEAD_PHONE, familyHeadPhoneNumber); activity.startActivity(intent); - - } - @Override protected void setupViews() { super.setupViews(); @@ -40,11 +37,8 @@ protected void setupViews() { titleView.setText(getString(R.string.return_to_all_pnc_women)); record_reccuringvisit_done_bar.setVisibility(View.GONE); textViewAncVisitNot.setVisibility(View.GONE); - - } - @Override public void setMemberName(String memberName) { basePncMemberProfileInteractor.getPncMotherNameDetails(MEMBER_OBJECT, text_view_anc_member_name, imageView); @@ -58,7 +52,6 @@ public void setMemberGA(String memberGA) { } } - @Override protected String getProfileType() { return Constants.MEMBER_PROFILE_TYPES.PNC; @@ -69,7 +62,6 @@ public void setRecordVisitTitle(String title) { textview_record_anc_visit.setText(getString(R.string.record_pnc_visit)); } - @Override public void openMedicalHistory() { BasePncMedicalHistoryActivity.startMe(this, MEMBER_OBJECT);