org.openelis.scriptlet.ms.integrate.Scriptlet.java Source code

Java tutorial

Introduction

Here is the source code for org.openelis.scriptlet.ms.integrate.Scriptlet.java

Source

/**
 * Exhibit A - UIRF Open-source Based Public Software License.
 * 
 * The contents of this file are subject to the UIRF Open-source Based Public
 * Software License(the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * openelis.uhl.uiowa.edu
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 * 
 * The Original Code is OpenELIS code.
 * 
 * The Initial Developer of the Original Code is The University of Iowa.
 * Portions created by The University of Iowa are Copyright 2006-2008. All
 * Rights Reserved.
 * 
 * Contributor(s): ______________________________________.
 * 
 * Alternatively, the contents of this file marked "Separately-Licensed" may be
 * used under the terms of a UIRF Software license ("UIRF Software License"), in
 * which case the provisions of a UIRF Software License are applicable instead
 * of those above.
 */
package org.openelis.scriptlet.ms.integrate;

import static org.openelis.scriptlet.ms.Constants.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.logging.Level;

import org.openelis.constants.Messages;
import org.openelis.domain.AnalysisQaEventViewDO;
import org.openelis.domain.AnalysisViewDO;
import org.openelis.domain.AnalyteParameterViewDO;
import org.openelis.domain.Constants;
import org.openelis.domain.DictionaryDO;
import org.openelis.domain.QaEventDO;
import org.openelis.domain.ResultViewDO;
import org.openelis.manager.AnalyteParameterManager1;
import org.openelis.manager.SampleManager1;
import org.openelis.manager.TestManager;
import org.openelis.meta.SampleMeta;
import org.openelis.scriptlet.SampleSO;
import org.openelis.scriptlet.SampleSO.Action_Before;
import org.openelis.scriptlet.ms.Constants.Interpretation;
import org.openelis.scriptlet.ms.ScriptletProxy;
import org.openelis.scriptlet.ms.Util;
import org.openelis.ui.common.DataBaseUtil;
import org.openelis.ui.common.Datetime;
import org.openelis.ui.common.FormErrorCaution;
import org.openelis.ui.common.FormErrorException;
import org.openelis.ui.common.NotFoundException;
import org.openelis.ui.common.data.QueryData;
import org.openelis.ui.scriptlet.ScriptletInt;
import org.openelis.ui.scriptlet.ScriptletObject.Status;
import org.openelis.utilcommon.ResultFormatter;
import org.openelis.utilcommon.ResultHelper;

import com.google.gwt.i18n.shared.DateTimeFormat;
import com.google.gwt.i18n.shared.DefaultDateTimeFormatInfo;

/**
 * The scriptlet for "ms integrate (Maternal Integrated Screen)" test. It uses a
 * compute engine to calculate the MoMs for various tests such as AFP and risks
 * and interpretations for various disorders such as Downs. The computations are
 * done based on patient information and results entered by the analyst. If the
 * results are overridden, the MoMs, risks and interpretations are blanked. The
 * computations are done as soon as the user changes any of the values or on
 * completing/releasing the analysis.
 */
public class Scriptlet implements ScriptletInt<SampleSO> {

    private ScriptletProxy proxy;

    private Integer analysisId;

    private Compute compute;

    private ArrayList<String> changes;

    private HashMap<String, HashMap<String, ResultViewDO>> resultMap;

    private AnalyteParameterManager1 paramManager;

    private SampleManagerComparator comparator;

    private static DateTimeFormat dateFormatter, dateTimeFormatter;

    private static boolean initialized;

    private static Integer YES, NO, UNIT_MM, UNIT_POUNDS, UNIT_YEARS, INT_POSITIVE, INT_NEGATIVE, INT_UNKNOWN,
            ACTION_US_AMN, ACTION_US_DIAG, ACTION_NO_ACTION, ACTION_RISK_NOT_CALC;

    private static String YES_STR, RACE_BLACK_STR;

    private static final String INTEG_TEST_NAME = "ms 1st integ", METHOD_NAME = "eia";

    private enum Risk {
        NTD, DOWNS, T18
    }

    public Scriptlet(ScriptletProxy proxy, Integer analysisId) {
        this.proxy = proxy;
        this.analysisId = analysisId;

        proxy.log(Level.FINE, "Initializing MSIntegrateScriptlet1", null);
        try {
            if (!initialized) {
                initialized = true;
                YES = proxy.getDictionaryBySystemName("yes").getId();
                NO = proxy.getDictionaryBySystemName("no").getId();
                UNIT_MM = proxy.getDictionaryBySystemName("millimeters").getId();
                UNIT_POUNDS = proxy.getDictionaryBySystemName("pounds_local").getId();
                UNIT_YEARS = proxy.getDictionaryBySystemName("years_local").getId();
                INT_POSITIVE = proxy.getDictionaryBySystemName("positive").getId();
                INT_NEGATIVE = proxy.getDictionaryBySystemName("negative").getId();
                INT_UNKNOWN = proxy.getDictionaryBySystemName("unknown").getId();
                ACTION_US_AMN = proxy.getDictionaryBySystemName("ms_rec_action_us_amn").getId();
                ACTION_US_DIAG = proxy.getDictionaryBySystemName("ms_rec_action_us_diag").getId();
                ACTION_NO_ACTION = proxy.getDictionaryBySystemName("ms_rec_action_no_action").getId();
                ACTION_RISK_NOT_CALC = proxy.getDictionaryBySystemName("ms_rec_action_risk_not_calc").getId();
                YES_STR = YES.toString();
                RACE_BLACK_STR = proxy.getDictionaryBySystemName("race_b").getId().toString();

                /*
                 * the formatters that convert from string to date or vice-versa
                 */
                dateFormatter = new DateTimeFormat(Messages.get().datePattern(), new DefaultDateTimeFormatInfo()) {
                };
                dateTimeFormatter = new DateTimeFormat(Messages.get().dateTimePattern(),
                        new DefaultDateTimeFormatInfo()) {
                };
            }
            proxy.log(Level.FINE, "Initialized MSIntegrateScriptlet1", null);
        } catch (Exception e) {
            initialized = false;
            proxy.log(Level.SEVERE, "Failed to initialize dictionary constants", e);
        }
    }

    @Override
    public SampleSO run(SampleSO data) {
        Integer accession;
        SampleManager1 sm;
        AnalysisViewDO ana;
        ResultViewDO res;

        proxy.log(Level.FINE, "In MSIntegrateScriptlet1.run", null);

        changes = data.getChanges();
        sm = data.getManager();
        ana = (AnalysisViewDO) sm.getObject(Constants.uid().getAnalysis(analysisId));
        if (ana == null || Constants.dictionary().ANALYSIS_RELEASED.equals(ana.getStatusId())
                || Constants.dictionary().ANALYSIS_CANCELLED.equals(ana.getStatusId()) || changes.size() == 0)
            return data;

        /*
         * manage result changed, the risks needing to be recomputed, the
         * analysis getting completed or released, a qa event added/removed or
         * collection date, patient's birth date or patient's race changed or
         * provider changed
         */
        if (data.getActionBefore().contains(Action_Before.RESULT)) {
            res = (ResultViewDO) sm.getObject(data.getUid());
            if (!analysisId.equals(res.getAnalysisId()))
                return data;
        } else if (data.getActionBefore().contains(Action_Before.COMPLETE)
                || data.getActionBefore().contains(Action_Before.RELEASE)
                || changes.contains(SampleMeta.getAnalysisStartedDate())) {
            ana = (AnalysisViewDO) sm.getObject(data.getUid());
            if (!analysisId.equals(ana.getId()))
                return data;
        } else if (!data.getActionBefore().contains(Action_Before.QA)
                && !data.getActionBefore().contains(Action_Before.PATIENT)
                && !data.getActionBefore().contains(Action_Before.RECOMPUTE)
                && !data.getActionBefore().contains(Action_Before.ANALYSIS)
                && !changes.contains(SampleMeta.getCollectionDate())
                && !changes.contains(SampleMeta.getReceivedDate())
                && !changes.contains(SampleMeta.getClinicalPatientBirthDate())
                && !changes.contains(SampleMeta.getClinicalPatientRaceId())
                && !changes.contains(SampleMeta.getClinicalProviderLastName())) {
            return data;
        }

        /*
         * patient must be present for doing computations; it may not be present
         * if, for example, this test is added to a PT sample
         */
        if (sm.getSampleClinical() == null || sm.getSampleClinical().getPatientId() == null) {
            accession = sm.getSample().getAccessionNumber();
            /*
             * for display
             */
            if (accession == null)
                accession = 0;
            data.setStatus(Status.FAILED);
            data.addException(new FormErrorException(Messages.get().analysis_patientRequiredForComputeException(
                    accession, ana.getTestName(), ana.getMethodName())));
            return data;
        }

        setRisks(data, ana);

        return data;
    }

    /**
     * Initializes the map containing the ResultViewDOs for various analytes;
     * calculates/copies result values based on sample and patient fields such
     * as birth date or race; sets values in the compute engine from results;
     * sets the values computed by the engine in results and formats them
     */
    private void setRisks(SampleSO data, AnalysisViewDO ana) {
        Integer accession;
        Exception lastEx;

        proxy.log(Level.FINE, "In MSIntegrateScriptlet1.setRisks", null);
        try {
            initializeResultMap(data, ana);
            setBirthDate(data, ana);
            setMaternalWeight(data, ana);
            setRaceBlack(data, ana);
            setFirstTrimesterValues(data, ana);
            setComputeValues(data, ana);
            compute.compute();
            setResultValues(data, ana);
            formatValues(data, ana);
            setUnits(data, ana);
            addRemoveQaEvents(data, ana);
            /*
             * if the compute engine encountered an error, make it available to
             * be shown to the user; add the accession number to the error
             * message because the engine doesn't know that information
             */
            lastEx = compute.getLastException();
            if (lastEx != null) {
                accession = data.getManager().getSample().getAccessionNumber();
                /*
                 * for display
                 */
                if (accession == null)
                    accession = 0;
                data.setStatus(Status.FAILED);
                data.addException(new FormErrorException(
                        DataBaseUtil.concatWithSeparator(Messages.get().sample_accessionAnalysisPrefix(accession,
                                ana.getTestName(), ana.getMethodName()), " ", lastEx.getMessage())));
            }
        } catch (Exception e) {
            data.setStatus(Status.FAILED);
            data.addException(e);
        }
    }

    /**
     * Initializes a hash map used to quickly lookup ResultViewDOs for getting
     * or setting their value
     */
    private void initializeResultMap(SampleSO data, AnalysisViewDO ana) {
        proxy.log(Level.FINE, "Creating hashmap of results", null);
        resultMap = createResultMap(data.getManager(), ana);
    }

    /**
     * Sets the birth date value from the egg donor's age or from patient's
     * birth date
     */
    private void setBirthDate(SampleSO data, AnalysisViewDO ana) throws Exception {
        ResultViewDO res, resAge, resBirth;
        ResultFormatter rf;
        TestManager tm;
        SampleManager1 sm;
        Datetime bd;

        proxy.log(Level.FINE, "Setting birth date from either the egg's age or from patient's birth date", null);
        sm = data.getManager();
        resBirth = getResult("birth");
        tm = (TestManager) data.getCache().get(Constants.uid().getTest(ana.getTestId()));
        rf = tm.getFormatter();
        /*
         * find out if egg's age was changed or egg donor was changed from "Yes"
         * to "No" or vice-versa
         */
        if (data.getActionBefore().contains(Action_Before.RESULT)) {
            res = (ResultViewDO) sm.getObject(data.getUid());
            if ("eggs_age".equals(res.getAnalyteExternalId()) || "egg_donor".equals(res.getAnalyteExternalId())) {
                /*
                 * if it's an egg donor, compute and set birth date from egg's
                 * age; otherwise wipe egg's age; set birth date value as either
                 * the computed value or the patient's birth date
                 */
                resAge = getResult("eggs_age");
                if (YES_STR.equals(getValue("egg_donor"))) {
                    bd = Util.getBirthDate(getDoubleValue(resAge, sm, ana, false), proxy, data, ana);
                } else {
                    bd = null;
                    setValue(resAge, null, rf, false, data, ana);
                }
                setValue(resBirth, bd, rf, false, data, ana);
            }
        }

        if (resBirth.getValue() == null && sm.getSampleClinical().getPatient().getBirthDate() != null) {
            bd = sm.getSampleClinical().getPatient().getBirthDate();
            setValue(resBirth, bd, rf, false, data, ana);
        }
        /*
         * if the result value for birth date doesn't match the patient's birth
         * date, add a caution to inform the user
         */
        if (DataBaseUtil.isDifferentYD(getDateValue(resBirth), sm.getSampleClinical().getPatient().getBirthDate()))
            addResultNotMatchCaution(resBirth.getAnalyte(), data, ana);
    }

    /**
     * Validates the maternal weight entered and converts it to lbs from kgs if
     * it contains "k"
     */
    private void setMaternalWeight(SampleSO data, AnalysisViewDO ana) throws Exception {
        double multiplier;
        String value;
        Double weight;
        ResultViewDO res;
        TestManager tm;

        /*
         * do something only if a result was changed, that result was maternal
         * weight and its value is not null
         */
        if (data.getActionBefore().contains(Action_Before.RESULT)) {
            res = (ResultViewDO) data.getManager().getObject(data.getUid());
            value = res.getValue();
            if ("mat_weight_2nd_tri".equals(res.getAnalyteExternalId()) && value != null) {
                proxy.log(Level.FINE, "Formatting and resetting maternal weight", null);
                /*
                 * convert from kgs to lbs if weight contains "k" or "kg"
                 */
                tm = (TestManager) data.getCache().get(Constants.uid().getTest(ana.getTestId()));
                try {
                    multiplier = 1.0;
                    if (value.endsWith("k")) {
                        value = value.substring(0, value.length() - 1);
                        multiplier = 2.204;
                    } else if (value.endsWith("kg")) {
                        value = value.substring(0, value.length() - 2);
                        multiplier = 2.204;
                    }
                    weight = new Double(value) * multiplier;
                    setValue(res, weight, tm.getFormatter(), false, data, ana);
                } catch (NumberFormatException e) {
                    throw getValueInvalidException(res, data.getManager(), ana);
                }
            }
        }
    }

    /**
     * Sets the value of "Maternal Race Black" to "Yes" if the patient's race is
     * black even if it's mixed race; otherwise sets the value to "No"
     */
    private void setRaceBlack(SampleSO data, AnalysisViewDO ana) throws Exception {
        boolean raceblack;
        Integer value, raceId;
        DictionaryDO dict;
        ResultViewDO res;
        TestManager tm;

        /*
         * race is black if the system name contains a "b" e.g. "race_b" or
         * "race_wb", otherwise not; change 'maternal race black' if either the
         * patient's race was changed or if the analyte's value is not set
         */
        raceId = data.getManager().getSampleClinical().getPatient().getRaceId();
        raceblack = false;
        res = getResult("mat_race_black");
        if (raceId != null) {
            dict = proxy.getDictionaryById(raceId);
            raceblack = dict.getSystemName() != null && dict.getSystemName().contains("b");
        }
        if (changes.contains(SampleMeta.getClinicalPatientRaceId()) && res.getValue() == null) {
            proxy.log(Level.FINE, "Setting 'maternal race black' from the sample", null);
            value = raceblack ? YES : NO;
            tm = (TestManager) data.getCache().get(Constants.uid().getTest(ana.getTestId()));
            setValue(res, value, tm.getFormatter(), true, data, ana);
        }
        /*
         * if the result value for 'maternal race black' isn't consistent with
         * the patient, add a caution to inform the user
         */
        value = getIntegerValue(res, data.getManager(), ana);
        if ((!raceblack && YES.equals(value)) || (raceblack && !YES.equals(value)))
            addResultNotMatchCaution(res.getAnalyte(), data, ana);
    }

    /**
     * Finds the most recent sample which has the same patient as the passed
     * sample and a released 1st trimester integrated analysis; uses the 1st
     * trimester accession number, if present, or the passed sample's patient;
     * copies values such as NT result and MoM from that sample to the passed
     * analysis' results
     */
    private void setFirstTrimesterValues(SampleSO data, AnalysisViewDO ana) throws Exception {
        int i, j;
        Integer accession, ftAccession;
        AnalysisViewDO tmpAna, ftAna;
        ResultViewDO res;
        SampleManager1 sm, ftSm;
        ArrayList<SampleManager1> ftSms;

        /*
         * fetch the 1st trimester sample(s) using the 1st trimester accession
         * number and/or the patient if either has been entered; do this only if
         * the 1st trimester accession number, patient or the received date was
         * changed or if the passed analysis was just added, is getting
         * completed or released or the risks need to be recomputed
         */
        sm = data.getManager();
        ftSms = null;
        try {
            if (data.getActionBefore().contains(Action_Before.RESULT)) {
                res = (ResultViewDO) sm.getObject(data.getUid());
                if ("accession_1st_tri".equals(res.getAnalyteExternalId())) {
                    ftAccession = getIntegerValue(res, sm, ana);
                    if (ftAccession != null
                            && ftAccession.equals(data.getManager().getSample().getAccessionNumber())) {
                        data.setStatus(Status.FAILED);
                        data.addException(
                                new FormErrorException(Messages.get().analysis_firstSecTriNotOnSameSampleException(
                                        ftAccession, ana.getTestName(), ana.getMethodName())));
                    }
                    ftSms = fetchFirstTriSamples(ftAccession, sm);
                } else {
                    return;
                }
            } else if (data.getActionBefore().contains(Action_Before.PATIENT)
                    || data.getActionBefore().contains(Action_Before.ANALYSIS)
                    || data.getActionBefore().contains(Action_Before.COMPLETE)
                    || data.getActionBefore().contains(Action_Before.RELEASE)
                    || data.getActionBefore().contains(Action_Before.RECOMPUTE)
                    || changes.contains(SampleMeta.getReceivedDate())) {
                ftAccession = getIntegerValue(getResult("accession_1st_tri"), sm, ana);
                ftSms = fetchFirstTriSamples(ftAccession, sm);
            } else {
                return;
            }
        } catch (NotFoundException e) {
            /*
             * a 1st trimester sample was not found; if this exception is not
             * handled here, an error with the message "null" is shown, which is
             * not very helpful; a more descriptive error for missing 1st
             * trimester values will be added later
             */
        }

        /*
         * find the released and not overridden 1st trimester integrated
         * analysis on the most recent of the fetched samples; the samples are
         * in ascending order of received date, so to find the most recent,
         * start from the end of the list; add an error if such an analysis was
         * not found; set the 1st trimester values in the passed analysis from
         * the 1st trimester sample and analysis
         */
        ftSm = null;
        ftAna = null;
        if (ftSms != null) {
            for (i = ftSms.size() - 1; i >= 0; i--) {
                ftSm = ftSms.get(i);
                if (!ftSm.qaEvent.hasType(Constants.dictionary().QAEVENT_OVERRIDE)) {
                    for (j = 0; j < ftSm.analysis.count(); j++) {
                        tmpAna = ftSm.analysis.get(j);
                        if (INTEG_TEST_NAME.equals(tmpAna.getTestName())
                                && METHOD_NAME.equals(tmpAna.getMethodName())
                                && Constants.dictionary().ANALYSIS_RELEASED.equals(tmpAna.getStatusId())
                                && !ftSm.qaEvent.hasType(tmpAna, Constants.dictionary().QAEVENT_OVERRIDE)) {
                            ftAna = tmpAna;
                            break;
                        }
                    }
                    if (ftAna != null)
                        break;
                }
            }
        }
        if (ftAna == null) {
            /*
             * for display
             */
            accession = sm.getSample().getAccessionNumber();
            if (accession == null)
                accession = 0;
            data.setStatus(Status.FAILED);
            data.addException(new FormErrorException(Messages.get().analysis_relatedFirstTriNotFoundException(
                    accession, ana.getTestName(), ana.getMethodName(), INTEG_TEST_NAME, METHOD_NAME)));
        }
        setFirstTrimesterValues(ftSm, ftAna, data, ana);
    }

    /**
     * Set values in the compute engine from the passed sample, analysis,
     * results of specific analytes and analyte parameters
     */
    private void setComputeValues(SampleSO data, AnalysisViewDO ana) throws Exception {
        SampleManager1 sm;
        HashMap<Integer, AnalyteParameterViewDO> paramMap;

        proxy.log(Level.FINE, "Setting values in the compute engine", null);
        /*
         * set sample/analysis level info
         */
        sm = data.getManager();
        compute = new Compute();
        compute.setIsOverridden(sm.qaEvent.hasType(Constants.dictionary().QAEVENT_OVERRIDE)
                || sm.qaEvent.hasType(ana, Constants.dictionary().QAEVENT_OVERRIDE));
        compute.setEnteredDate(sm.getSample().getEnteredDate());
        compute.setCollectionDate(sm.getSample().getCollectionDate());
        /*
         * set patient info
         */
        compute.setBirthDate(getDateValue(getValue("birth")));
        compute.setWeight(getDoubleValue(getResult("mat_weight_2nd_tri"), sm, ana, false));
        compute.setIsRaceBlack(RACE_BLACK_STR.equals(getValue("mat_race_black")));
        compute.setIsDiabetic(YES_STR.equals(getValue("insulin_dep_diabetic")));
        compute.setHasHistoryOfNTD(YES_STR.equals(getValue("history_ntd")));
        compute.setNumFetus(getIntegerValue(getResult("number_of_fetuses"), sm, ana));
        /*
         * set 1st trimester values
         */
        compute.setFirstTriGestAge(getFirstTriGestAge(sm, ana));
        compute.setFirstTriUltrasoundDate(getDateValue(getValue("ultrasound")));
        compute.setFirstTriCRL(getIntegerValue(getResult("crl"), sm, ana));
        compute.getNT().setResult(getDoubleValue(getResult("nt_measurement"), sm, ana, false));
        compute.getNT().setMom(getDoubleValue(getResult("nt_mom"), sm, ana, false));
        compute.getPAPPA().setMom(getDoubleValue(getResult("papp_a_mom"), sm, ana, false));

        if (paramManager == null) {
            try {
                paramManager = proxy.fetchParameters(ana.getTestId(), Constants.table().TEST);
            } catch (NotFoundException e) {
                /*
                 * the analyte parameters have not been defined; if this
                 * exception is not handled here, an error with the message
                 * "null" is shown, which is not very helpful; a more
                 * descriptive error for missing p-values will be added later if
                 * some computation can't be done without them
                 */
            }
        }
        paramMap = Util.getParameters(paramManager, data, ana);
        /*
         * set p-values and result from the instrument for tests e.g. NT
         */
        setTestValues(compute.getAFP(), "afp_mom", paramMap, sm, ana);
        setTestValues(compute.getEST(), "estriol_mom", paramMap, sm, ana);
        setTestValues(compute.getHCG(), "hcg_mom", paramMap, sm, ana);
        setTestValues(compute.getInhibin(), "inhibin_mom", paramMap, sm, ana);
    }

    /**
     * Sets values such as MoMs, risks, cutoffs, interpretations etc. in the
     * results from the compute engine
     */
    private void setResultValues(SampleSO data, AnalysisViewDO ana) throws Exception {
        Object value;
        TestManager tm;
        ResultFormatter rf;

        proxy.log(Level.FINE, "Setting values in the results from the compute engine", null);
        tm = (TestManager) data.getCache().get(Constants.uid().getTest(ana.getTestId()));
        rf = tm.getFormatter();
        setValue(getResult("mat_age_at_delivery"), compute.getMothersDueAge(), rf, false, data, ana);
        /*
         * MoMs
         */
        setValue(getResult("afp_mom"), compute.getAFP().getMom(), rf, false, data, ana);
        setValue(getResult("estriol_mom"), compute.getEST().getMom(), rf, false, data, ana);
        setValue(getResult("hcg_mom"), compute.getHCG().getMom(), rf, false, data, ana);
        setValue(getResult("inhibin_mom"), compute.getInhibin().getMom(), rf, false, data, ana);
        /*
         * risks, screening cutoffs(limits), interpretations, recommended
         * actions; ntd
         */
        value = null;
        if (compute.getLimitNTD() != null)
            value = DataBaseUtil.concatWithSeparator("NTD >=", " ", compute.getLimitNTD());
        setValue(getResult("afp_mom", "screen_cutoff"), value, rf, false, data, ana);
        setInterpretation("afp_mom", compute.getInterpretationNTD(), rf, data, ana);
        setRecommendedAction("afp_mom", compute.getInterpretationNTD(), Risk.NTD, rf, data, ana);
        /*
         * downs
         */
        setRisk("downs_risk", compute.getRiskSignDowns(), compute.getRiskDowns(), rf, data, ana);
        setScreeningCutoff("downs_risk", compute.getLimitDowns(), rf, data, ana);
        setInterpretation("downs_risk", compute.getInterpretationDowns(), rf, data, ana);
        setRecommendedAction("downs_risk", compute.getInterpretationDowns(), Risk.DOWNS, rf, data, ana);
        /*
         * t18
         */
        setRisk("trisomy_18_risk", compute.getRiskSignTrisomy18(), compute.getRiskTrisomy18(), rf, data, ana);
        setScreeningCutoff("trisomy_18_risk", compute.getLimitTrisomy18(), rf, data, ana);
        setInterpretation("trisomy_18_risk", compute.getInterpretationTrisomy18(), rf, data, ana);
        setRecommendedAction("trisomy_18_risk", compute.getInterpretationTrisomy18(), Risk.T18, rf, data, ana);
        /*
         * downs by age
         */
        setValue(getResult("downs_age_risk"), compute.getRiskDownsByAge(), rf, false, data, ana);
        /*
         * gestational age in days and weeks and days
         */
        setValue(getResult("gest_age_2nd_tri"), Util.getWeeksAndDays(compute.getGestAge()), rf, false, data, ana);
    }

    /**
     * Formats "double" values such as egg's age, MoMs and intrument results
     * etc. to have one or two fractional digits
     */
    private void formatValues(SampleSO data, AnalysisViewDO ana) throws Exception {
        TestManager tm;

        proxy.log(Level.FINE, "Formatting result values", null);
        tm = (TestManager) data.getCache().get(Constants.uid().getTest(ana.getTestId()));
        /*
         * egg's age and maternal weight have one fractional digit
         */
        formatValue(getResult("eggs_age"), 1, tm.getFormatter(), false, data, ana);
        formatValue(getResult("mat_weight_1st_tri"), 1, tm.getFormatter(), false, data, ana);
        formatValue(getResult("mat_weight_2nd_tri"), 1, tm.getFormatter(), false, data, ana);
        /*
         * test MoMs and instrument results have two fractional digit
         */
        formatValue(getResult("afp_mom"), 2, tm.getFormatter(), false, data, ana);
        formatValue(getResult("afp_mom", "result"), 2, tm.getFormatter(), true, data, ana);
        formatValue(getResult("estriol_mom"), 2, tm.getFormatter(), false, data, ana);
        formatValue(getResult("estriol_mom", "result"), 2, tm.getFormatter(), true, data, ana);
        formatValue(getResult("hcg_mom"), 2, tm.getFormatter(), false, data, ana);
        formatValue(getResult("hcg_mom", "result"), 2, tm.getFormatter(), true, data, ana);
        formatValue(getResult("inhibin_mom"), 2, tm.getFormatter(), false, data, ana);
        formatValue(getResult("inhibin_mom", "result"), 2, tm.getFormatter(), true, data, ana);
        formatValue(getResult("nt_mom"), 2, tm.getFormatter(), false, data, ana);
        formatValue(getResult("nt_mom"), 2, tm.getFormatter(), true, data, ana);
        formatValue(getResult("papp_a_mom"), 2, tm.getFormatter(), false, data, ana);
    }

    /**
     * Sets the values in the "Unit" column for various row analytes such as
     * "Maternal Weight", "CRL" etc; sets the unit as null if the row analyte's
     * value is null
     */
    private void setUnits(SampleSO data, AnalysisViewDO ana) throws Exception {
        ResultFormatter rf;
        TestManager tm;

        tm = (TestManager) data.getCache().get(Constants.uid().getTest(ana.getTestId()));
        rf = tm.getFormatter();
        setUnit("eggs_age", UNIT_YEARS, rf, data, ana);
        setUnit("mat_age_at_delivery", UNIT_YEARS, rf, data, ana);
        setUnit("mat_weight_2nd_tri", UNIT_POUNDS, rf, data, ana);
        setUnit("mat_weight_1st_tri", UNIT_POUNDS, rf, data, ana);
        setUnit("crl", UNIT_MM, rf, data, ana);
        setUnit("nt_measurement", UNIT_MM, rf, data, ana);
    }

    /**
     * Adds a QA event to the analysis if the MoM for EST is below a certain
     * limit or if age at delivery < 15 years or if there are multiple fetuses;
     * removes a QA event if the condition for which it was added is no longer
     * true
     */
    private void addRemoveQaEvents(SampleSO data, AnalysisViewDO ana) throws Exception {
        Integer numFetus;
        Double age, mom;

        mom = getDoubleValue(getValue("estriol_mom"), false);
        if (mom != null && mom < EST_LIMIT)
            addQAEvent(QA_EST_MOM, data, ana);
        else
            removeQAEvent(QA_EST_MOM, data, ana);

        age = getDoubleValue(getValue("mat_age_at_delivery"), false);
        if (age != null && age < 15.0)
            addQAEvent(QA_LESS_THAN_15, data, ana);
        else
            removeQAEvent(QA_LESS_THAN_15, data, ana);

        numFetus = getIntegerValue(getValue("number_of_fetuses"));
        if (numFetus != null && numFetus > 1)
            addQAEvent(QA_TWINS, data, ana);
        else
            removeQAEvent(QA_TWINS, data, ana);
    }

    /**
     * Creates a hash map where the key is the external id of a row analyte and
     * the value is another hash map containing the ResultViewDOs for all
     * analytes in the row; the key in the second map is the external id of a
     * row or column analyte and the value is the ResultVIewDO for that analyte
     */
    private HashMap<String, HashMap<String, ResultViewDO>> createResultMap(SampleManager1 sm, AnalysisViewDO ana) {
        int i, j;
        ResultViewDO res;
        HashMap<String, ResultViewDO> map;
        HashMap<String, HashMap<String, ResultViewDO>> resultMap;

        if (sm == null || ana == null)
            return null;

        resultMap = new HashMap<String, HashMap<String, ResultViewDO>>();
        map = null;
        for (i = 0; i < sm.result.count(ana); i++) {
            for (j = 0; j < sm.result.count(ana, i); j++) {
                res = sm.result.get(ana, i, j);
                if ("N".equals(res.getIsColumn())) {
                    map = resultMap.get(res.getAnalyteExternalId());
                    if (map == null) {
                        map = new HashMap<String, ResultViewDO>();
                        resultMap.put(res.getAnalyteExternalId(), map);
                    }
                }
                map.put(res.getAnalyteExternalId(), res);
            }
        }

        return resultMap;
    }

    /**
     * Returns the ResultViewDO for the row analyte with the passed external id
     */
    private ResultViewDO getResult(String rowExtId) {
        return getResult(rowExtId, rowExtId);
    }

    /**
     * Returns from the passed map, the ResultViewDO for the row analyte with
     * the passed external id
     */
    private ResultViewDO getResult(String rowExtId, HashMap<String, HashMap<String, ResultViewDO>> resultMap) {
        return getResult(rowExtId, rowExtId, resultMap);
    }

    /**
     * Returns the ResultViewDO for a column analyte whose row analyte's
     * external id is the first argument and whose external id is the second
     * argument
     */
    private ResultViewDO getResult(String rowExtId, String colExtId) {
        return getResult(rowExtId, colExtId, resultMap);
    }

    /**
     * Returns from the passed map, the ResultViewDO for a column analyte whose
     * row analyte's external id is the first argument and whose external id is
     * the second argument
     */
    private ResultViewDO getResult(String rowExtId, String colExtId,
            HashMap<String, HashMap<String, ResultViewDO>> resultMap) {
        HashMap<String, ResultViewDO> map;

        map = resultMap != null ? resultMap.get(rowExtId) : null;
        return map != null ? map.get(colExtId) : null;
    }

    /**
     * Sets the passed value in the passed result; the flag isDict means that
     * the value is a dictionary id and a dictionary record needs to be
     * obtained, because ResultHelper validates dictionary entries and not ids
     */
    private void setValue(ResultViewDO result, Object value, ResultFormatter rf, boolean isDict, SampleSO data,
            AnalysisViewDO ana) throws Exception {
        String val;

        val = null;
        if (value != null)
            val = isDict ? proxy.getDictionaryById((Integer) value).getEntry() : value.toString();

        if (ResultHelper.formatValue(result, val, ana.getUnitOfMeasureId(), rf)) {
            data.setChanges(result.getAnalyteExternalId());
            data.addChangedUid(Constants.uid().getResult(result.getId()));
            proxy.log(Level.FINE, "Setting the value of " + result.getAnalyte() + " as: " + val, null);
        }
    }

    /**
     * Returns the value for the row analyte whose external id is the passed
     * value
     */
    private String getValue(String rowExtId) {
        ResultViewDO res;

        res = getResult(rowExtId);
        return res != null ? res.getValue() : null;
    }

    /**
     * Adds a caution to inform the user that the value for passed analyte e.g.
     * "Race" doesn't match the patient's value; a caution is used here because
     * it's just a warning to the user; the sample doesn't need to go to error
     * status and update doesn't need to fail
     */
    private void addResultNotMatchCaution(String analyteName, SampleSO data, AnalysisViewDO ana) throws Exception {
        Integer accession;

        /*
         * for display
         */
        accession = data.getManager().getSample().getAccessionNumber();
        if (accession == null)
            accession = 0;
        data.setStatus(Status.FAILED);
        data.addException(new FormErrorCaution(Messages.get().result_resultNotMatchPatientCaution(accession,
                ana.getTestName(), ana.getMethodName(), analyteName)));
    }

    /**
     * Fetches the 1st trimester sample(s) for the passed sample's patient; the
     * fetched samples must have a released 1st trimester integrated analysis
     * and must have been received before the passed sample; returns the most
     * recent of those samples; returns null if the passed sample doesn't have a
     * patient linked yet
     */
    private ArrayList<SampleManager1> fetchFirstTriSamples(Integer ftAccession, SampleManager1 sm)
            throws Exception {
        Datetime startDT, endDT;
        QueryData field;
        ArrayList<QueryData> fields;
        ArrayList<SampleManager1> sms;

        if (sm.getSampleClinical().getPatientId() == null)
            return null;

        fields = new ArrayList<QueryData>();

        if (ftAccession != null) {
            field = new QueryData();
            field.setKey(SampleMeta.getAccessionNumber());
            field.setType(QueryData.Type.INTEGER);
            field.setQuery(ftAccession.toString());
            fields.add(field);
        }

        /*
         * make sure that the samples to be fetched were received at the most 90
         * days before the current(passed) sample
         */
        endDT = sm.getSample().getReceivedDate() != null ? sm.getSample().getReceivedDate()
                : sm.getSample().getEnteredDate();
        startDT = endDT.add(-90);

        field = new QueryData();
        field.setKey(SampleMeta.getReceivedDate());
        field.setType(QueryData.Type.DATE);
        field.setQuery(format(dateTimeFormatter, startDT) + ".." + format(dateTimeFormatter, endDT));
        fields.add(field);

        sms = Util.fetchRelatedSamples(INTEG_TEST_NAME, METHOD_NAME, sm, fields, proxy, SampleManager1.Load.QA,
                SampleManager1.Load.RESULT);
        if (sms != null) {
            /*
             * sort the fetched managers by received date because in the
             * back-end they were sorted by sample id
             */
            if (comparator == null)
                comparator = new SampleManagerComparator();
            Collections.sort(sms, comparator);
        }
        return sms;
    }

    /**
     * Returns the id for the row analyte whose external id is the passed value
     */
    private Integer getAnalyteId(String rowExtId) {
        ResultViewDO res;

        res = getResult(rowExtId);
        return res != null ? res.getAnalyteId() : null;
    }

    /**
     * Returns the passed result's value converted to an Integer; returns null
     * if the value is null; throws an exception if the value contains any
     * non-numeric characters such as "<" or ">"
     */
    private Integer getIntegerValue(ResultViewDO res, SampleManager1 sm, AnalysisViewDO ana) throws Exception {
        if (res != null) {
            try {
                return getIntegerValue(res.getValue());
            } catch (NumberFormatException e) {
                throw getValueInvalidException(res, sm, ana);
            }
        }
        return null;
    }

    /**
     * Returns the passed value converted to an Integer; returns null if the
     * value is null; throws an exception if the value contains any non-numeric
     * characters such as "<" or ">"
     */
    private Integer getIntegerValue(String value) throws Exception {
        return value != null ? new Integer(value) : null;
    }

    /**
     * Returns the passed results's value converted to a Datetime; returns null
     * if the result or value is null
     */
    private Datetime getDateValue(ResultViewDO res) throws Exception {
        return res != null ? getDateValue(res.getValue()) : null;
    }

    /**
     * Returns the passed value converted to a Datetime; returns null if the
     * value is null
     */
    private Datetime getDateValue(String value) throws Exception {
        if (DataBaseUtil.isEmpty(value))
            return null;
        return Datetime.getInstance(Datetime.YEAR, Datetime.DAY, dateFormatter.parseStrict(value));
    }

    /**
     * Returns the passed Datetime's date formatted using the passed formatter
     */
    private String format(DateTimeFormat formatter, Datetime dt) throws Exception {
        return dt != null ? formatter.format(dt.getDate()) : null;
    }

    /**
         * Returns the passed result's value converted to a Double; returns null if
         * the value is null; if the boolean flag is true, treats a value with a "<"
         * as valid; otherwise throws an exception if the value contains any non-numeric
         * characters such as "<" or ">"
         */
    private Double getDoubleValue(ResultViewDO res, SampleManager1 sm, AnalysisViewDO ana, boolean allowLessThan)
            throws Exception {
        if (res != null) {
            try {
                return getDoubleValue(res.getValue(), allowLessThan);
            } catch (NumberFormatException e) {
                throw getValueInvalidException(res, sm, ana);
            }
        }
        return null;
    }

    /**
         * Returns the passed value converted to a Double; returns null if
         * the value is null; if the boolean flag is true, treats a value with a "<"
         * as valid; otherwise throws an exception if the value contains any non-numeric
         * characters such as "<" or ">"
         */
    private Double getDoubleValue(String value, boolean allowLessThan) throws Exception {
        if (value != null) {
            value = allowLessThan ? value.replaceAll("<", "") : value;
            return new Double(value);
        }
        return null;
    }

    /**
     * Returns an exception whose message states that the passed result's value
     * is invalid
     */
    private Exception getValueInvalidException(ResultViewDO res, SampleManager1 sm, AnalysisViewDO ana) {
        Integer accession;

        accession = sm.getSample().getAccessionNumber();
        /*
         * for display
         */
        if (accession == null)
            accession = 0;
        return new FormErrorException(Messages.get().result_valueInvalidException(accession, ana.getTestName(),
                ana.getMethodName(), res.getAnalyte(), res.getValue()));
    }

    /**
     * Sets the 1st trimester values from the passed sample and analysis; blanks
     * the values if the analysis is null, because a 1st trimester integrated
     * analysis was not found on the passed sample
     */
    private void setFirstTrimesterValues(SampleManager1 ftSm, AnalysisViewDO ftAna, SampleSO data,
            AnalysisViewDO ana) throws Exception {
        Integer accession;
        String gestAge;
        Datetime cd, rd;
        SampleManager1 sm;
        ResultViewDO resUltra, resCrl, resNTMom, resNTResult, resSono, resPappaMom, resMatWeight, resGestAge;
        TestManager tm;
        ResultFormatter rf;
        HashMap<String, HashMap<String, ResultViewDO>> ftResultMap;

        /*
         * get the data from the 1st trimester sample
         */
        ftResultMap = createResultMap(ftSm, ftAna);
        accession = ftSm != null ? ftSm.getSample().getAccessionNumber() : null;
        cd = ftSm != null ? ftSm.getSample().getCollectionDate() : null;
        rd = ftSm != null ? ftSm.getSample().getReceivedDate() : null;
        resUltra = getResult("ultrasound", ftResultMap);
        resCrl = getResult("crl", ftResultMap);
        resNTMom = getResult("nt_mom", ftResultMap);
        resNTResult = getResult("nt_measurement", ftResultMap);
        resSono = getResult("sonographer", ftResultMap);
        resPappaMom = getResult("papp_a_mom", ftResultMap);
        resMatWeight = getResult("mat_weight", ftResultMap);
        resGestAge = getResult("gest_age", ftResultMap);

        /*
         * set the values in the current(passed) analysis; if a 1st trimester
         * sample was not found, wipe all the 1si trimester fields accept the
         * accession number; this will allow spotting and correcting any typos
         */
        tm = (TestManager) data.getCache().get(Constants.uid().getTest(ana.getTestId()));
        rf = tm.getFormatter();
        sm = data.getManager();
        if (accession != null)
            setValue(getResult("accession_1st_tri"), accession, rf, false, data, ana);
        setValue(getResult("collected_1st_tri"), format(dateFormatter, cd), rf, false, data, ana);
        setValue(getResult("received_1st_tri"), format(dateFormatter, rd), rf, false, data, ana);
        setValue(getResult("ultrasound"), getDateValue(resUltra), rf, false, data, ana);
        setValue(getResult("crl"), getDoubleValue(resCrl, sm, ana, false), rf, false, data, ana);
        setValue(getResult("nt_measurement"), getDoubleValue(resNTResult, sm, ana, true), rf, false, data, ana);
        setValue(getResult("sonographer"), getIntegerValue(resSono, sm, ana), rf, true, data, ana);
        setValue(getResult("nt_mom"), getDoubleValue(resNTMom, sm, ana, false), rf, false, data, ana);
        setValue(getResult("papp_a_mom"), getDoubleValue(resPappaMom, sm, ana, false), rf, false, data, ana);
        setValue(getResult("mat_weight_1st_tri"), getDoubleValue(resMatWeight, sm, ana, false), rf, false, data,
                ana);
        gestAge = resGestAge != null ? resGestAge.getValue() : null;
        setValue(getResult("gest_age_1st_tri"), gestAge, rf, false, data, ana);
    }

    /**
     * Returns the total number of days from the first trimester gestational age
     * which is in the format "X days (YwZd)"; here X, Y and Z represent the
     * gestational age in total number of days and weeks and days, e.g. 145, 20
     * and 5 respectively
     */
    private Integer getFirstTriGestAge(SampleManager1 sm, AnalysisViewDO ana) throws Exception {
        ResultViewDO res;
        String value[];

        res = getResult("gest_age_1st_tri");
        if (res.getValue() != null) {
            value = res.getValue().split(" ");
            if (value.length > 0) {
                try {
                    return Integer.valueOf(value[0]);
                } catch (NumberFormatException e) {
                    throw getValueInvalidException(res, sm, ana);
                }
            } else {
                throw getValueInvalidException(res, sm, ana);
            }
        }
        return null;
    }

    /**
     * Sets p-values and result from the intrument for the passed test
     */
    private void setTestValues(Test t, String analyteName, HashMap<Integer, AnalyteParameterViewDO> paramMap,
            SampleManager1 sm, AnalysisViewDO ana) throws Exception {
        AnalyteParameterViewDO ap;

        ap = paramMap != null ? paramMap.get(getAnalyteId(analyteName)) : null;
        t.setP1(getP1(ap));
        t.setP2(getP2(ap));
        t.setP3(getP3(ap));
        t.setResult(getDoubleValue(getResult(analyteName, "result"), sm, ana, true));
    }

    /**
     * Sets the value in the "Unit" column for a row analyte such as
     * "Maternal Weight", "CRL" etc; sets the unit as null if the row analyte's
     * value is null
     */
    private void setUnit(String externalId, Integer unitId, ResultFormatter rf, SampleSO data, AnalysisViewDO ana)
            throws Exception {
        Object value;
        ResultViewDO res;

        res = getResult(externalId);
        value = (res != null && res.getValue() != null) ? unitId : null;
        setValue(getResult(externalId, "unit"), value, rf, true, data, ana);
    }

    /**
     * Returns the passed parameter's p1 value; returns null if either the
     * parameter or the value is null
     */
    private Double getP1(AnalyteParameterViewDO ap) {
        return ap == null || ap.getP1() == null ? null : ap.getP1();
    }

    /**
     * Returns the passed parameter's p2 value; returns null if either the
     * parameter or the value is null
     */
    private Double getP2(AnalyteParameterViewDO ap) {
        return ap == null || ap.getP2() == null ? null : ap.getP2();
    }

    /**
     * Returns the passed parameter's p3 value; returns null if either the
     * parameter or the value is null
     */
    private Double getP3(AnalyteParameterViewDO ap) {
        return ap == null || ap.getP3() == null ? null : ap.getP3();
    }

    /**
     * Sets the value of the analyte with the passed external id as the risk
     * created from the passed risk sign and computed risk
     */
    private void setRisk(String externalId, String riskSign, Integer risk, ResultFormatter rf, SampleSO data,
            AnalysisViewDO ana) throws Exception {
        String value;

        value = risk == null ? null : DataBaseUtil.concatWithSeparator(riskSign, "1:", risk);
        setValue(getResult(externalId), value, rf, false, data, ana);
    }

    /**
     * Sets the value of the analyte with the passed external id as the
     * screening cutoff created from the passed computed limit
     */
    private void setScreeningCutoff(String externalId, Integer limit, ResultFormatter rf, SampleSO data,
            AnalysisViewDO ana) throws Exception {
        String value;

        value = limit == null ? null : DataBaseUtil.concat("1:", limit);
        setValue(getResult(externalId, "screen_cutoff"), value, rf, false, data, ana);
    }

    /**
     * Sets the value of the analyte with the passed external id as the
     * interpretation e.g. "Positive", based on the passed computed
     * interpretation e.g. "Presumptive Positive"
     */
    private void setInterpretation(String externalId, Interpretation interp, ResultFormatter rf, SampleSO data,
            AnalysisViewDO ana) throws Exception {
        Object value;

        value = null;
        if (interp != null) {
            switch (interp) {
            case NORMAL:
                value = INT_NEGATIVE;
                break;
            case PRES_POS:
                value = INT_POSITIVE;
                break;
            case UNKNOWN:
                value = INT_UNKNOWN;
                break;
            }
        }
        setValue(getResult(externalId, "interpretation"), value, rf, true, data, ana);
    }

    /**
     * Sets the value of the analyte with the passed external id as a
     * recommended action e.g. "Offer amniocentesis"; the action depends on the
     * passed interpretation e.g. "Positive" and a risk e.g. "Downs"
     */
    private void setRecommendedAction(String externalId, Interpretation interp, Risk risk, ResultFormatter rf,
            SampleSO data, AnalysisViewDO ana) throws Exception {
        Integer value;

        value = null;
        if (interp != null) {
            switch (interp) {
            case NORMAL:
                value = ACTION_NO_ACTION;
                break;
            case PRES_POS:
                switch (risk) {
                case NTD:
                    value = ACTION_US_AMN;
                    break;
                case DOWNS:
                case T18:
                    value = ACTION_US_DIAG;
                    break;
                }
                break;
            case UNKNOWN:
                value = ACTION_RISK_NOT_CALC;
                break;
            }
        }

        setValue(getResult(externalId, "rec_action"), value, rf, true, data, ana);
    }

    /**
         * Formats the passed result's value to have one or two fractional digits
         * based on the passed precision; the boolean flag specifies whether the 
         * value can have a "<"; if the value has a "<" before formatting, 
         * concatenates "<" to the formatted value 
         */
    private void formatValue(ResultViewDO res, int precision, ResultFormatter rf, boolean allowLessThan,
            SampleSO data, AnalysisViewDO ana) throws Exception {
        boolean hasLessThan;
        Double val;
        String value;

        value = res.getValue();
        if (value == null)
            return;
        hasLessThan = value.startsWith("<");
        val = getDoubleValue(res, data.getManager(), ana, allowLessThan);
        value = Util.format(val, precision, proxy);
        if (hasLessThan)
            value = DataBaseUtil.concat("<", value);
        setValue(res, value, rf, false, data, ana);
    }

    /**
     * Adds a QA event with the passed name to the passed analysis if it's not
     * already added; throws an exception if the QA event was not found in the
     * system
     */
    private void addQAEvent(String name, SampleSO data, AnalysisViewDO ana) throws Exception {
        Integer accession;
        QaEventDO qa;
        AnalysisQaEventViewDO aqa;
        SampleManager1 sm;

        qa = Util.getQAEvent(name, ana.getTestId(), proxy);
        sm = data.getManager();
        accession = sm.getSample().getAccessionNumber();
        if (qa == null) {
            /*
             * for display
             */
            if (accession == null)
                accession = 0;
            throw new FormErrorException(Messages.get().analysis_qaEventNotFoundException(accession,
                    ana.getTestName(), ana.getMethodName(), name));
        }
        if (!sm.qaEvent.hasName(ana, name)) {
            proxy.log(Level.FINE, "Adding the qa event: " + name, null);
            aqa = sm.qaEvent.add(ana, qa);
            data.addChangedUid(Constants.uid().getAnalysisQAEvent(aqa.getId()));
        }
    }

    /**
     * Removes the QA event with the passed name from the passed analysis if it
     * was added earlier
     */
    private void removeQAEvent(String name, SampleSO data, AnalysisViewDO ana) {
        int i;
        AnalysisQaEventViewDO aqa;
        SampleManager1 sm;

        proxy.log(Level.FINE, "Removing the qa event: " + name, null);
        sm = data.getManager();
        for (i = 0; i < sm.qaEvent.count(ana); i++) {
            aqa = sm.qaEvent.get(ana, i);
            if (aqa.getQaEventName().equals(name)) {
                sm.qaEvent.remove(ana, aqa);
                break;
            }
        }
        data.addChangedUid(Constants.uid().getAnalysis(ana.getId()));
        data.addActionAfter(SampleSO.Action_After.QA_REMOVED);
    }

    /**
     * This class is used for sorting the 1st trimester sample managers fetched
     * by this scriptlet; the managers are sorted by received date and are here
     * because the "query" method in SampleBean sorts by the default field which
     * is sample id
     */
    private class SampleManagerComparator implements Comparator<SampleManager1> {
        public int compare(SampleManager1 sm1, SampleManager1 sm2) {
            Datetime rd1, rd2;

            rd1 = sm1.getSample().getReceivedDate();
            rd2 = sm2.getSample().getReceivedDate();
            if (DataBaseUtil.isAfter(rd2, rd1))
                return -1;
            else if (DataBaseUtil.isAfter(rd1, rd2))
                return 1;
            else
                return 0;
        }
    }
}