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

Java tutorial

Introduction

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

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

import org.openelis.constants.Messages;
import org.openelis.domain.AnalysisViewDO;
import org.openelis.domain.AnalyteParameterViewDO;
import org.openelis.domain.Constants;
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 conf (Amniotic Fluid AFP)" test. It uses a compute
 * engine to compute the MoMs for AFP and risk and interpretation for NTD. The
 * computations are done based on patient information and results entered by the
 * analyst. If the results are overridden, the MoM, risk and interpretation 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, UNIT_MM, UNIT_YEARS, GEST_AGE_LMP, GEST_AGE_US, INT_POSITIVE, INT_NEGATIVE,
            ACTION_SEE_ACHE, ACTION_NO_ACTION;

    private static String YES_STR;

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

        proxy.log(Level.FINE, "Initializing MSConfScriptlet1", null);
        try {
            if (!initialized) {
                initialized = true;
                YES = proxy.getDictionaryBySystemName("yes").getId();
                UNIT_MM = proxy.getDictionaryBySystemName("millimeters").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();
                ACTION_SEE_ACHE = proxy.getDictionaryBySystemName("ms_rec_action_see_ache_res").getId();
                ACTION_NO_ACTION = proxy.getDictionaryBySystemName("ms_rec_action_no_action").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 MSConfScriptlet1", 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 MSConfScriptlet1.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 MSConfScriptlet1.setRisks", null);
        try {
            initializeResultMap(data, ana);
            setBirthDate(data, ana);
            setComputeValues(data, ana);
            compute.compute();
            setResultValues(data, ana);
            formatValues(data, ana);
            setUnits(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 the 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 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();

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

    /**
     * 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;
        AnalyteParameterViewDO ap;
        AFP afp;
        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.setIsDiabetic(YES_STR.equals(getValue("insulin_dep_diabetic")));
        compute.setBirthDate(getDateValue(getValue("birth")));
        compute.setLMPDate(getDateValue(getValue("lmp")));
        compute.setUltrasoundDate(getDateValue(getValue("ultrasound")));
        compute.setCRL(getIntegerValue(getResult("crl"), sm, ana));
        compute.setBPD(getIntegerValue(getResult("bpd"), sm, ana));
        compute.setWeeksDays(getDoubleValue(getResult("weeks_days"), sm, ana, false));

        /*
         * 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 AFP
         */
        ap = paramMap != null ? paramMap.get(getAnalyteId("afp_mom")) : null;
        afp = (AFP) compute.getAFP();
        afp.setP1(getP1(ap));
        afp.setP2(getP2(ap));
        afp.setP3(getP3(ap));
        afp.setResult(getDoubleValue(getResult("afp_mom", "result"), sm, ana, true));
    }

    /**
     * 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 gaCurr;
        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);
        /*
         * MoM
         */
        setValue(getResult("afp_mom"), compute.getAFP().getMomFinal(), rf, false, data, ana);
        /*
         * risks, screening cutoffs(limits), interpretations, recommended
         * actions; ntd
         */
        value = null;
        if (compute.getLimitNTD() != null)
            value = DataBaseUtil.concatWithSeparator("NTD >=", " ", Util.format(compute.getLimitNTD(), 1, proxy));
        setValue(getResult("afp_mom", "screen_cutoff"), value, rf, false, data, ana);
        setInterpretation("afp_mom", compute.getInterpretationNTD(), rf, data, ana);
        setRecommendedAction("afp_mom", compute.getInterpretationNTD(), rf, data, ana);
        /*
         * gestational ages, methods
         */
        gaCurr = Util.getWeeksAndDays(compute.getGestAgeCurr());
        setValue(getResult("gest_age"), gaCurr, rf, false, data, ana);
        setGestAgeMethod("gest_age", compute.getGestAgeCurrMethod(), rf, ana, data);
    }

    /**
     * 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 weeks & days have one fractional digit
         */
        formatValue(getResult("mat_age_at_delivery"), 1, tm.getFormatter(), false, data, ana);
        formatValue(getResult("weeks_days"), 1, tm.getFormatter(), true, 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);
    }

    /**
     * Sets the values in the "Unit" column for various row analytes such as
     * "Maternal Age At Delivery", "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("mat_age_at_delivery", UNIT_YEARS, rf, data, ana);
        setUnit("crl", UNIT_MM, rf, data, ana);
        setUnit("bpd", UNIT_MM, rf, 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 || res.getValue() == null)
            return null;
        try {
            return Integer.valueOf(res.getValue());
        } catch (NumberFormatException e) {
            throw getValueInvalidException(res, sm, ana);
        }
    }

    /**
     * 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 the value in the "Unit" column for a row analyte such as
     * "Maternal Age At Delivery", "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
     * 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;
            }
        }
        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"
     */
    private void setRecommendedAction(String externalId, Interpretation interp, 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:
            case PRES_POS_AS:
                value = ACTION_SEE_ACHE;
                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, AnalysisViewDO ana,
            SampleSO data) 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);
    }
}