gov.nih.nci.cabig.caaers.api.impl.SafetyReportServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.cabig.caaers.api.impl.SafetyReportServiceImpl.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.api.impl;

import gov.nih.nci.cabig.caaers.dao.ExpeditedAdverseEventReportDao;
import gov.nih.nci.cabig.caaers.dao.ParticipantDao;
import gov.nih.nci.cabig.caaers.dao.StudyDao;
import gov.nih.nci.cabig.caaers.dao.StudyParticipantAssignmentDao;
import gov.nih.nci.cabig.caaers.domain.AdverseEvent;
import gov.nih.nci.cabig.caaers.domain.AdverseEventReportingPeriod;
import gov.nih.nci.cabig.caaers.domain.ExpeditedAdverseEventReport;
import gov.nih.nci.cabig.caaers.domain.Organization;
import gov.nih.nci.cabig.caaers.domain.Participant;
import gov.nih.nci.cabig.caaers.domain.ReportStatus;
import gov.nih.nci.cabig.caaers.domain.Study;
import gov.nih.nci.cabig.caaers.domain.StudySite;
import gov.nih.nci.cabig.caaers.domain.dto.ReportDefinitionWrapper.ActionType;
import gov.nih.nci.cabig.caaers.domain.report.Report;
import gov.nih.nci.cabig.caaers.domain.report.ReportDefinition;
import gov.nih.nci.cabig.caaers.domain.repository.ReportRepository;
import gov.nih.nci.cabig.caaers.domain.validation.ExpeditedAdverseEventReportValidator;
import gov.nih.nci.cabig.caaers.event.EventFactory;
import gov.nih.nci.cabig.caaers.integration.schema.aereport.AdverseEventReport;
import gov.nih.nci.cabig.caaers.integration.schema.aereport.BaseAdverseEventReport;
import gov.nih.nci.cabig.caaers.integration.schema.aereport.BaseReportType;
import gov.nih.nci.cabig.caaers.integration.schema.aereport.BaseReports;
import gov.nih.nci.cabig.caaers.integration.schema.aereportid.ReportIdCriteria;
import gov.nih.nci.cabig.caaers.integration.schema.common.CaaersServiceResponse;
import gov.nih.nci.cabig.caaers.integration.schema.common.ResponseDataType;
import gov.nih.nci.cabig.caaers.integration.schema.common.ServiceResponse;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.EvaluateAndInitiateInputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.EvaluateAndInitiateOutputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.RecommendedActions;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.SaveAndEvaluateAEsOutputMessage;
import gov.nih.nci.cabig.caaers.service.AdeersIntegrationFacade;
import gov.nih.nci.cabig.caaers.service.DomainObjectImportOutcome;
import gov.nih.nci.cabig.caaers.service.ReportSubmissionService;
import gov.nih.nci.cabig.caaers.service.migrator.BaseExpeditedAdverseEventReportConverter;
import gov.nih.nci.cabig.caaers.service.migrator.EvaluateAndInitiateReportConverter;
import gov.nih.nci.cabig.caaers.service.migrator.ExpeditedAdverseEventReportConverter;
import gov.nih.nci.cabig.caaers.service.migrator.report.ExpeditedReportMigrator;
import gov.nih.nci.cabig.caaers.service.synchronizer.report.ExpeditedAdverseEventReportSynchronizer;
import gov.nih.nci.cabig.caaers.utils.DateUtils;
import gov.nih.nci.cabig.caaers.validation.CaaersValidationException;
import gov.nih.nci.cabig.caaers.validation.ValidationError;
import gov.nih.nci.cabig.caaers.validation.ValidationErrors;
import gov.nih.nci.cabig.caaers.ws.faults.CaaersFault;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
import org.springframework.transaction.annotation.Transactional;

public class SafetyReportServiceImpl {
    private static Log logger = LogFactory.getLog(SafetyReportServiceImpl.class);

    /**   Base Expedited Report Converter. **/
    private BaseExpeditedAdverseEventReportConverter baseEaeConverter;

    /**   Expedited Report Converter. **/
    private ExpeditedAdverseEventReportConverter eaeConverter;

    private ParticipantServiceImpl participantService;

    private ParticipantDao participantDao;

    private StudyDao studyDao;

    private MessageSource messageSource;

    private ExpeditedAdverseEventReportDao expeditedAdverseEventReportDao;
    private StudyParticipantAssignmentDao studyParticipantAssignmentDao;

    /** Validator Service. **/
    private ExpeditedAdverseEventReportValidator aeReportValidator;

    /** Expedited Report Migrator. **/
    private ExpeditedReportMigrator aeReportMigrator;
    private ExpeditedAdverseEventReportSynchronizer aeReportSynchronizer;

    /** The report Repository. */
    private ReportRepository reportRepository;
    private ReportSubmissionService reportSubmissionService;

    private IDServiceImpl idServiceImpl;

    private AdeersIntegrationFacade adeersIntegrationFacade;

    private EventFactory eventFactory;

    private EvaluateAndInitiateReportConverter evaluateAndInitiateReportConverter;

    public EvaluateAndInitiateReportConverter getEvaluateAndInitiateReportConverter() {
        return evaluateAndInitiateReportConverter;
    }

    public void setEvaluateAndInitiateReportConverter(EvaluateAndInitiateReportConverter reportConverter) {
        this.evaluateAndInitiateReportConverter = reportConverter;
    }

    public EventFactory getEventFactory() {
        return eventFactory;
    }

    public void setEventFactory(EventFactory eventFactory) {
        this.eventFactory = eventFactory;
    }

    public AdeersIntegrationFacade getAdeersIntegrationFacade() {
        return adeersIntegrationFacade;
    }

    public void setAdeersIntegrationFacade(AdeersIntegrationFacade adeersIntegrationFacade) {
        this.adeersIntegrationFacade = adeersIntegrationFacade;
    }

    public ReportRepository getReportRepository() {
        return reportRepository;
    }

    public void setReportRepository(ReportRepository reportRepository) {
        this.reportRepository = reportRepository;
    }

    public void setReportSubmissionService(ReportSubmissionService reportSubmissionService) {
        this.reportSubmissionService = reportSubmissionService;
    }

    public IDServiceImpl getIdServiceImpl() {
        return idServiceImpl;
    }

    public void setIdServiceImpl(IDServiceImpl idServiceImpl) {
        this.idServiceImpl = idServiceImpl;
    }

    /**
     * Does the validation of the input message
     * @param aeSrcReport
     * @return
     */
    @SuppressWarnings("unused")
    private ValidationErrors validateInput(ExpeditedAdverseEventReport aeSrcReport) {

        AdverseEventReportingPeriod rpSrc = aeSrcReport.getReportingPeriod();
        ValidationErrors errors = new ValidationErrors();

        //do I have reporting period ?
        if (rpSrc == null) {
            errors.addValidationError("ER-RP-1", "Missing Reporting period and Adverse event in input message");
            return errors;
        }

        //do I have AEs ?
        if (rpSrc.getAdverseEvents() == null || rpSrc.getAdverseEvents().isEmpty()) {
            errors.addValidationError("WS_AEMS_025", "Missing Adverse Events in the input message");
            return errors;
        }

        //do I have study site details ?
        StudySite studySiteSrc = rpSrc.getStudySite();
        if (studySiteSrc == null) {
            errors.addValidationError("WS_AEMS_034", "StudySite information is missing in input message");
            return errors;
        }

        if (studySiteSrc.getOrganization() == null
                || studySiteSrc.getOrganization().getNciInstituteCode() == null) {
            errors.addValidationError("ER-STU-3", "Missing Study Site details - Organization NCI code");
            return errors;
        }

        //do I have study details ?
        Study studySrc = rpSrc.getStudy();
        if (studySrc == null || studySrc.getFundingSponsorIdentifierValue() == null) {
            logger.error("Missing study identifier");
            errors.addValidationError("WS_AEMS_034", "Missing Study Identifier");
            return errors;
        }
        if (studySrc.getFundingSponsorIdentifierValue() == null) {
            logger.error("Missing study identifier");
            errors.addValidationError("WS_AEMS_034", "Missing Study Identifier");
            return errors;
        }

        //do I have subject details ?
        Participant subjectSrc = rpSrc.getParticipant();
        if (subjectSrc == null) {
            errors.addValidationError("ER-SUB-1", "Subject information is missing in input message");
            return errors;
        }

        return errors;
    }

    private CaaersServiceResponse populateErrors(CaaersServiceResponse response, ValidationErrors errors) {
        logger.error("Adverse Event Management Service create or update call failed :" + String.valueOf(errors));
        for (ValidationError ve : errors.getErrors()) {
            String message = messageSource.getMessage(ve.getCode(), ve.getReplacementVariables(), ve.getMessage(),
                    Locale.getDefault());
            Helper.populateError(response, ve.getCode(), message);
        }
        return response;
    }

    private void migrate(ExpeditedAdverseEventReport aeSrcReport, ExpeditedAdverseEventReport aeDestReport,
            ValidationErrors errors) {
        try {
            adeersIntegrationFacade.updateStudy(aeSrcReport.getStudy().getId(), false);
        } catch (Exception e) {
            logger.warn("Study synchronization failed.", e);
        }
        DomainObjectImportOutcome<ExpeditedAdverseEventReport> outCome = new DomainObjectImportOutcome<ExpeditedAdverseEventReport>();
        aeReportMigrator.migrate(aeSrcReport, aeDestReport, outCome);
        if (outCome.hasErrors())
            errors.addValidationErrors(outCome.getValidationErrors().getErrors());
    }

    private void transferStudySubjectIfRequired(ExpeditedAdverseEventReport aeSrcReport,
            ExpeditedAdverseEventReport aeDestReport, ValidationErrors errors) {
        try {
            StudySite originalSite = aeDestReport.getAssignment().getStudySite();
            Participant dbParticipant = aeDestReport.getAssignment().getParticipant();
            Organization organizationTransferredTo = aeSrcReport.getAssignment().getStudySite().getOrganization();
            if (!aeDestReport.getAssignment().getStudySite().getOrganization().getNciInstituteCode()
                    .equals(aeSrcReport.getAssignment().getStudySite().getOrganization().getNciInstituteCode())) {
                participantService.transferParticipant(dbParticipant, originalSite, organizationTransferredTo,
                        errors);
            }
        } catch (Exception e) {
            logger.error("Error while transferring the StudySubject", e);
        }
    }

    /**
     * Will create a Report and associate it to the ExpeditedAdverseEventReport
     * @param report
     * @param aeReport
     * @return
     */
    public Report createReport(Report report, ExpeditedAdverseEventReport aeReport) {
        Date gradedDate = AdverseEventReportingPeriod.findEarliestGradedDate(aeReport.getUnReportedAdverseEvents());
        report.getReportDefinition().setBaseDate(gradedDate);
        Report newReport = reportRepository.createReport(report.getReportDefinition(), aeReport);
        newReport.copy(report);
        reportRepository.save(newReport);
        if (logger.isInfoEnabled()) {
            logger.info("Created report : " + newReport.getName() + "(" + String.valueOf(newReport.getId()) + ")");
        }
        return newReport;
    }

    /**
     * Will update a Report associated it to the ExpeditedAdverseEventReport and in parallel withdraw any Notifications
     * that are submitted previously for the Report being withdrawn.
     * @param report
     * @param aeReport
     * @return
     */
    public Report withdrawReport(Report report, ExpeditedAdverseEventReport aeReport) {
        reportRepository.withdrawReport(report);
        reportRepository.withdrawExternalReport(aeReport, report);
        if (logger.isInfoEnabled()) {
            logger.info("Withdrew report : " + report.getName() + "(" + String.valueOf(report.getId()) + ")");
        }
        return report;
    }

    /**
     * Will amend the Report
     * @param report
     * @param aeReport
     * @return
     */
    public Report amendReport(Report report, ExpeditedAdverseEventReport aeReport) {
        reportRepository.amendReport(report);
        if (logger.isInfoEnabled()) {
            logger.info("Amended report : " + report.getName() + "(" + String.valueOf(report.getId()) + ")");
        }
        return report;
    }

    /**
     * Will unamend an older version when a new revision of the report is withdrawn.
     * @param report
     * @param aeReport
     * @return
     */
    public Report unAmendReport(Report report, ExpeditedAdverseEventReport aeReport) {
        reportRepository.unAmendReport(report);
        if (logger.isInfoEnabled()) {
            logger.info("Unamended report : " + report.getName() + "(" + String.valueOf(report.getId()) + ")");
        }
        return report;
    }

    private ExpeditedAdverseEventReport getOrSetReportId(ExpeditedAdverseEventReport aeSrcReport) {
        String externalId = aeSrcReport.getExternalId();
        ExpeditedAdverseEventReport dbReport = null;
        if (StringUtils.isEmpty(externalId)) {
            String newReportId = generateSafetyReportId();
            aeSrcReport.setExternalId(newReportId);
            List<Report> reports = aeSrcReport.getReports();
            for (Report report : reports) {
                report.setCaseNumber(newReportId);
            }
        } else {
            dbReport = externalId != null ? expeditedAdverseEventReportDao.getByExternalId(externalId) : null;
        }

        return dbReport;
    }

    private String generateSafetyReportId() {
        return idServiceImpl.generateSafetyReportId(new ReportIdCriteria()).getSafetyReportId();
    }

    /**
     * Will initiate an safety reporting action, and return the data-collection
     * @param aeSrcReport
     * @param caaersServiceResponseWrapper
     * @param errors
     * @return
     */
    public ExpeditedAdverseEventReport initiateSafetyReportAction(ExpeditedAdverseEventReport aeSrcReport,
            CaaersServiceResponseWrapper caaersServiceResponseWrapper, ValidationErrors errors) {

        //migrate data-collection
        ExpeditedAdverseEventReport aeDestReport = new ExpeditedAdverseEventReport();
        migrate(aeSrcReport, aeDestReport, errors);
        if (errors.hasErrors())
            return aeDestReport;

        //transfer subject if needed
        transferStudySubjectIfRequired(aeSrcReport, aeDestReport, errors);
        if (errors.hasErrors())
            return aeDestReport;

        boolean fireExpeditedReportCreationEvent = false;

        //Determine the flow, create vs update
        ExpeditedAdverseEventReport dbReport = getOrSetReportId(aeSrcReport);

        List<Report> reportsAffected = new ArrayList<Report>();
        if (dbReport != null) {
            //update flow - synchronize the data-collections
            DomainObjectImportOutcome<ExpeditedAdverseEventReport> outCome = new DomainObjectImportOutcome<ExpeditedAdverseEventReport>();
            outCome.setContext("initiate");
            aeReportSynchronizer.migrate(aeDestReport, dbReport, outCome);

            if (outCome.hasErrors())
                errors.addValidationErrors(outCome.getValidationErrors().getErrors());
            if (errors.hasErrors())
                return aeDestReport;

            dbReport.updateAESignatures();
            expeditedAdverseEventReportDao.save(dbReport);
            for (Report r : dbReport.getActiveReports()) {
                reportRepository.save(r);
            }

        } else {

            //create flow - so lets only create the data collection
            aeDestReport.updateAESignatures();
            List<Report> reportsToCreate = aeDestReport.getReports();
            aeDestReport.setReports(new ArrayList<Report>()); //clear off the reports so that later we can create it.
            expeditedAdverseEventReportDao.save(aeDestReport);
            dbReport = aeDestReport;

            //now create a new dummy expedited report to hold the Reports.
            aeDestReport = new ExpeditedAdverseEventReport();
            aeDestReport.setReports(reportsToCreate);

            fireExpeditedReportCreationEvent = true;
        }
        caaersServiceResponseWrapper.addAdditionalInfo("reportsAffected", reportsAffected);

        inferReportingAction(aeSrcReport, dbReport, aeDestReport, reportsAffected, caaersServiceResponseWrapper);

        //only fire event in create flow
        if (getEventFactory() != null && fireExpeditedReportCreationEvent)
            getEventFactory().publishEntityModifiedEvent(aeDestReport);
        return aeDestReport;
    }

    /**
     * Will update an ExpeditedAdverseEventReport, and return the list of Reports that got updated.
     * @param aeSrcReport
     * @param dbReport
     * @param errors
     * @return
     */
    public List<Report> updateSafetyReport(ExpeditedAdverseEventReport aeSrcReport,
            ExpeditedAdverseEventReport dbReport, ValidationErrors errors) {
        List<Report> reportsAffected = new ArrayList<Report>();
        ExpeditedAdverseEventReport aeDestReport = new ExpeditedAdverseEventReport();
        migrate(aeSrcReport, aeDestReport, errors);
        if (errors.hasErrors())
            return reportsAffected;

        DomainObjectImportOutcome<ExpeditedAdverseEventReport> outCome = new DomainObjectImportOutcome<ExpeditedAdverseEventReport>();
        aeReportSynchronizer.migrate(aeDestReport, dbReport, outCome);
        if (outCome.hasErrors())
            errors.addValidationErrors(outCome.getValidationErrors().getErrors());
        if (errors.hasErrors())
            return reportsAffected;

        expeditedAdverseEventReportDao.save(dbReport);
        for (Report r : dbReport.getActiveReports()) {
            reportRepository.save(r);
        }

        transferStudySubjectIfRequired(aeSrcReport, aeDestReport, errors);
        if (errors.hasErrors())
            return reportsAffected;

        dbReport.getAssignment().synchronizeMedicalHistoryFromReportToAssignment(dbReport);
        studyParticipantAssignmentDao.save(dbReport.getAssignment());

        inferReportingAction(aeSrcReport, dbReport, aeDestReport, reportsAffected, null);

        return reportsAffected;
    }

    private void inferReportingAction(ExpeditedAdverseEventReport aeSrcReport, ExpeditedAdverseEventReport dbReport,
            ExpeditedAdverseEventReport aeDestReport, List<Report> reportsAffected,
            CaaersServiceResponseWrapper caaersServiceResponseWrapper) {

        //Withdraw active reports
        //find active reports that are eligible for withdraw
        List<Report> withdrawableReports = dbReport.getActiveReports();
        List<Report> reportsToBeWithdrawn = new ArrayList<Report>();
        for (Report report : aeDestReport.getReports()) {
            if (report.getWithdrawnOn() != null) {
                // add the withdrawn report to withdraw list
                reportsToBeWithdrawn.add(report);
                for (Report withdrawableReport : withdrawableReports) {
                    if (withdrawableReport.isSameReportByCaseNumberOrReportDefinition(report)) {
                        withdrawReport(withdrawableReport, dbReport);
                        if (caaersServiceResponseWrapper != null) {
                            buildReportInformationOutput(withdrawableReport, caaersServiceResponseWrapper,
                                    ActionType.WITHDRAW);
                        }
                    }
                }
            }
        }

        // remove reports that are withdrawn
        for (Report withdrawnreport : reportsToBeWithdrawn) {
            aeDestReport.getReports().remove(withdrawnreport);
        }

        // Find a relationship between parent and child exists. check if the parent report is already submitted.
        Report parentCompletedReport = null;

        for (Report srcReport : dbReport.getReports()) {
            if (srcReport.getStatus().equals(ReportStatus.COMPLETED) && srcReport.getReportDefinition().getName()
                    .equals(aeSrcReport.getReports().get(0).getReportDefinition().getName())) {
                // Check if the child record exists.
                parentCompletedReport = srcReport;
            }
        }

        //if parent report is completed, change the updateReport definition to match the child report, ie, followup report
        if (parentCompletedReport != null) {
            for (Report srcReport : dbReport.getReports()) {
                if (!(srcReport.getStatus().equals(ReportStatus.INPROCESS)
                        || srcReport.getStatus().equals(ReportStatus.PENDING)))
                    continue; // If the Report is completed then skip it.
                ReportDefinition parentReportDef = srcReport.getReportDefinition().getParent();
                if (parentReportDef != null && parentReportDef.getName().equals(parentCompletedReport.getName())) {

                    // Override the Report Definition of the Source to Child since child Report is active.

                    if (aeDestReport.getReports().size() != 0) {
                        aeDestReport.getReports().get(0).setReportDefinition(srcReport.getReportDefinition());
                    }

                }
            }
        }

        //create, amend or withdraw reports
        for (Report srcReport : aeDestReport.getReports()) {
            List<Report> reportsToAmend = dbReport.findReportsToAmmend(srcReport.getReportDefinition());
            for (Report report : reportsToAmend) {
                amendReport(report, dbReport);
                //reportsAffected.add(createReport(srcReport, dbReport));
                if (caaersServiceResponseWrapper != null) {
                    buildReportInformationOutput(report, caaersServiceResponseWrapper, ActionType.AMEND);
                }
            }
            List<Report> reportsToWithdraw = dbReport.findReportsToWithdraw(srcReport.getReportDefinition());
            for (Report report : reportsToWithdraw) {
                withdrawReport(report, dbReport);
                if (caaersServiceResponseWrapper != null) {
                    buildReportInformationOutput(report, caaersServiceResponseWrapper, ActionType.WITHDRAW);
                }
            }
            List<Report> reportsToEdit = dbReport.findReportsToEdit(srcReport.getReportDefinition());
            if (reportsToEdit.isEmpty()) {
                Report createdReport = createReport(srcReport, dbReport);
                reportsAffected.add(createdReport);
                if (caaersServiceResponseWrapper != null) {
                    buildReportInformationOutput(createdReport, caaersServiceResponseWrapper, ActionType.CREATE);
                }
            } else {
                for (Report report : reportsToEdit) {
                    reportsAffected.add(report);
                    // Copy the Submitter Information from the Input Source.
                    //TODO : need to check if we should call the report.copy() to get all the info
                    report.setSubmitter(srcReport.getSubmitter());
                    report.setCaseNumber(srcReport.getCaseNumber());
                    if (logger.isInfoEnabled()) {
                        logger.info(
                                "Edited report : " + report.getName() + "( id :" + String.valueOf(report.getId())
                                        + ", caseNumber:" + srcReport.getCaseNumber() + ")");
                    }
                    if (caaersServiceResponseWrapper != null) {
                        buildReportInformationOutput(report, caaersServiceResponseWrapper, ActionType.EDIT);
                    }
                }
            }

            //TODO : BJ implement unammend feature
        }
    }

    /**
     * Will create an ExpeditedAdverseEventReport, then will return all the Reports that got created.
     * @param aeSrcReport
     * @param aeDestReport
     * @param errors
     * @return
     */
    public List<Report> createSafetyReport(ExpeditedAdverseEventReport aeSrcReport,
            ExpeditedAdverseEventReport aeDestReport, ValidationErrors errors) {
        List<Report> reportsAffected = new ArrayList<Report>();

        //Call the Migration
        migrate(aeSrcReport, aeDestReport, errors);
        if (errors.hasErrors())
            return reportsAffected;

        for (AdverseEvent ae : aeDestReport.getAdverseEvents()) {
            ae.setReport(aeDestReport);
        }
        // Set the signature for the AE.
        aeDestReport.updateAESignatures();

        //Call the ExpediteReportDao and save this report.
        expeditedAdverseEventReportDao.save(aeDestReport);

        // transfer the study subject if required.
        transferStudySubjectIfRequired(aeSrcReport, aeDestReport, errors);
        if (errors.hasErrors())
            return reportsAffected;

        aeDestReport.getAssignment().synchronizeMedicalHistoryFromReportToAssignment(aeDestReport);
        studyParticipantAssignmentDao.save(aeDestReport.getAssignment());

        // Deep copy the reports as it is throwing ConcurrentModification Exception.
        List<Report> reports = new ArrayList<Report>(aeDestReport.getReports());
        aeDestReport.getReports().clear();
        // Save the report(s) after Migration.
        for (Report rpt : reports) {
            reportsAffected.add(createReport(rpt, aeDestReport));
        }

        if (getEventFactory() != null)
            getEventFactory().publishEntityModifiedEvent(aeDestReport);
        return reportsAffected;
    }

    @Transactional(readOnly = false)
    public void initiateSafetyReportAction(EvaluateAndInitiateInputMessage evaluateInputMessage,
            SaveAndEvaluateAEsOutputMessage response, EvaluateAndInitiateOutputMessage retVal,
            AdverseEventReportingPeriod repPeriod) {

        RecommendedActions withdrawAction = null;
        RecommendedActions createAction = null;
        RecommendedActions amendAction = null;

        boolean explicitWithdraw = false;
        boolean replace = false;
        boolean withdraw = false;
        boolean create = false;
        boolean amend = false;

        ExpeditedAdverseEventReport aeSrcReport = evaluateAndInitiateReportConverter.convert(evaluateInputMessage,
                repPeriod, response);
        ValidationErrors errors = new ValidationErrors();
        List<RecommendedActions> recActions = response.getRecommendedActions();

        //explicit withdraw request ?
        if (BooleanUtils.isTrue(evaluateInputMessage.isWithdrawReport())) {

            explicitWithdraw = true;
            if (!recActions.isEmpty()) {
                final RecommendedActions action = recActions.get(0);
                action.setAction("Withdraw");
                action.setActionText("Withdraw the " + action.getReport());
                action.setDue("Never");
                action.setDueDate(null);
                response.getRecommendedActions().clear();
                response.getRecommendedActions().add(action);
            }

        } else {
            //not an explicit withdraw, so perform all the recommendations
            amendAction = findRecommendedActions(recActions, ActionType.AMEND);
            createAction = findRecommendedActions(recActions, ActionType.CREATE);
            withdrawAction = findRecommendedActions(recActions, ActionType.WITHDRAW);

            withdraw = withdrawAction != null;
            amend = amendAction != null;
            create = createAction != null;
            replace = create && withdraw;

            if (replace) {
                aeSrcReport.removeReport(withdrawAction.getReport());
            } else if (amend && create) {
                aeSrcReport.removeReport(amendAction.getReport());
            }
        }

        //CAAERS-7414 - always respond with a report-id
        String caseNumber = aeSrcReport.getExternalId();
        if (!explicitWithdraw && StringUtils.isEmpty(caseNumber)) {
            caseNumber = generateSafetyReportId();
            aeSrcReport.setExternalId(caseNumber);
            for (Report report : aeSrcReport.getReports())
                report.setCaseNumber(caseNumber);
        }
        retVal.setReportId(caseNumber);

        //perform the recomended action
        CaaersServiceResponseWrapper caaersServiceResponseWrapper = Helper.createResponseWrapper();
        if (!response.getRecommendedActions().isEmpty()) {

            initiateSafetyReportAction(aeSrcReport, caaersServiceResponseWrapper, errors);
            //are there errors ?
            if (errors.getErrorCount() > 0) {
                throw new CaaersValidationException(errors.toString());
            }
        }

        //update the amendment number if the report is amendable.
        for (RecommendedActions action : recActions) {
            if (StringUtils.equalsIgnoreCase(action.getAction(), "Create")) {
                action.setStatus("In process");
            }
            String due = (String) caaersServiceResponseWrapper
                    .getAdditionalInfo(action.getReport() + "_displayDue");
            String dueDate = (String) caaersServiceResponseWrapper.getAdditionalInfo(action.getReport() + "_due");
            if (StringUtils.isNotEmpty(due))
                action.setDue(due);
            if (StringUtils.isNotEmpty(dueDate))
                action.setDueDate(dueDate);
            Boolean amendable = (Boolean) caaersServiceResponseWrapper
                    .getAdditionalInfo(action.getReport() + "_amendable");
            String strAmendmentNumber = (String) caaersServiceResponseWrapper
                    .getAdditionalInfo(action.getReport() + "_amendmentNumber");
            BigInteger amendmentNumber = NumberUtils.isNumber(strAmendmentNumber)
                    ? new BigInteger(strAmendmentNumber)
                    : null;
            if (BooleanUtils.isTrue(amendable)) {
                action.setAmendmentNumber(amendmentNumber);
            }
        }

    }

    /**
     * Will initiate the safety reporting action
     * @param baseAadverseEventReport
     * @return                                                                                                 */
    @Transactional(readOnly = false)
    public CaaersServiceResponse initiateSafetyReportAction(BaseAdverseEventReport baseAadverseEventReport)
            throws Exception {
        CaaersServiceResponseWrapper caaersServiceResponseWrapper = Helper.createResponseWrapper();
        CaaersServiceResponse caaersServiceResponse = caaersServiceResponseWrapper.getCaaersServiceResponse();

        ValidationErrors errors = new ValidationErrors();
        ExpeditedAdverseEventReport aeSrcReport = null;
        try {
            // 1. Call the Converter(s) to construct the domain object.
            aeSrcReport = baseEaeConverter.convert(baseAadverseEventReport);

        } catch (Exception e) {
            logger.error("Unable to convert the XML report to domain object", e);
            Helper.populateError(caaersServiceResponse, "WS_GEN_000",
                    "Error while converting XML to domain object:" + e.getMessage());
            throw e;
        }

        try {

            // initialize the service response
            ResponseDataType rdType = new ResponseDataType();
            caaersServiceResponse.getServiceResponse().setResponseData(rdType);
            rdType.setAny(new BaseReports());

            initiateSafetyReportAction(aeSrcReport, caaersServiceResponseWrapper, errors);

            if (errors.hasErrors()) {
                expeditedAdverseEventReportDao.clearSession();
                populateErrors(caaersServiceResponse, errors);
            } else {
                caaersServiceResponse.getServiceResponse()
                        .setMessage("Initiated safety report action for the safety report, "
                                + baseAadverseEventReport.getExternalId());
            }

        } catch (Exception e) {
            logger.error("Unable to initiate a safety report action from Safety Management Service", e);
            Helper.populateError(caaersServiceResponse, "WS_GEN_000", e.getMessage());
            throw e;
        }
        return caaersServiceResponse;
    }

    /**
     * Will create/update the ExpeditedAdverseEventReport and then will submit the Reports modified to external agency.
     * @param adverseEventReport
     * @return
     */
    @Transactional(readOnly = false)
    public CaaersServiceResponse submitSafetyReport(AdverseEventReport adverseEventReport) throws Exception {
        CaaersServiceResponse response = Helper.createResponse();
        try {
            List<Report> reportsAffected = new ArrayList<Report>();
            ValidationErrors errors = createOrUpdateSafetyReport(adverseEventReport, reportsAffected);
            if (errors.hasErrors()) {
                populateErrors(response, errors);
                return response;
            }

            //submit report
            List<Report> failedReports = new ArrayList<Report>();
            for (Report report : reportsAffected) {
                reportSubmissionService.submitReport(report);
                if (ReportStatus.FAILED.equals(report.getStatus())) {
                    failedReports.add(report);
                }
            }

            if (!failedReports.isEmpty()) {
                StringBuilder str = new StringBuilder(1024);
                str.append("Could not send ").append(failedReports.size()).append(" out of ")
                        .append(reportsAffected.size()).append(" reports, for the following reasons;\n");
                for (Report r : failedReports) {
                    str.append("Report: '").append(r.getName()).append("' (").append(r.getId()).append("); Error: ")
                            .append(r.getSubmissionMessage()).append("\n\n");
                }
                logger.error(str.toString());
                response = Helper.populateError(response, "WS_GEN_007", str.toString().trim());
            }

        } catch (Exception e) {
            logger.error("Unable to Create/Update a Report from Safety Management Service", e);
            Helper.populateError(response, "WS_GEN_000", e.getMessage());
            throw e;
        }
        return response;
    }

    // CAAERS-7414 validate if reportId is consistent with reporting period attributes
    public void validateReportIdAndAdverseEventReportingPeriodAttributes(ExpeditedAdverseEventReport aeReport,
            ValidationErrors errors) throws CaaersFault {

        if (StringUtils.isBlank(aeReport.getExternalId())) {
            return;
        }

        // find reporting period attributes from input
        Date startDateSrc = aeReport.getReportingPeriod().getStartDate();
        Integer cycleNumberSrc = aeReport.getReportingPeriod().getCycleNumber();
        String tAC = null;
        if (aeReport.getReportingPeriod().getTreatmentAssignment() != null) {
            tAC = aeReport.getReportingPeriod().getTreatmentAssignment().getCode();
        }

        ExpeditedAdverseEventReport dbReport = expeditedAdverseEventReportDao
                .getByExternalId(aeReport.getExternalId());
        if (dbReport != null) {
            if (!dbReport.getReportingPeriod().hasSameCoreAttributes(cycleNumberSrc, startDateSrc, tAC)) {
                errors.addValidationError("WS_GEN_SRS1",
                        "Reporting Period with given attributes is not found in the report with id "
                                + dbReport.getExternalId() + ".");
            }
        }
    }

    /**
     * Will save the ExpeditedAdverseEventReport
     * @param adverseEventReport
     * @return
     */
    @Transactional(readOnly = false)
    public CaaersServiceResponse saveSafetyReport(AdverseEventReport adverseEventReport) {
        CaaersServiceResponse response = Helper.createResponse();
        try {
            ValidationErrors errors = createOrUpdateSafetyReport(adverseEventReport, new ArrayList<Report>());
            if (errors.hasErrors())
                populateErrors(response, errors);
        } catch (Exception e) {
            logger.error("Unable to Create/Update a Report from Safety Management Service", e);
            Helper.populateError(response, "WS_GEN_000", e.getMessage());
        }
        return response;
    }

    private void buildReportInformationOutput(Report report,
            CaaersServiceResponseWrapper caaersServiceResponseWrapper, ActionType actionType) {
        CaaersServiceResponse caaersServiceResponse = caaersServiceResponseWrapper.getCaaersServiceResponse();
        String reportName = report.getName();
        caaersServiceResponseWrapper.addAdditionalInfo(reportName, report);

        BaseReportType baseReport = new BaseReportType();
        baseReport.setReportID(report.getAeReport().getExternalId());

        baseReport.setAction(actionType.getDisplayName());
        if (report.getLastVersion().getAmendmentNumber() != null) {
            baseReport.setAmendmentNumber(report.getLastVersion().getAmendmentNumber().toString());
            caaersServiceResponseWrapper.addAdditionalInfo(reportName + "_amendmentNumber",
                    baseReport.getAmendmentNumber());
        }
        caaersServiceResponseWrapper.addAdditionalInfo(reportName + "_amendable",
                report.getReportDefinition().getAmendable());
        baseReport.setReportName(report.getReportDefinition().getName());
        baseReport.setCaseNumber(report.getCaseNumber());
        caaersServiceResponseWrapper.addAdditionalInfo(reportName + "_caseNumber", report.getCaseNumber());
        if ((report.getStatus() == ReportStatus.AMENDED || report.getStatus() == ReportStatus.PENDING
                || report.getStatus() == ReportStatus.FAILED || report.getStatus() == ReportStatus.INPROCESS)
                && report.getDueOn() != null) {
            baseReport.setDueDate(DateUtils.formatToWSResponseDateWithTimeZone(report.getDueOn()));
            caaersServiceResponseWrapper.addAdditionalInfo(reportName + "_due", baseReport.getDueDate());
            caaersServiceResponseWrapper.addAdditionalInfo(reportName + "_displayDue",
                    report.getReportDefinition().getDueInGivenDueDate(report.getDueOn()));
        }

        // set action text https://tracker.nci.nih.gov/browse/CAAERS-6962
        baseReport.setActionText(actionType.name().substring(0, 1).toUpperCase()
                + actionType.name().substring(1, actionType.name().length()).toLowerCase() + " the "
                + report.getReportDefinition().getName());
        caaersServiceResponseWrapper.addAdditionalInfo(reportName + "_actionText", baseReport.getActionText());

        ServiceResponse serviceResponse = caaersServiceResponse.getServiceResponse();
        if (serviceResponse == null) {
            serviceResponse = new ServiceResponse();
            caaersServiceResponse.setServiceResponse(serviceResponse);
        }
        ResponseDataType respData = serviceResponse.getResponseData();
        if (respData == null) {
            respData = new ResponseDataType();
            serviceResponse.setResponseData(respData);
        }
        Object obj = respData.getAny();
        BaseReports reportList;
        if (obj == null || !(obj instanceof BaseReports)) {
            reportList = new BaseReports();
            respData.setAny(reportList);
        } else {
            reportList = (BaseReports) obj;
        }
        reportList.getBaseReport().add(baseReport);
    }

    /**
     * Will create or update an ExpeditedAdverseEventReport, and updates the reportsAffected with the Reports that
     * got amended/edited/created.
     * @param adverseEventReport
     * @param reportsAffected
     * @return
     * @throws Exception
     */
    public ValidationErrors createOrUpdateSafetyReport(AdverseEventReport adverseEventReport,
            List<Report> reportsAffected) throws Exception {
        ValidationErrors errors = new ValidationErrors();
        ExpeditedAdverseEventReport aeSrcReport = null;

        try {

            // 1. Call the Converter(s) to construct the domain object.
            aeSrcReport = eaeConverter.convert(adverseEventReport);

        } catch (Exception e) {
            logger.error("Error while converting AdverseEvent XML to domain object", e);
            errors.addValidationError("WS_GEN_008",
                    "Error while converting XML to domain object:" + e.getMessage());
            return errors;
        }

        try {

            //2. Do some basic validations (if needed)
            validateReportIdAndAdverseEventReportingPeriodAttributes(aeSrcReport, errors);
            //3. Determine the flow, create vs update
            String externalId = aeSrcReport.getExternalId();
            ExpeditedAdverseEventReport dbAeReport = externalId != null
                    ? expeditedAdverseEventReportDao.getByExternalId(externalId)
                    : null;

            if (dbAeReport == null) {
                //create flow
                reportsAffected.addAll(createSafetyReport(aeSrcReport, new ExpeditedAdverseEventReport(), errors));
            } else {
                //update flow
                reportsAffected.addAll(updateSafetyReport(aeSrcReport, dbAeReport, errors));
            }

            if (errors.hasErrors()) {
                expeditedAdverseEventReportDao.clearSession();
                return errors;
            }

        } catch (Exception e) {
            expeditedAdverseEventReportDao.clearSession();
            logger.error("Unable to Create/Update a Report from Safety Management Service", e);
            errors.addValidationError("WS_GEN_000",
                    "Error while creating or updating safety report:" + e.getMessage());
        }
        return errors;
    }

    private RecommendedActions findRecommendedActions(List<RecommendedActions> recomendActionsList,
            ActionType actionType) {
        if (recomendActionsList == null)
            return null;
        for (RecommendedActions actions : recomendActionsList) {
            if (StringUtils.equalsIgnoreCase(actionType.name(), actions.getAction()))
                return actions;
        }
        return null;
    }

    public BaseExpeditedAdverseEventReportConverter getBaseEaeConverter() {
        return baseEaeConverter;
    }

    public void setBaseEaeConverter(BaseExpeditedAdverseEventReportConverter baseEaeConverter) {
        this.baseEaeConverter = baseEaeConverter;
    }

    public ExpeditedAdverseEventReportConverter getEaeConverter() {
        return eaeConverter;
    }

    public void setEaeConverter(ExpeditedAdverseEventReportConverter eaeConverter) {
        this.eaeConverter = eaeConverter;
    }

    public ParticipantServiceImpl getParticipantService() {
        return participantService;
    }

    public void setParticipantService(ParticipantServiceImpl participantService) {
        this.participantService = participantService;
    }

    public ParticipantDao getParticipantDao() {
        return participantDao;
    }

    public void setParticipantDao(ParticipantDao participantDao) {
        this.participantDao = participantDao;
    }

    public StudyDao getStudyDao() {
        return studyDao;
    }

    public void setStudyDao(StudyDao studyDao) {
        this.studyDao = studyDao;
    }

    public MessageSource getMessageSource() {
        return messageSource;
    }

    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public ExpeditedAdverseEventReportDao getExpeditedAdverseEventReportDao() {
        return expeditedAdverseEventReportDao;
    }

    public void setExpeditedAdverseEventReportDao(ExpeditedAdverseEventReportDao expeditedAdverseEventReportDao) {
        this.expeditedAdverseEventReportDao = expeditedAdverseEventReportDao;
    }

    public ExpeditedReportMigrator getAeReportMigrator() {
        return aeReportMigrator;
    }

    public void setAeReportMigrator(ExpeditedReportMigrator aeReportMigrator) {
        this.aeReportMigrator = aeReportMigrator;
    }

    public ExpeditedAdverseEventReportValidator getAeReportValidator() {
        return aeReportValidator;
    }

    public void setAeReportValidator(ExpeditedAdverseEventReportValidator aeReportValidator) {
        this.aeReportValidator = aeReportValidator;
    }

    public ExpeditedAdverseEventReportSynchronizer getAeReportSynchronizer() {
        return aeReportSynchronizer;
    }

    public void setAeReportSynchronizer(ExpeditedAdverseEventReportSynchronizer aeReportSynchronizer) {
        this.aeReportSynchronizer = aeReportSynchronizer;
    }

    public StudyParticipantAssignmentDao getStudyParticipantAssignmentDao() {
        return studyParticipantAssignmentDao;
    }

    public void setStudyParticipantAssignmentDao(StudyParticipantAssignmentDao studyParticipantAssignmentDao) {
        this.studyParticipantAssignmentDao = studyParticipantAssignmentDao;
    }

}