gov.nih.nci.cabig.caaers.domain.repository.ReportValidationServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.cabig.caaers.domain.repository.ReportValidationServiceImpl.java

Source

/*******************************************************************************
 * Copyright SemanticBits, Northwestern University and Akaza Research
 * 
 * Distributed under the OSI-approved BSD 3-Clause License.
 * See http://ncip.github.com/caaers/LICENSE.txt for details.
 ******************************************************************************/
package gov.nih.nci.cabig.caaers.domain.repository;

import gov.nih.nci.cabig.caaers.CaaersSystemException;
import gov.nih.nci.cabig.caaers.domain.*;
import gov.nih.nci.cabig.caaers.domain.attribution.AdverseEventAttribution;
import gov.nih.nci.cabig.caaers.domain.expeditedfields.ExpeditedReportSection;
import gov.nih.nci.cabig.caaers.domain.expeditedfields.ExpeditedReportTree;
import gov.nih.nci.cabig.caaers.domain.expeditedfields.TreeNode;
import gov.nih.nci.cabig.caaers.domain.expeditedfields.UnsatisfiedProperty;
import gov.nih.nci.cabig.caaers.domain.report.Report;
import gov.nih.nci.cabig.caaers.service.EvaluationService;
import gov.nih.nci.cabig.caaers.service.ReportSubmittability;
import gov.nih.nci.cabig.ctms.domain.CodedEnum;
import gov.nih.nci.cabig.ctms.domain.DomainObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.annotation.Required;

import java.util.*;

import static gov.nih.nci.cabig.caaers.domain.expeditedfields.ExpeditedReportSection.*;

/**
 * Provides method to validate the completeness of a {@link gov.nih.nci.cabig.caaers.domain.report.Report}
 * @author Sameer Sawant
 * @author Biju Joseph
 */
public class ReportValidationServiceImpl implements ReportValidationService {

    /** The Constant log. */
    private static final Log log = LogFactory.getLog(ReportValidationServiceImpl.class);

    /** The expedited report tree. */
    private ExpeditedReportTree expeditedReportTree;

    /** The evaluation service. */
    private EvaluationService evaluationService;

    /**
     * Checks if the {@link gov.nih.nci.cabig.caaers.domain.report.Report} can be submitted, based on the mandatory section
     * and other instantiated sections.
     *
     * @param report the report
     * @return the report submittability
     */
    public ReportSubmittability isSubmittable(Report report) {

        Map<Integer, Collection<ExpeditedReportSection>> mandatorySectionMap = evaluationService
                .mandatorySections(report.getAeReport(), report.getReportDefinition());

        return validate(report, Arrays.asList(ExpeditedReportSection.values()),
                mandatorySectionMap.get(report.getReportDefinition().getId()));
    }

    /**
     * Will tell whether all the mandatory field for this report is duly filled.
     * Internally this will call the validate method for each element having children in the {@link ExpeditedReportTree}
     *
     * @param report the report
     * @param reportSections - The sections that are to be validated.
     * @param mandatorySections - The sections which are marked mandatory in mandatory section rules.
     * @return ErrorMessages
     */
    public ReportSubmittability validate(Report report, Collection<ExpeditedReportSection> reportSections,
            Collection<ExpeditedReportSection> mandatorySections) {

        ReportSubmittability messages = new ReportSubmittability();
        try {
            ExpeditedAdverseEventReport aeReport = report.getAeReport();

            //evaluate mandatoryness
            evaluationService.evaluateMandatoryness(aeReport, report);

            List<String> nonSelfReferencedMandatoryFields = report.getPathOfNonSelfReferencedMandatoryFields();
            List<String> selfReferencedMandatoryFields = report.getPathOfSelfReferencedMandatoryFields();
            for (ExpeditedReportSection section : reportSections) {
                if (section == null)
                    throw new NullPointerException("The mandatory sections collection must not contain nulls");
                validate(aeReport, nonSelfReferencedMandatoryFields, selfReferencedMandatoryFields, section,
                        messages);
            }

            //biz rule - Attribution validation should be done if the ReportDefinition says that it is attributable
            if (report.getReportDefinition().getAttributionRequired()) {

                for (AdverseEvent ae : aeReport.getAdverseEvents()) {
                    if (!ae.lookForPositiveAttribution())
                        continue;

                    Attribution max = null;
                    for (AdverseEventAttribution<?> attribution : ae.getAdverseEventAttributions()) {
                        if (attribution.getAttribution() == null) {
                            max = null;
                            break;
                        } //special case when people click save again (after an error).
                        if (max == null || attribution.getAttribution().getCode() > max.getCode()) {
                            max = attribution.getAttribution();
                        }
                    }
                    if (max == null || max.getCode() < Attribution.POSSIBLE.getCode()) {
                        messages.addValidityMessage(ExpeditedReportSection.ATTRIBUTION_SECTION, String.format(
                                "The adverse event, '%s, ' is not attributed to a cause. An attribution of possible or higher must be selected for at least one of the causes.",
                                ae.getAdverseEventTerm().getUniversalTerm() != null
                                        ? ae.getAdverseEventTerm().getUniversalTerm()
                                        : ae.getDetailsForOther()));
                    }
                }
            }

            //biz rule - Physician Sign-Off should be true if the ReportDefinition says that Physician Sign-Off is needed.
            if (report.getReportDefinition().getPhysicianSignOff()) {
                if (report.getPhysicianSignoff() == null || !report.getPhysicianSignoff()) {
                    messages.addValidityMessage(ExpeditedReportSection.SUBMIT_REPORT_SECTION,
                            "Physician sign-off is mandatory for this report.");
                }
            }

            //additional business rules - if a section is mandatory at least one active child should be present.
            if (CollectionUtils.isNotEmpty(mandatorySections)) {
                for (ExpeditedReportSection mandatorySecton : mandatorySections) {

                    //special case - Medical information section has a list but should be ignored
                    if (mandatorySecton.equals(ExpeditedReportSection.MEDICAL_INFO_SECTION))
                        continue;

                    boolean sectionFilled = true;

                    if (mandatorySecton.equals(ExpeditedReportSection.STUDY_INTERVENTIONS)) {

                        if (mandatorySections.contains(ExpeditedReportSection.AGENTS_INTERVENTION_SECTION)
                                || mandatorySections.contains(ExpeditedReportSection.RADIATION_INTERVENTION_SECTION)
                                || mandatorySections.contains(ExpeditedReportSection.SURGERY_INTERVENTION_SECTION)
                                || mandatorySections.contains(ExpeditedReportSection.MEDICAL_DEVICE_SECTION)) {
                            continue;
                        }

                        //special case - need to check at least one of its children is present instead.
                        sectionFilled = isElementPresentInSection(aeReport,
                                ExpeditedReportSection.AGENTS_INTERVENTION_SECTION)
                                || isElementPresentInSection(aeReport,
                                        ExpeditedReportSection.RADIATION_INTERVENTION_SECTION)
                                || isElementPresentInSection(aeReport,
                                        ExpeditedReportSection.SURGERY_INTERVENTION_SECTION)
                                || isElementPresentInSection(aeReport,
                                        ExpeditedReportSection.MEDICAL_DEVICE_SECTION);

                    } else {
                        sectionFilled = isElementPresentInSection(aeReport, mandatorySecton);
                    }

                    if (!sectionFilled) {
                        messages.addValidityMessage(mandatorySecton,
                                String.format("The section '%s' is mandatory for this report and cannot be empty",
                                        mandatorySecton.getDisplayName()));
                    }
                }
            }

        } catch (RuntimeException re) {
            log.error("error while evaluating report submit-ability", re);
            messages.addValidityMessage(ExpeditedReportSection.BASICS_SECTION, re.getMessage());
        }
        return messages;
    }

    /**
     * Will check if the {@link gov.nih.nci.cabig.caaers.domain.ExpeditedAdverseEventReport} has the values for elements
     * associated with the section.
     *
     * @param aeReport the ae report
     * @param section the section
     * @return true, if is element present in section
     */
    protected boolean isElementPresentInSection(ExpeditedAdverseEventReport aeReport,
            ExpeditedReportSection section) {
        TreeNode sectionNode = expeditedReportTree.getNodeForSection(section);
        List<TreeNode> listNodes = new ArrayList<TreeNode>();
        sectionNode.recursivelyCollectListNodes(listNodes);

        if (listNodes.isEmpty())
            return true;

        for (TreeNode listNode : listNodes) {
            MutablePropertyValues mpvs = listNode.getPropertyValuesFrom(aeReport);
            for (PropertyValue pv : mpvs.getPropertyValues()) {
                Object obj = pv.getValue();
                if (obj instanceof DomainObject) {
                    if (obj instanceof Retireable && ((Retireable) obj).isRetired())
                        continue;
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Validate.
     *
     * @param aeReport the ae report
     * @param mandatoryFields the mandatory fields
     * @param selfReferencedMandatoryFields the self referenced mandatory fields
     * @param section the section
     * @param messages the messages
     */
    @SuppressWarnings("unchecked")
    private void validate(ExpeditedAdverseEventReport aeReport, List<String> mandatoryFields,
            List<String> selfReferencedMandatoryFields, ExpeditedReportSection section,
            ReportSubmittability messages) {
        BeanWrapper bw = new BeanWrapperImpl(aeReport);

        TreeNode sectionNode = expeditedReportTree.getNodeForSection(section);
        if (sectionNode == null)
            throw new CaaersSystemException("There is no section node in the report tree for " + section.name()
                    + ".  This shouldn't be possible.");

        List<String> applicableFields = new LinkedList<String>();
        for (String field : mandatoryFields) {
            TreeNode n = sectionNode.find(field);
            if (n == null)
                continue;
            applicableFields.add(field);
        }
        List<UnsatisfiedProperty> unsatisfied = expeditedReportTree.verifyPropertiesPresent(applicableFields,
                aeReport);
        for (UnsatisfiedProperty uProp : unsatisfied) {
            if (isErrorApplicable(bw, section, uProp))
                messages.addMissingField(section, uProp.getDisplayName(), uProp.getBeanPropertyName());
        }

        //evaluate the self referenced fields

        for (String fieldPath : selfReferencedMandatoryFields) {
            TreeNode node = expeditedReportTree.find(fieldPath);

            if (node != null && sectionNode.isAncestorOf(node)) {
                Object value = bw.getPropertyValue(fieldPath);

                if (value == null
                        || (value instanceof CodedEnum && String.valueOf(value).contains("Please select"))) {
                    messages.addMissingField(section, node.getDisplayName(), fieldPath);
                }
            }

        }

    }

    /**
     * Applicability criteria :-
     *   1. Lab category micro-biology (then baseline and
     * @param section
     * @param property
     * @return
     */
    public boolean isErrorApplicable(BeanWrapper bw, ExpeditedReportSection section, UnsatisfiedProperty property) {
        String propertyPath = property.getBeanPropertyName();
        int dotIndex = propertyPath.indexOf('.');

        String parentEntityPath = dotIndex == -1 ? propertyPath : propertyPath.substring(0, dotIndex);

        if (section == LABS_SECTION) {
            Lab lab = (Lab) bw.getPropertyValue(parentEntityPath);
            return isLabErrorApplicable(lab, propertyPath);
        }
        if (section == MEDICAL_DEVICE_SECTION) {
            MedicalDevice device = (MedicalDevice) bw.getPropertyValue(parentEntityPath);
            return isMedicalDeviceErrorApplicable(device, propertyPath);
        }

        if (section == DESCRIPTION_SECTION) {
            AdverseEventResponseDescription description = (AdverseEventResponseDescription) bw
                    .getPropertyValue(parentEntityPath);
            return isDescriptionErrorApplicable(description, propertyPath);
        }

        if (section == CONCOMITANT_MEDICATION_SECTION) {
            ConcomitantMedication conMed = (ConcomitantMedication) bw.getPropertyValue(parentEntityPath);
            return isConMedErrorApplicable(conMed, propertyPath);
        }

        return true;
    }

    private boolean isLabErrorApplicable(Lab lab, String propertyPath) {

        if (propertyPath.contains("baseline") || propertyPath.contains("nadir") || propertyPath.contains("recovery")
                || propertyPath.contains("infectiousAgent")) {
            if (lab == null || lab.getLabTerm() == null || lab.getLabTerm().getCategory() == null)
                return true;
            if (lab.getLabTerm().getCategory().getName().contains("Microbiology"))
                return false;
        }
        if (propertyPath.contains("labDate") || propertyPath.contains("site")) {
            if (lab == null || lab.getLabTerm() == null || lab.getLabTerm().getCategory() == null)
                return true;
            if (!lab.getLabTerm().getCategory().getName().contains("Microbiology"))
                return false;
        }

        return true;
    }

    private boolean isDescriptionErrorApplicable(AdverseEventResponseDescription description, String propertyPath) {
        if (propertyPath.contains("recoveryDate")) {
            if (description == null || description.getPresentStatus() == null
                    || description.getPresentStatus() != PostAdverseEventStatus.DEAD
                    || description.getPresentStatus() != PostAdverseEventStatus.RECOVERED_WITH_SEQUELAE
                    || description.getPresentStatus() != PostAdverseEventStatus.RECOVERED_WITHOUT_SEQUELAE)
                return false;
        }

        return true;
    }

    private boolean isMedicalDeviceErrorApplicable(MedicalDevice medicalDevice, String propertyPath) {
        if (propertyPath.contains("reprocessorName") || propertyPath.contains("reprocessorAddress")) {
            if (medicalDevice == null || medicalDevice.getDeviceReprocessed() == null
                    || medicalDevice.getDeviceReprocessed() != ReprocessedDevice.YES)
                return false;
        }
        if (propertyPath.contains("returnedDate")) {
            if (medicalDevice == null || medicalDevice.getEvaluationAvailability() == null
                    || medicalDevice.getEvaluationAvailability() != Availability.RETURNED)
                return false;
        }

        return true;
    }

    private boolean isConMedErrorApplicable(ConcomitantMedication conMed, String propertyPath) {
        if (propertyPath.contains("endDate")) {
            if (conMed == null || conMed.getStillTakingMedications() == null
                    || conMed.getStillTakingMedications().equals(true))
                return false;
        }
        return true;
    }

    /**
     * Sets the expedited report tree.
     *
     * @param expeditedReportTree the new expedited report tree
     */
    @Required
    public void setExpeditedReportTree(final ExpeditedReportTree expeditedReportTree) {
        this.expeditedReportTree = expeditedReportTree;
    }

    /**
     * Sets the evaluation service.
     *
     * @param evaluationService the new evaluation service
     */
    @Required
    public void setEvaluationService(EvaluationService evaluationService) {
        this.evaluationService = evaluationService;
    }
}