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

Java tutorial

Introduction

Here is the source code for org.openelis.scriptlet.ms.quad.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.quad;

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

import java.util.ArrayList;
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.Gest_Age_Method;
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.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 quad (Maternal Quad 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 NTD. 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 static DateTimeFormat dateFormatter;

    private static boolean initialized;

    private static Integer YES, NO, UNIT_MM, UNIT_POUNDS, UNIT_YEARS, GEST_AGE_LMP, GEST_AGE_US, INT_POSITIVE,
            INT_NEGATIVE, INT_UNKNOWN, ACTION_US, ACTION_AMN, ACTION_US_AMN, ACTION_NO_ACTION, ACTION_RISK_NOT_CALC;

    private static String YES_STR;

    private enum Risk {
        NTD, DOWNS, T18
    }

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

        proxy.log(Level.FINE, "Initializing MSQuadScriptlet1", 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();
                GEST_AGE_LMP = proxy.getDictionaryBySystemName("gest_age_date_type_lmp").getId();
                GEST_AGE_US = proxy.getDictionaryBySystemName("gest_age_date_type_us").getId();
                INT_POSITIVE = proxy.getDictionaryBySystemName("positive").getId();
                INT_NEGATIVE = proxy.getDictionaryBySystemName("negative").getId();
                INT_UNKNOWN = proxy.getDictionaryBySystemName("unknown").getId();
                ACTION_US = proxy.getDictionaryBySystemName("ms_rec_action_us").getId();
                ACTION_AMN = proxy.getDictionaryBySystemName("ms_rec_action_amn").getId();
                ACTION_US_AMN = proxy.getDictionaryBySystemName("ms_rec_action_us_amn").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();

                /*
                 * the formatter that converts from string to date
                 */
                dateFormatter = new DateTimeFormat(Messages.get().datePattern(), new DefaultDateTimeFormatInfo()) {
                };
            }
            proxy.log(Level.FINE, "Initialized MSQuadScriptlet1", 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 MSQuadScriptlet1.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
         */
        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.getClinicalPatientBirthDate())
                && !changes.contains(SampleMeta.getClinicalPatientRaceId())) {
            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 MSQuadScriptlet1.setRisks", null);
        try {
            initializeResultMap(data, ana);
            setBirthDate(data, ana);
            setMaternalWeight(data, ana);
            setRaceBlack(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);
        }
    }

    /**
     * Initialize 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;
     * these maps are used to quickly lookup ResultViewDOs for getting or
     * setting their value
     */
    private void initializeResultMap(SampleSO data, AnalysisViewDO ana) throws Exception {
        int i, j;
        ResultViewDO res;
        SampleManager1 sm;
        HashMap<String, ResultViewDO> map;

        proxy.log(Level.FINE, "Creating hashmap of results", null);
        sm = data.getManager();
        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);
            }
        }
    }

    /**
     * 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".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);
    }

    /**
     * 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 {
        Integer gestAgeMethodId;
        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());
        compute.setBeenReleased(ana.getRevision() > 0);
        /*
         * set patient info
         */
        compute.setIsRaceBlack(YES_STR.equals(getValue("mat_race_black")));
        compute.setIsDiabetic(YES_STR.equals(getValue("insulin_dep_diabetic")));
        compute.setHasHistoryOfNTD(YES_STR.equals(getValue("history_ntd")));
        compute.setBirthDate(getDateValue(getValue("birth")));
        compute.setLMPDate(getDateValue(getValue("lmp")));
        compute.setUltrasoundDate(getDateValue(getValue("ultrasound")));
        compute.setNumFetus(getIntegerValue(getResult("number_of_fetuses"), sm, ana));
        compute.setWeight(getDoubleValue(getResult("mat_weight"), sm, ana, false));
        compute.setCRL(getIntegerValue(getResult("crl"), sm, ana));
        compute.setBPD(getIntegerValue(getResult("bpd"), sm, ana));
        compute.setWeeksDays(getDoubleValue(getResult("weeks_days"), sm, ana, false));
        /*
         * set the method by which gestational age was initially computed
         */
        gestAgeMethodId = getIntegerValue(getResult("init_gest_age", "determined_by"), sm, ana);
        if (GEST_AGE_LMP.equals(gestAgeMethodId))
            compute.setGestAgeInitMethod(Gest_Age_Method.LMP);
        else if (GEST_AGE_US.equals(gestAgeMethodId))
            compute.setGestAgeInitMethod(Gest_Age_Method.US);
        /*
         * get the analyte parameters for this analysis' test
         */
        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 intrument for tests e.g. AFP
         */
        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 {
        String gaInit, gaCurr;
        Object value;
        TestManager tm;
        ResultFormatter rf;
        Gest_Age_Method gaInitMethod, gaCurrMethod;

        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().getMomFinal(), rf, false, data, ana);
        setValue(getResult("estriol_mom"), compute.getEST().getMomFinal(), rf, false, data, ana);
        setValue(getResult("hcg_mom"), compute.getHCG().getMomFinal(), rf, false, data, ana);
        setValue(getResult("inhibin_mom"), compute.getInhibin().getMomFinal(), rf, false, data, ana);
        /*
         * risks, screening cutoffs(limits), interpretations, recommended
         * actions; ntd
         */
        gaInitMethod = compute.getGestAgeInitMethod();
        gaCurrMethod = compute.getGestAgeCurrMethod();
        value = null;
        if (compute.getLimitNTD() != null)
            value = DataBaseUtil.concatWithSeparator("NTD >=", " ", compute.getLimitNTD());
        setGestAgeMethod("afp_mom", gaCurrMethod, rf, data, ana);
        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);
        setGestAgeMethod("downs_risk", gaCurrMethod, 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);
        setGestAgeMethod("trisomy_18_risk", gaInitMethod, 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 ages, methods
         */
        gaInit = Util.getWeeksAndDays(compute.getGestAgeInit());
        gaCurr = Util.getWeeksAndDays(compute.getGestAgeCurr());
        setValue(getResult("init_gest_age"), gaInit, rf, false, data, ana);
        setGestAgeMethod("init_gest_age", gaInitMethod, rf, data, ana);
        setValue(getResult("gest_age"), gaCurr, rf, false, data, ana);
        setGestAgeMethod("gest_age", gaCurrMethod, rf, 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()));
        /*
         * age at delivery, egg's age, weight and weeks & days have one
         * fractional digit
         */
        formatValue(getResult("mat_age_at_delivery"), 1, tm.getFormatter(), false, data, ana);
        formatValue(getResult("eggs_age"), 1, tm.getFormatter(), false, data, ana);
        formatValue(getResult("mat_weight"), 1, tm.getFormatter(), false, data, ana);
        formatValue(getResult("weeks_days"), 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);
    }

    /**
     * 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", UNIT_POUNDS, rf, data, ana);
        setUnit("crl", UNIT_MM, rf, data, ana);
        setUnit("bpd", 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);
    }

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

    /**
     * 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) {
        HashMap<String, ResultViewDO> map;

        map = resultMap.get(rowExtId);
        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)));
    }

    /**
     * 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 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 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);
    }

    /**
     * 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);
    }

    /**
     * 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 {
        Integer value;

        value = null;
        if (interp != null) {
            switch (interp) {
            case NORMAL:
                value = INT_NEGATIVE;
                break;
            case PRES_POS:
            case PRES_POS_AS:
                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:
                case DOWNS:
                    value = ACTION_US;
                    break;
                case T18:
                    value = ACTION_AMN;
                    break;
                }
                break;
            case PRES_POS_AS:
                switch (risk) {
                case NTD:
                    value = ACTION_US_AMN;
                    break;
                case DOWNS:
                case T18:
                    value = ACTION_AMN;
                    break;
                }
                break;
            case UNKNOWN:
                value = ACTION_RISK_NOT_CALC;
                break;
            }
        }

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

    /**
     * Sets the value of the analyte as the method for determining gestational
     * age e.g. LMP, based on the passed computed method
     */
    private void setGestAgeMethod(String externalId, Gest_Age_Method method, ResultFormatter rf, SampleSO data,
            AnalysisViewDO ana) throws Exception {
        Integer value;

        value = null;
        if (method != null) {
            switch (method) {
            case LMP:
                value = GEST_AGE_LMP;
                break;
            case US:
                value = GEST_AGE_US;
                break;
            }
        }
        setValue(getResult(externalId, "determined_by"), 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);
    }
}