V1_0.java [src/java/m/wqm/scpestslp] Revision: 886be74d86186df3b90bbf4c767699f709c10060  Date: Wed Apr 20 22:50:24 MDT 2016
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package m.wqm.scpestslp;

import csip.ModelDataService;
import csip.ServiceException;
import csip.utils.JSONUtils;
import java.util.ArrayList;
import java.util.Map;
import javax.ws.rs.Path;
import oms3.annotations.Description;
import oms3.annotations.Name;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;

/**
 *
 * @author Srinivas
 * @author Rumpal Sidhu
 * @author Shaun Case
 */
@Name("WQM-07: Pesticide Soil Leaching Potential (PesticideSLP)")
@Description("This service computes pesticide soil leaching potential for soil components in an area of analysis, and then computes pesticide soil leaching potential representing the area of analysis. The service primarily will consume data from the WQM-2 WQMSoilAttributes service to compute soil pesticide leaching potential used later by the WQM-13 service to compute threshold treatment level scores.")
@Path("m/pesticide_slp/1.0")

public class V1_0 extends ModelDataService {

    private static final int VERY_LOW = 1;
    private static final int LOW = 2;
    private static final int INTERMEDIATE = 3;
    private static final int HIGH = 4;
    private final String comp_pslp_string[] = {"", "VERY LOW", "LOW", "INTERMEDIATE", "HIGH"};

    private int comp_pslp_number;
    private String aoa_pslp;
    private ArrayList<AoA> aoAs;
    private String error_msg;

    // request payload
    // Store all of the input data by AoA
    private Input inputData;

    // response payload
    // Storeage the results
    private Result result;

    @Override
    // reading the inputs from the json file into input object and placing it in the arraylist
    protected void preProcess() throws Exception {
        comp_pslp_number = 0;
        aoa_pslp = "";
        error_msg = "";
        result = null;

        aoAs = new ArrayList<>();
        inputData = new Input(aoAs);

        if (inputData.getError()) {
           throw new ServiceException(inputData.getErrorMsg());
        }
    }

    @Override
    protected void doProcess() throws Exception {
        if (error_msg.isEmpty()) {
            for (AoA aoA : aoAs) {
                if (!aoA.process()) {
                    error_msg += aoA.getErrorMsg();
                    break;
                }
            }
            if (error_msg.isEmpty()) {
                result = new Result(aoAs);
            }
        }

       
    }

    @Override
    //writing the results back to JSON
    protected void postProcess() throws Exception {
        if (null != result) {
            result.putResults();
        }
    }

    class AoA {

        private ArrayList<SoilComponent> soilComponents;
        private final int AoAId;
        private String error_msg;
        private double cum_pslp_product;
        private double aoa_area;
        private String aoa_pslp;

        AoA(int AoAId, JSONArray components) {
            this.cum_pslp_product = 0;
            this.aoa_area = 0;
            this.AoAId = AoAId;
            this.error_msg = "";
            this.aoa_pslp = "NONE";

            this.soilComponents = new ArrayList<>();

            if (AoAId == -1) {
                this.error_msg = " Invalid AoA Identifier. ";
            } else {
                try {
                    if ((null != components) && (components.length() > 0)) {
                        for (int j = 0; j < components.length(); j++) {
                            Map<String, JSONObject> cokey = JSONUtils.preprocess(components.getJSONArray(j));
                            SoilComponent tSC = new SoilComponent(AoAId, JSONUtils.getStringParam(cokey, "cokey", "err"),
                                    JSONUtils.getStringParam(cokey, "compname", " "), JSONUtils.getDoubleParam(cokey, "aoa_comp_area", 0.0),
                                    JSONUtils.getStringParam(cokey, "aoa_comp_hsg", "err"), JSONUtils.getDoubleParam(cokey, "aoa_comp_kfact", 0.0),
                                    JSONUtils.getDoubleParam(cokey, "aoa_comp_om", 0.0), JSONUtils.getIntParam(cokey, "aoa_comp_hzdepth", 0),
                                    JSONUtils.getBooleanParam(cokey, "aoa_comp_cracksgr24", false), JSONUtils.getBooleanParam(cokey, "aoa_comp_hwt_lt_24", false)
                            );
                            if (!tSC.getError()) {
                                if (!soilComponents.add(tSC)) {
                                    //Shouldn't ever happen, but just in case...
                                    error_msg += "Internal error, could not add this component to the list.";
                                    break;
                                }
                            } else {
                                //Most likely an input validation error here, caught by the subclass
                                error_msg += tSC.getErrorMsg();
                                break;
                            }
                        }// End for loop of components for this AoA
                    } else {
                        //This component was empty or the JSON failed to create an item.
                        error_msg += " This AoA's component is empty or missing, cannot proceed.";
                    }
                } catch (Exception ex) {
                    error_msg += " " + ex.getMessage();
                }
            }
        }

        public Boolean process() {
            Boolean ret_val = true;

            for (SoilComponent component : soilComponents) {
                if (!component.process()) {
                    error_msg += " " + component.getErrorMsg();
                    break;
                }

                cum_pslp_product += (component.getCompPslpNumber() * component.getAoaCompArea());
                aoa_area += component.getAoaCompArea();
            }

            if (error_msg.isEmpty()) {
                double aoa_pslp_fract = (cum_pslp_product / aoa_area);

                if (aoa_pslp_fract <= 1.50) {
                    aoa_pslp = "VERY LOW";
                } else if (aoa_pslp_fract > 1.50 && aoa_pslp_fract <= 2.50) {
                    aoa_pslp = "LOW";
                } else if (aoa_pslp_fract > 2.50 && aoa_pslp_fract <= 3.50) {
                    aoa_pslp = "INTERMEDIATE";
                } else {
                    aoa_pslp = "HIGH";
                }
            }

            return ret_val;
        }

        public JSONArray putResults() throws ServiceException {
            JSONArray AoAData;
            JSONArray components;

            AoAData = new JSONArray();
            try {
                AoAData.put(JSONUtils.dataDesc("AoAId", AoAId, "Area of Analysis Identifier"));
                AoAData.put(JSONUtils.dataDesc("aoa_pslp", aoa_pslp, "Pesticide soil leaching potential representing the AoA"));

                components = new JSONArray();
                for (SoilComponent component : soilComponents) {
                    components.put(component.putResults());
                    if (component.getError()) {
                        error_msg += " " + component.getErrorMsg();
                        break;
                    }
                }

                AoAData.put(JSONUtils.dataDesc("componentlist", components, "List of soil components"));
            } catch (JSONException ex) {
                error_msg += " " + ex.getMessage();
            throw new ServiceException("JSONException",ex);
            }

            return AoAData;
        }

        public Boolean getError() {
            return (!error_msg.isEmpty());
        }

        public String getErrorMsg() {
            return error_msg;
        }

        public String getAoAPslp() {
            return aoa_pslp;
        }

        public int getAoAId() {
            return AoAId;
        }

        class SoilComponent {

            private final int AoAId;
            private final String cokey;
            private final String compname;
            private String comp_pslp;
            private int comp_pslp_number;
            private final double aoa_comp_area;
            private final String aoa_comp_hsg;
            private final double aoa_comp_kfact;
            private final double aoa_comp_om;
            private final int aoa_comp_hzdepth;
            private final boolean aoa_comp_cracksgr24;
            private final boolean aoa_comp_hwt_lt_24;
            private String error_msg;

            SoilComponent(int AoAId, String cokey, String compname, double aoa_comp_area, String aoa_comp_hsg, double aoa_comp_kfact, double aoa_comp_om, int aoa_comp_hzdepth, boolean aoa_comp_cracksgr24, boolean aoa_comp_hwt_lt_24) {
                this.AoAId = AoAId;
                this.cokey = cokey;
                this.compname = compname;
                this.aoa_comp_area = aoa_comp_area;
                this.aoa_comp_hsg = aoa_comp_hsg;
                this.aoa_comp_kfact = aoa_comp_kfact;
                this.aoa_comp_om = aoa_comp_om;
                this.aoa_comp_hzdepth = aoa_comp_hzdepth;
                this.aoa_comp_cracksgr24 = aoa_comp_cracksgr24;
                this.aoa_comp_hwt_lt_24 = aoa_comp_hwt_lt_24;

                this.error_msg = "";
                this.comp_pslp = "";
                this.comp_pslp_number = 0;

                if (cokey.equals("err") || aoa_comp_hsg.equals("err") || (AoAId == -1)) {
                    error_msg += " Invalid input data for this soil component.";
                }
            }

            public int getAoAId() {
                return AoAId;
            }

            public String getCokey() {
                return cokey;
            }

            public String getCompname() {
                return compname;
            }

            public String getCompPslp() {
                return comp_pslp;
            }

            ;
            public int getCompPslpNumber() {
                return comp_pslp_number;
            }

            public double getAoaCompArea() {
                return aoa_comp_area;
            }

            public Boolean getError() {
                return (!error_msg.isEmpty());
            }

            public String getErrorMsg() {
                return (error_msg);
            }

            public boolean process() {
                boolean ret_val = true;

                if (aoa_comp_hwt_lt_24) {
                    comp_pslp_number = HIGH;
                } else if (((aoa_comp_hsg.equals("A") || aoa_comp_hsg.equals("A/D")) && ((aoa_comp_hzdepth * aoa_comp_om) <= 30))
                        || ((aoa_comp_hsg.equals("B") || aoa_comp_hsg.equals("B/D")) && ((aoa_comp_hzdepth * aoa_comp_om) <= 9) && (aoa_comp_kfact <= 0.48))
                        || ((aoa_comp_hsg.equals("B") || aoa_comp_hsg.equals("B/D")) && ((aoa_comp_hzdepth * aoa_comp_om) <= 15) && (aoa_comp_kfact <= 0.26))) {
                    comp_pslp_number = HIGH;
                } else if (((aoa_comp_hsg.equals("B") || aoa_comp_hsg.equals("B/D")) && ((aoa_comp_hzdepth * aoa_comp_om) >= 35) && aoa_comp_kfact >= 0.40)
                        || ((aoa_comp_hsg.equals("B") || aoa_comp_hsg.equals("B/D")) && ((aoa_comp_hzdepth * aoa_comp_om) >= 45) && aoa_comp_kfact >= 0.20)
                        || ((aoa_comp_hsg.equals("C") || aoa_comp_hsg.equals("C/D")) && ((aoa_comp_hzdepth * aoa_comp_om) <= 10) && aoa_comp_kfact <= 0.28)
                        || ((aoa_comp_hsg.equals("C") || aoa_comp_hsg.equals("C/D")) && ((aoa_comp_hzdepth * aoa_comp_om) >= 10))) {
                    if (!aoa_comp_cracksgr24) {
                        comp_pslp_number = LOW;
                    } else {
                        comp_pslp_number = INTERMEDIATE;
                    }
                } else if (aoa_comp_hsg.equals("D")) {
                    if (!aoa_comp_cracksgr24) {
                        comp_pslp_number = VERY_LOW;
                    } else {
                        comp_pslp_number = LOW;
                    }
                } else {
                    if (!aoa_comp_cracksgr24) {
                        comp_pslp_number = INTERMEDIATE;
                    } else {
                        comp_pslp_number = HIGH;
                    }
                }

                comp_pslp = comp_pslp_string[comp_pslp_number];

                return ret_val;
            }

            public JSONArray putResults() throws ServiceException {
                JSONArray componentData;
                componentData = new JSONArray();

                try {
                    componentData.put(JSONUtils.dataDesc("cokey", cokey, "Soil component key"));
                    componentData.put(JSONUtils.dataDesc("compname", compname, "Soil component name"));
                    componentData.put(JSONUtils.dataDesc("comp_pslp", comp_pslp, "Pesticide soil leaching potential representing the soil component"));
                } catch (JSONException ex) {                   
                    error_msg += " " + ex.getMessage();
                    throw new ServiceException("JSONException",ex);
                }

                return componentData;
            }
        }
    }

    public class Input {

        private String error_msg;
        private JSONArray AoAList;

        Input(ArrayList<AoA> aoAs) throws ServiceException {
            error_msg = "";

            try {
                AoAList = getJSONArrayParam("AoAList");
                if (null != AoAList) {
                    for (int i = 0; i < AoAList.length(); i++) {
                        Map<String, JSONObject> componentGroup = JSONUtils.preprocess(AoAList.getJSONArray(i));

                        int AoAId = JSONUtils.getIntParam(componentGroup, "AoAId", -1);

                        if (AoAId != -1) {
                            JSONArray components = JSONUtils.getJSONArrayParam(componentGroup, "componentlist");
                            AoA aoA = new AoA(AoAId, components);
                            if (aoA.getError()) {
                                error_msg += " " + aoA.getErrorMsg();
                                break;
                            } else {
                                aoAs.add(aoA);
                            }
                        } else {
                            error_msg += " The AoAId is not valid or is missing, cannot proceed";
                            break;
                        }
                    }//  End for loop of AoA's
                } else {
                    error_msg += " There were no AoA's found in the input payload.  Cannot proceed.";
                }
            } catch (ServiceException | JSONException ex) {
                error_msg += " " + ex.getMessage();
                throw new ServiceException("JSONException",ex);
            }
        }

        public String getErrorMsg() {
            return error_msg;
        }

        public Boolean getError() {
            return (!error_msg.isEmpty());
        }
    }

    public class Result {

        JSONArray AoAList;

        Result(ArrayList<AoA> aoAs) throws Exception {
            if (!aoAs.isEmpty()) {
                AoAList = new JSONArray();
                for (AoA aoA : aoAs) {
                    AoAList.put(aoA.putResults());
                    if (aoA.getError()) {
                        AoAList.put(JSONUtils.dataDesc("Error:", aoA.getErrorMsg(), "Error message returned while trying to build result output"));
                        break;
                    }
                }
            } else {
                AoAList = null;
            }
        }

        public void putResults() {
            if (null != AoAList) {
                putResult("AoAList", AoAList);
            }
        }
    }

}