OpenSRP/opensrp-client-anc

View on GitHub
opensrp-anc/src/main/java/org/smartregister/anc/library/model/ContactVisit.java

Summary

Maintainability
F
3 days
Test Coverage
package org.smartregister.anc.library.model;

import android.text.TextUtils;

import com.vijay.jsonwizard.constants.JsonFormConstants;
import com.vijay.jsonwizard.rules.RuleConstant;

import org.apache.commons.lang3.StringUtils;
import org.jeasy.rules.api.Facts;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.smartregister.anc.library.AncLibrary;
import org.smartregister.anc.library.domain.WomanDetail;
import org.smartregister.anc.library.domain.YamlConfig;
import org.smartregister.anc.library.domain.YamlConfigItem;
import org.smartregister.anc.library.repository.PartialContactRepository;
import org.smartregister.anc.library.repository.PatientRepository;
import org.smartregister.anc.library.repository.PreviousContactRepository;
import org.smartregister.anc.library.util.ANCFormUtils;
import org.smartregister.anc.library.util.ANCJsonFormUtils;
import org.smartregister.anc.library.util.ConstantsUtils;
import org.smartregister.anc.library.util.DBConstantsUtils;
import org.smartregister.anc.library.util.FilePathUtils;
import org.smartregister.clientandeventmodel.Event;
import org.smartregister.util.JsonFormUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import timber.log.Timber;

public class ContactVisit {
    private final Map<String, String> details;
    private final String referral;
    private final String baseEntityId;
    private final int nextContact;
    private final String nextContactVisitDate;
    private final PartialContactRepository partialContactRepository;
    private final List<PartialContact> partialContactList;
    private Facts facts;
    private List<String> formSubmissionIDs;
    private WomanDetail womanDetail;
    private final Map<String, Integer> attentionFlagCountMap = new HashMap<>();
    private final List<String> parsableFormsList =
            Arrays.asList(ConstantsUtils.JsonFormUtils.ANC_QUICK_CHECK, ConstantsUtils.JsonFormUtils.ANC_PROFILE,
                    ConstantsUtils.JsonFormUtils.ANC_SYMPTOMS_FOLLOW_UP, ConstantsUtils.JsonFormUtils.ANC_PHYSICAL_EXAM,
                    ConstantsUtils.JsonFormUtils.ANC_TEST, ConstantsUtils.JsonFormUtils.ANC_COUNSELLING_TREATMENT);
    private Map<String, Long> currentClientTasks = new HashMap<>();
    private final ANCFormUtils ancFormUtils = new ANCFormUtils();

    public ContactVisit(Map<String, String> details, String referral, String baseEntityId, int nextContact,
                        String nextContactVisitDate, PartialContactRepository partialContactRepository,
                        List<PartialContact> partialContactList) {
        this.details = details;
        this.referral = referral;
        this.baseEntityId = baseEntityId;
        this.nextContact = nextContact;
        this.nextContactVisitDate = nextContactVisitDate;
        this.partialContactRepository = partialContactRepository;
        this.partialContactList = partialContactList;
    }

    public Facts getFacts() {
        return facts;
    }

    public List<String> getFormSubmissionIDs() {
        return formSubmissionIDs;
    }

    public WomanDetail getWomanDetail() {
        return womanDetail;
    }

    public ContactVisit invoke() throws Exception {
        facts = new Facts();
        formSubmissionIDs = new ArrayList<>();

        getCurrentClientsTasks(baseEntityId);
        updateEventAndRequiredStepsField(baseEntityId, partialContactRepository, partialContactList, facts, formSubmissionIDs);

        womanDetail = getWomanDetail(baseEntityId, nextContactVisitDate, nextContact);
        processAttentionFlags(womanDetail, facts);

        if (referral != null) {
            int yellowFlagCount = 0;
            int redFlagCount = 0;
            if (details.containsKey(DBConstantsUtils.KeyUtils.YELLOW_FLAG_COUNT) && details.get(DBConstantsUtils.KeyUtils.YELLOW_FLAG_COUNT) != null) {
                yellowFlagCount = Integer.valueOf(details.get(DBConstantsUtils.KeyUtils.YELLOW_FLAG_COUNT));
            }

            if (details.containsKey(DBConstantsUtils.KeyUtils.RED_FLAG_COUNT) && details.get(DBConstantsUtils.KeyUtils.RED_FLAG_COUNT) != null) {
                redFlagCount = Integer.valueOf(details.get(DBConstantsUtils.KeyUtils.RED_FLAG_COUNT));
            }

            womanDetail.setReferral(true);
            womanDetail.setRedFlagCount(redFlagCount);
            womanDetail.setYellowFlagCount(yellowFlagCount);
            womanDetail.setContactStatus(details.get(DBConstantsUtils.KeyUtils.PREVIOUS_CONTACT_STATUS));
            womanDetail.setPreviousContactStatus(details.get(DBConstantsUtils.KeyUtils.PREVIOUS_CONTACT_STATUS));
            womanDetail.setLastContactRecordDate(details.get(DBConstantsUtils.KeyUtils.LAST_CONTACT_RECORD_DATE));
        }
        PatientRepository.updateContactVisitDetails(womanDetail, true);
        return this;
    }

    /**
     * Returns a {@link Map} of the tasks keys and task id.  These are used to delete the tasks in case a test with the same key is completed doing the current contact.
     *
     * @param baseEntityId {@link String} Client's base entity id.
     */
    private void getCurrentClientsTasks(String baseEntityId) {
        List<Task> tasksList = AncLibrary.getInstance().getContactTasksRepository().getTasks(baseEntityId, null);
        if (tasksList != null && tasksList.size() > 0) {
            Map<String, Long> tasksMap = new HashMap<>();
            for (int i = 0; i < tasksList.size(); i++) {
                Task task = tasksList.get(i);
                if (task != null) {
                    tasksMap.put(task.getKey(), task.getId());
                }
            }
            setCurrentClientTasks(tasksMap);
        }
    }

    private void updateEventAndRequiredStepsField(String baseEntityId, PartialContactRepository partialContactRepository,
                                                  List<PartialContact> partialContactList, Facts facts, List<String> formSubmissionIDs) throws Exception {
        if (partialContactList != null) {
            Collections.sort(partialContactList, (firstPartialContact, secondPartialContact) -> firstPartialContact.getSortOrder().compareTo(secondPartialContact.getSortOrder()));

            for (PartialContact partialContact : partialContactList) {
                JSONObject formObject = ANCJsonFormUtils.toJSONObject(
                        partialContact.getFormJsonDraft() != null ? partialContact.getFormJsonDraft() :
                                partialContact.getFormJson());

                if (formObject != null) {
                    //process form details
                    if (parsableFormsList.contains(partialContact.getType())) {
                        processFormFieldKeyValues(baseEntityId, formObject, String.valueOf(partialContact.getContactNo()));
                    }

                    //process attention flags
                    ANCFormUtils.processRequiredStepsField(facts, formObject);

                    //process events
                    Event event = ANCJsonFormUtils.processContactFormEvent(formObject, baseEntityId);
                    formSubmissionIDs.add(event.getFormSubmissionId());

                    JSONObject eventJson = new JSONObject(ANCJsonFormUtils.gson.toJson(event));
                    eventJson.put(JsonFormConstants.Properties.DETAILS, JsonFormUtils.getJSONObject(formObject, JsonFormConstants.Properties.DETAILS));
                    AncLibrary.getInstance().getEcSyncHelper().addEvent(baseEntityId, eventJson);

                    processTasks(formObject);
                }

                //Remove partial contact
                partialContactRepository.deletePartialContact(partialContact.getId());
            }
        }
    }

    private WomanDetail getWomanDetail(String baseEntityId, String nextContactVisitDate, Integer nextContact) {
        WomanDetail womanDetail = new WomanDetail();
        womanDetail.setBaseEntityId(baseEntityId);
        womanDetail.setNextContact(nextContact);
        womanDetail.setNextContactDate(nextContactVisitDate);
        womanDetail.setContactStatus(ConstantsUtils.AlertStatusUtils.TODAY);
        womanDetail.setPreviousContactStatus(ConstantsUtils.AlertStatusUtils.TODAY);
        return womanDetail;
    }

    private void processAttentionFlags(WomanDetail patientDetail, Facts facts) throws IOException {
        Iterable<Object> ruleObjects = AncLibrary.getInstance().readYaml(FilePathUtils.FileUtils.ATTENTION_FLAGS);
        int redCount = 0;
        int yellowCount = 0;

        for (Object ruleObject : ruleObjects) {
            YamlConfig attentionFlagConfig = (YamlConfig) ruleObject;

            for (YamlConfigItem yamlConfigItem : attentionFlagConfig.getFields()) {
                if (AncLibrary.getInstance().getAncRulesEngineHelper().getRelevance(facts, yamlConfigItem.getRelevance())) {
                   if(attentionFlagConfig.getGroup().equals(ConstantsUtils.AttentionFlagUtils.RED))
                   {
                       redCount = redCount+1;
                   }
                   else if(attentionFlagConfig.getGroup().equals(ConstantsUtils.AttentionFlagUtils.YELLOW))
                   {
                       yellowCount = yellowCount+1;
                   }

                }
            }
        }

        patientDetail.setRedFlagCount(redCount);
        patientDetail.setYellowFlagCount(yellowCount);
    }

    private void processFormFieldKeyValues(String baseEntityId, JSONObject object, String contactNo) throws Exception {
        if (object != null) {
            persistRequiredInvisibleFields(baseEntityId, contactNo, object);
            Iterator<String> keys = object.keys();

            while (keys.hasNext()) {
                String key = keys.next();

                if (key.startsWith(RuleConstant.STEP)) {
                    JSONArray stepArray = object.getJSONObject(key).getJSONArray(JsonFormConstants.FIELDS);

                    for (int i = 0; i < stepArray.length(); i++) {
                        JSONObject fieldObject = stepArray.getJSONObject(i);
                        ANCFormUtils.processSpecialWidgets(fieldObject);

                        if (fieldObject.getString(JsonFormConstants.TYPE).equals(JsonFormConstants.EXPANSION_PANEL)) {
                            saveExpansionPanelPreviousValues(baseEntityId, fieldObject, contactNo);
                            continue;
                        }

                        //Do not save empty checkbox values with nothing inside square braces ([])
                        if (fieldObject.has(JsonFormConstants.VALUE) &&
                                !TextUtils.isEmpty(fieldObject.getString(JsonFormConstants.VALUE)) &&
                                !isCheckboxValueEmpty(fieldObject)) {

                            fieldObject.put(PreviousContactRepository.CONTACT_NO, contactNo);
                            ancFormUtils.savePreviousContactItem(baseEntityId, fieldObject);
                        }

                        if (fieldObject.has(ConstantsUtils.KeyUtils.SECONDARY_VALUES) &&
                                fieldObject.getJSONArray(ConstantsUtils.KeyUtils.SECONDARY_VALUES).length() > 0) {
                            JSONArray secondaryValues = fieldObject.getJSONArray(ConstantsUtils.KeyUtils.SECONDARY_VALUES);
                            for (int count = 0; count < secondaryValues.length(); count++) {
                                JSONObject secondaryValuesJSONObject = secondaryValues.getJSONObject(count);
                                secondaryValuesJSONObject.put(PreviousContactRepository.CONTACT_NO, contactNo);
                                ancFormUtils.savePreviousContactItem(baseEntityId, secondaryValuesJSONObject);
                            }
                        }
                    }
                }
            }
        }

    }

    private void processTasks(JSONObject formObject) {
        try {
            String encounterType = formObject.getString(ConstantsUtils.JsonFormKeyUtils.ENCOUNTER_TYPE);
            if (formObject.has(ConstantsUtils.JsonFormKeyUtils.ENCOUNTER_TYPE) && StringUtils.isNotBlank(encounterType) && ConstantsUtils.JsonFormUtils.ANC_TEST_ENCOUNTER_TYPE.equals(encounterType)) {
                JSONObject dueStep = formObject.optJSONObject(JsonFormConstants.STEP1);
                if (dueStep != null && dueStep.has(JsonFormConstants.STEP_TITLE) && ConstantsUtils.DUE.equals(dueStep.getString(JsonFormConstants.STEP_TITLE))) {
                    JSONArray stepFields = dueStep.optJSONArray(JsonFormConstants.FIELDS);
                    if (stepFields != null && stepFields.length() > 0) {
                        saveOrDeleteTasks(stepFields);
                    }
                }
            }
        } catch (JSONException e) {
            Timber.e(e, " --> processTasks");
        }
    }

    /***
     * Method that persist previous invisible required fields
     * @param baseEntityId unique Id for the woman
     * @param contactNo the contact number
     * @param object main form json object
     * @throws JSONException exception thrown
     */
    private void persistRequiredInvisibleFields(String baseEntityId, String contactNo, JSONObject object) throws JSONException {
        if (object.has(JsonFormConstants.INVISIBLE_REQUIRED_FIELDS)) {
            String key = JsonFormConstants.INVISIBLE_REQUIRED_FIELDS + "_" + object.getString(ConstantsUtils.JsonFormKeyUtils.ENCOUNTER_TYPE).toLowerCase().replace(" ", "_");
            ancFormUtils.savePreviousContactItem(baseEntityId, new JSONObject().put(JsonFormConstants.KEY, key)
                    .put(JsonFormConstants.VALUE, object.getString(JsonFormConstants.INVISIBLE_REQUIRED_FIELDS))
                    .put(PreviousContactRepository.CONTACT_NO, contactNo));
        }
    }

    private void saveExpansionPanelPreviousValues(String baseEntityId, JSONObject fieldObject, String contactNo) throws JSONException {
        if (fieldObject != null) {
            JSONArray value = fieldObject.optJSONArray(JsonFormConstants.VALUE);
            if (value == null) {
                return;
            }
            for (int j = 0; j < value.length(); j++) {
                JSONObject valueItem = value.getJSONObject(j);
                ancFormUtils.saveExpansionPanelValues(baseEntityId, contactNo, valueItem);
            }
        }
    }

    private boolean isCheckboxValueEmpty(JSONObject fieldObject) throws JSONException {
        if (!fieldObject.has(JsonFormConstants.VALUE)) {
            return true;
        }
        String currentValue = fieldObject.getString(JsonFormConstants.VALUE);
        return TextUtils.equals(currentValue, "[]") || (currentValue.length() == 2
                && currentValue.startsWith("[") && currentValue.endsWith("]"));
    }

    private void saveOrDeleteTasks(@NotNull JSONArray stepFields) throws JSONException {
        for (int i = 0; i < stepFields.length(); i++) {
            JSONObject field = stepFields.getJSONObject(i);
            if (field != null && field.has(JsonFormConstants.IS_VISIBLE) && field.getBoolean(JsonFormConstants.IS_VISIBLE)) {
                JSONArray jsonArray = field.optJSONArray(JsonFormConstants.VALUE);
                String key = field.optString(JsonFormConstants.KEY);
                if (jsonArray == null || (jsonArray.length() == 0)) {
                    if (getCurrentClientTasks() != null && !getCurrentClientTasks().containsKey(key)) {
                        saveTasks(field);
                    }
                } else {
                    if (StringUtils.isNotBlank(key) && getCurrentClientTasks() != null) {
                        if (checkTestsStatus(jsonArray)) {
                            if (!getCurrentClientTasks().containsKey(key)) {
                                saveTasks(field);
                            }
                        } else {
                            Long tasksId = getCurrentClientTasks().get(key);
                            if (tasksId != null) {
                                AncLibrary.getInstance().getContactTasksRepository().deleteContactTask(tasksId);
                            }
                        }
                    }
                }
            }
        }
    }

    public Map<String, Long> getCurrentClientTasks() {
        return currentClientTasks;
    }

    public void setCurrentClientTasks(Map<String, Long> currentClientTasks) {
        this.currentClientTasks = currentClientTasks;
    }

    private void saveTasks(JSONObject field) {
        if (field != null) {
            String key = field.optString(JsonFormConstants.KEY);
            AncLibrary.getInstance().getContactTasksRepository().saveOrUpdateTasks(getTask(field, key));
        }
    }

    /**
     * Checks where a test qualifies to be a tasks.  This happens in case a test is marked as ordered or not done;
     *
     * @param valueArray {@link JSONArray} the expansion panel values
     * @return isTasks {@link Boolean} true/false if true then it means the test qualifies to be a task.
     */
    private boolean checkTestsStatus(JSONArray valueArray) {
        boolean isTask = false;
        try {
            for (int i = 0; i < valueArray.length(); i++) {
                JSONObject value = valueArray.getJSONObject(i);
                if (value != null && value.has(JsonFormConstants.TYPE) && JsonFormConstants.EXTENDED_RADIO_BUTTON.equals(value.getString(JsonFormConstants.TYPE))) {
                    JSONArray givenValue = value.getJSONArray(JsonFormConstants.VALUES);
                    if (givenValue.length() > 0) {
                        String firstValue = givenValue.optString(0);
                        if (StringUtils.isNotBlank(firstValue) && (firstValue.contains(ConstantsUtils.AncRadioButtonOptionTypesUtils.ORDERED) || firstValue.contains(ConstantsUtils.AncRadioButtonOptionTypesUtils.NOT_DONE))) {
                            isTask = true;
                        }
                    }
                    break;
                }
            }
        } catch (JSONException e) {
            Timber.e(e, " --> checkTestsStatus");
        }
        return isTask;
    }

    @NotNull
    private Task getTask(JSONObject field, String key) {
        Task task = new Task();
        task.setBaseEntityId(baseEntityId);
        task.setKey(key);
        task.setValue(String.valueOf(field));
        task.setUpdated(false);
        task.setComplete(ANCJsonFormUtils.checkIfTaskIsComplete(field));
        task.setCreatedAt(Calendar.getInstance().getTimeInMillis());
        return task;
    }

    protected PreviousContactRepository getPreviousContactRepository() {
        return AncLibrary.getInstance().getPreviousContactRepository();
    }
}