V2_0.java [src/java/m/rusle2] Revision: 61d88444f1fe0c65d16f73d830d0842bc0ce67ec  Date: Fri Sep 11 13:24:36 MDT 2015
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package m.rusle2;

import c.PostGIS;
import csip.Config;
import csip.ServiceException;
import java.io.File;
import java.io.FileOutputStream;
import java.util.*;
import java.util.concurrent.Callable;
import javax.ws.rs.*;
import oms3.annotations.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;
import org.codehaus.jettison.json.JSONException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import csip.ModelDataService;
import static csip.ModelDataService.EXEC_FAILED;
import static csip.ModelDataService.EXEC_OK;
import static csip.ModelDataService.KEY_REQUEST_RESULTS;
import static csip.ModelDataService.REPORT_DIM;
import static csip.ModelDataService.REPORT_NAME;
import static csip.ModelDataService.REPORT_TYPE;
import static csip.ModelDataService.REPORT_VALUE;
import static csip.ModelDataService.VALUE;
import static util.ErosionConst.*;

import csip.utils.Binaries;
import csip.utils.JSONUtils;
import csip.utils.Services;
import csip.annotations.Polling;
import static m.rusle2.R2Run.LOG;

/**
 * REST Web Service. Front end callable.
 *
 * @author wlloyd, od
 */
@Name("Rusle2")
@Description("IET / Phacil pyrome version of R2")
@VersionInfo("2.0")
@Path("m/rusle2/2.0")
@Polling(first = 1000, next = 1000)
public class V2_0 extends ModelDataService {

    static final boolean PRODUCTION_MODE = true;
    //
    static final String R2_TMP_FILENAME = "rusle2csip";
    static final String R2_TMP_FILEEXT = ".py";
    static final String IO_TIMING_FILENAME = "/tmp/io-timing";
    static final String IO_TIMING_FILEEXT = ".txt";
    static final String REPORT_JSON_TEMPLATE_FILENAME = "Rusle2_Report.json";
    static final String REPORT_RSH_FILENAME = "rusle2_report.rsh";

    /*
     * The R2 Run
     */
    final R2Run r2run = new R2Run();
    static final List<String> potResults = Collections.unmodifiableList(Arrays.asList(
            RES_SLOPE_DELIVERY,
            RES_SLOPE_T_VALUE,
            RES_SLOPE_DEGRAD,
            RES_SLOPE_EQUIV_DIESEL_USE_PER_AREA,
            RES_SOIL_COND_INDEX_STIR_VAL,
            RES_SOIL_COND_INDEX_RESULT,
            RES_SEG_SIM_DAY_LIVE_BIOMASS,
            RES_SEG_SIM_DAY_COVER_MASS_SUM,
            RES_SEG_SIM_DAY_STAND_MASS_SUM,
            RES_SEG_SIM_DAY_CANOPY_COVER,
            RES_SEG_SIM_DAY_PERENN_VEG_LIVE_HEIGHT,
            RES_SLOPE_SIM_DAY_DEGRAD,
            RES_SURF_RES_OUTPUTS_SURF_COV_AT_OP,
            RES_SOIL_COND_INDEX_OM_SUBFACTOR,
            RES_SOIL_COND_INDEX_FO_SUBFACTOR,
            RES_SOIL_COND_INDEX_ER_SUBFACTOR));
    //
    String climate;
    String soil;
    String mgmt;
    //

    @Override
    protected void preprocess() throws Exception {
        // check for requested output.

        try {
            LOG.info("\n\n\n\n\nMETAINFO:");
            LOG.info("\n" + getMetainfo().toString());
            LOG.info("\n\n\n");
            JSONUtils.checkValidResultRequest(getMetainfo(), potResults);
        } catch (ServiceException se) {
            LOG.severe("\n\n\nERROR!!!\n\n\n" + se.toString());
            LOG.warning("No Rusle2 return parameters requested! Will use defaults: SLOPE_DELIVERY, SLOPE_T_VALUE, SLOPE_DEGRAD");
            List<String> params = new LinkedList<String>();
            params.add(RES_SLOPE_DELIVERY);
            params.add(RES_SLOPE_T_VALUE);
            params.add(RES_SLOPE_DEGRAD);
            
            // check if these are being set
            params.add("CONTOUR_SYSTEM_PTR");
            params.add("STRIP_BARRIER_SYSTEM_PTR");
            params.add("HYD_ELEM_SYSTEM_PTR");
            
            getMetainfo().put(KEY_REQUEST_RESULTS, params);
        }

        // check if sufficient input is there.
        //JSONUtils.checkKeyExists(getParamMap(), KEY_CLIMATES);
        JSONUtils.checkKeyExists(getParamMap(), KEY_SOILS);
//        JSONUtils.checkKeyExists(getParamMap(), KEY_MANAGEMENTS);
        JSONUtils.checkKeyExists(getParamMap(), KEY_LENGTH);
        JSONUtils.checkKeyExists(getParamMap(), KEY_STEEPNESS);

        JSONUtils.checkKeyExists(getParamMap(), KEY_MGMTS);

        File r2script = new File(getWorkspaceDir(), R2_TMP_FILENAME + R2_TMP_FILEEXT);
        createInputFile(r2script, getMetainfo(), getParamMap());
    }

    @Override
    protected Callable<String> createCallable() throws Exception {
        return new Callable<String>() {

            @Override
            public String call() throws Exception {
                
                int result = r2run.executePyrome(new File(getWorkspaceDir(), R2_TMP_FILENAME + R2_TMP_FILEEXT));
                if (result != 0) {
                    return EXEC_FAILED;
                }
                return EXEC_OK;
            }
        };
    }

    @Override
    protected JSONArray createResults() throws Exception {
        JSONArray results = new JSONArray();
//      results.put(JSONUtils.data(KEY_SLOPE_DELIVERY, 3.0));
        for (String r : JSONUtils.getRequestedResults(getMetainfo())) {
            results.put(JSONUtils.data(r, r2run.getResultPyrome(r)));
        }

        results.put(JSONUtils.data(KEY_CLIMATES, climate));
        results.put(JSONUtils.data(KEY_SOILS, soil));
        results.put(JSONUtils.data(KEY_MANAGEMENTS, mgmt));
//      results.put(JSONUtils.data(KEY_ALT_R2DB, in_ALT_R2DB));
        return results;
    }

    private void createInputFile(File file, JSONObject metainfo, Map<String, JSONObject> param)
            throws Exception {

        double steepness = JSONUtils.getDoubleParam(param, KEY_STEEPNESS, 0.0);
        double length = JSONUtils.getDoubleParam(param, KEY_LENGTH, 0.0);
        boolean resolveLoc = JSONUtils.getBooleanParam(param, KEY_RESOLVE_LOCATION, false);
        double latitude = JSONUtils.getDoubleParam(param, KEY_LATITUDE, 0.0);
        double longitude = JSONUtils.getDoubleParam(param, KEY_LONGITUDE, 0.0);

        String climatePtr = JSONUtils.getStringParam(param, KEY_CLIMATES, "");
        String soilPtr = JSONUtils.getStringParam(param, KEY_SOILS, "");
        String managementFormalName[] = new String[]{};
        
        String contourSystem = JSONUtils.getStringParam(param, KEY_CONTOUR_SYSTEM_PTR, "");
        String stripBarrierSystem = JSONUtils.getStringParam(param, KEY_STRIP_BARRIER_SYSTEM_PTR, "");
        String hydElemSystem = JSONUtils.getStringParam(param, KEY_HYD_ELEM_SYSTEM_PTR, "");

//        String mgmtPtr = JSONUtils.getStringParam(param, KEY_MANAGEMENTS, "");
//        if (!mgmtPtr.startsWith("managements\\")) {
//            mgmtPtr = "managements\\" + mgmtPtr;
//        }
        // management conversion
        // Make file line separator unix compatible. ???? not sure if needed.
        System.setProperty("line.separator", "\n");
        //
        JSONArray managements = JSONUtils.getJSONArrayParam(param, KEY_MGMTS);
        

        LOG.info("managements array=" + managements.toString());
        managementFormalName = new String[managements.length()];

        for (int i = 0; i < managements.length(); i++) {
            LOG.info("management #" + i);

            JSONObject lmod = (JSONObject) managements.get(i);
            LOG.info(lmod.toString());

            LOG.info("creating Jim's translator.");
            lmod2rusle2.Rusle2Translator translator = new lmod2rusle2.Rusle2Translator();
            LOG.info("trying to put in the lmod json");
            translator.readJsonString(lmod.toString());
            LOG.info("trying to translate=" + translator.Translate());

//            // extract the element as JSON
//            Document lmod_xml = Lmod2Rusle2.readJSON(lmod, false);
//            // Translate the file into RUSLE2 format
//            Document r2_xml = Lmod2Rusle2.LmodXmlToRusle2Xml(lmod_xml, "F");
            Document r2_xml = translator.getDocument();

            LOG.info("R2 XML FILE from trhe translator:\n\n" + translator.getRusle2Xml());
            LOG.info("R2 XML FILE:\n\n" + translator.getRusle2Xml());
            //LOG.info("LMOD XML FILE:\n\n" + lmod_xml.getTextContent());

//            if ((lmod_xml != null) && (lmod_xml.getElementById("Filename") != null))
//                managementFormalName[i] = lmod_xml.getElementById("Filename").getTextContent();
            if ((r2_xml != null) && (r2_xml.getElementsByTagName("Filename") != null)) {
                NodeList nl = r2_xml.getElementsByTagName("Filename");
                for (int ii = 0; ii < nl.getLength(); ii++) {
                    LOG.info("filename node=" + nl.item(ii).getTextContent());
                    managementFormalName[i] = nl.item(ii).getTextContent();
                }
            }
            translator.writeRusle2Xml(getWorkspaceDir().toString(), "lmod_file" + i + ".xml");
        }

        String altR2db = null;
        if (param.get(KEY_ALT_R2DB) != null) {
            altR2db = param.get(KEY_ALT_R2DB).getString(VALUE);
        }

        PostGIS db = PostGIS.singleton();

        // If this is a Lat/Lng request, then we need to make a DB lookup
        // to obtain the Climate and Soil information and ignore whatever
        // may have been passed in
        LOG.info("Rulse 2 model request resolveLocation=" + resolveLoc);
        //if (resolveLoc) {

        if (db != null) {
            PostGIS.FileQryResult climate = db.findClimate(latitude, longitude);
            if (climate == null) {
                LOG.warning("no climate for lat=" + latitude + "\nfor long=" + longitude + "\n");
                climatePtr = "";
            } else {
                climatePtr = climate.file_path + "\\" + climate.file_name;
            }

            // to do
            // use only the first soil for now - later we need to use all of them
            String cokey = soilPtr.replace(",", " ");
            // strip first and last char
            String t2 = cokey.substring(1, cokey.length() - 1);
            String delims = "\"";
            String tokens[] = t2.split(delims);
            LinkedList<String> lst = new LinkedList();
            for (String token : tokens) {
                if (token.trim().length() > 0) {
                    lst.add(token);
                }
            }
            cokey = lst.get(0);  // first soil only for now
            PostGIS.FileQryResult soil = db.findSoilsByCokey(cokey, longitude);
            if (soil == null) {
                LOG.warning("No soil for lat=" + latitude + "\nfor long=" + longitude + "\n");
                soilPtr = "";
            } else {
                soilPtr = soil.file_path + "\\" + soil.file_name;
            }
        } else {
            LOG.warning("WARNING: Unable to access database to resolve the climate and soil information for this lat/long driven rusle2 model run.  Using deafults.");
        }
        //}

        FileOutputStream fos = new FileOutputStream(file);

        fos.write("import sys\n".getBytes());
        fos.write("sys.path.append('/tmp/csip/bin/bin/win-x86/')\n".getBytes());
        
        fos.write("from pyrome import *\n".getBytes());
        fos.write("from time import sleep\n".getBytes());

        fos.write("def linearTest(save = False, openFlags = RX_FILESOPEN_TEMP):\n".getBytes());
        fos.write("    romeDLL = RomeInit('')\n".getBytes());
        fos.write("    files = RomeGetFiles(romeDLL)\n".getBytes());
        fos.write("    engine = RomeGetEngine(romeDLL)\n".getBytes());
        fos.write("    RomeEngineSetAutorun(engine,RX_FALSE)\n".getBytes());

        fos.write("    #Inputs\n".getBytes());
        String text = "    slopes=['" + steepness + "']\n";
        fos.write(text.getBytes());
        text = "    lengths=['" + length + "']\n";
        fos.write(text.getBytes());
        fos.write("    soilIndex=[0]\n".getBytes());
        fos.write("    manIndex=[0]\n".getBytes());
        fos.write("    results = list()\n".getBytes());

        fos.write("    #Run a simple profile\n".getBytes());
        fos.write("    profile = RomeFilesOpen(files,'profiles\\\\csippyrome',openFlags)\n".getBytes());
        fos.write("    mgmt = RomeFilesOpen(files, '#XML:lmod_file0.xml',0)\n".getBytes());
        fos.write("    cli = RomeFilesOpen(files, '#XML:cli_file0.xml',0)\n".getBytes());
        fos.write("    soil = RomeFilesOpen(files, '#XML:soils_file0.xml',0)\n".getBytes());

        text = "    RomeFileSetAttrValue(profile, 'SLOPE_HORIZ', '" + length + "', 0)\n";
        fos.write(text.getBytes());
        text = "    RomeFileSetAttrValue(profile, 'SLOPE_STEEP', '" + steepness + "', 0)\n";
        fos.write(text.getBytes());

        fos.write("    # Set SOIL_PTR\n".getBytes());
        text = "    RomeFileSetAttrValue(profile, 'SOIL_PTR', '" + StringEscapeUtils.escapeJava(soilPtr) + ".xml',0)\n";
        fos.write(text.getBytes());
        fos.write("    print('SOIL_PTR=%s' % RomeFileGetAttrValue(profile, 'SOIL_PTR', 0))\n".getBytes());

        fos.write("    # Set MAN_PTR\n".getBytes());
        text = "    RomeFileSetAttrValue(profile, 'MAN_PTR', '" + StringEscapeUtils.escapeJava(managementFormalName[0]) + "',0)\n";
        fos.write(text.getBytes());
        fos.write("    print('MAN_PTR=%s' % RomeFileGetAttrValue(profile, 'MAN_PTR', 0))\n".getBytes());

        fos.write("    # SET CLIMATE_PTR\n".getBytes());
        fos.write("    RomeFileSetAttrValue(profile, 'CLIMATE_PTR','climates\\\\aaa',0)\n".getBytes());
        fos.write("    print('CLIMATE_PTR=%s' % RomeFileGetAttrValue(profile, 'CLIMATE_PTR', 0))\n".getBytes());
        
        if ((contourSystem != null) && (contourSystem.length() > 1)) {
            text = "    RomeFileSetAttrValue(profile, 'CONTOUR_SYSTEM_PTR', '" + contourSystem + "',0)\n";
            fos.write(text.getBytes());
        }

        fos.write("    RomeFileSave(profile)\n".getBytes());
        fos.write("    RomeEngineRun(engine)\n".getBytes());
//        fos.write("    results.append(RomeFileGetAttrValue(profile, 'SLOPE_SOIL_LOSS', 0))\n".getBytes());
//        fos.write("    results.append(RomeFileGetAttrValue(profile, 'SLOPE_DELIVERY', 0))\n".getBytes());
//        fos.write("    results.append(RomeFileGetAttrValue(profile, 'SLOPE_DEGRAD', 0))\n".getBytes());

        // Request specific outputs from Rusle2
        for (String r : JSONUtils.getRequestedResults(metainfo)) {
            text = "    results.append(RomeFileGetAttrValue(profile, '" + r + "', 0))\n";
            fos.write(text.getBytes());
            text = "    print('" + r + "=%s' % RomeFileGetAttrValue(profile, '" + r + "', 0))\n";
            fos.write(text.getBytes());
        }
        
//        fos.write("    print('SLOPE_DELIVERY=%s' % RomeFileGetAttrValue(profile, 'SLOPE_DELIVERY', 0))\n".getBytes());
//        fos.write("    print('SLOPE_T_VALUE=%s' % RomeFileGetAttrValue(profile, 'SLOPE_T_VALUE', 0))\n".getBytes());
//        fos.write("    print('SLOPE_DEGRAD=%s' % RomeFileGetAttrValue(profile, 'SLOPE_DEGRAD', 0))\n".getBytes());
        fos.write("    RomeFileClose(profile)\n".getBytes());
        fos.write("    return results\n".getBytes());

        fos.write("if __name__ == \"__main__\":\n".getBytes());
        fos.write("    #inputs\n".getBytes());
        fos.write("    save = False\n".getBytes());
        fos.write("    #Run simulation\n".getBytes());
        fos.write("    romeDLL = RomeInit('pyrome /DirRoot=/tmp/csip/bin/bin/win-x86')\n".getBytes());
        fos.write("    database = RomeGetDatabase(romeDLL)\n".getBytes());
        // prints all outputs in single array
        //fos.write("    print(results[:])\n".getBytes());

        // Specify the nginx server
        String r2db = Config.getString("r2.db", "http://oms-db.engr.colostate.edu/r2");
        if (altR2db == null) {
            text = "    RomeDatabaseOpen(database,'" + r2db + "')\n";
            fos.write(text.getBytes());
        } else {
            String altr2db = r2db.substring(0, r2db.lastIndexOf("/")) + "/model-data/" + altR2db;
            text = "    RomeDatabaseOpen(database,'" + altr2db + "')\n";
            fos.write(text.getBytes());
        }
        fos.write("    results = linearTest(save)\n".getBytes());
        fos.write("    RomeDatabaseClose(database)\n".getBytes());
        fos.write("    RomeExit(romeDLL)\n".getBytes());
        

// to do
// Don't worry about multiple mgmts for now
        

//        if ((managements != null) && (managements.length() >= 1)) {
//            fos.write(("FilesOpen \"#XML:" + new File(getWorkspaceDir(), "lmod_file" + 0 + ".xml" + "\"\n")).getBytes());
//            // fos.write(("Activate \"" + managementFormalName[0] + "\"\n").getBytes());
//            fos.write(("RomeFileSetAttrValue MAN_BASE_PTR \"" + managementFormalName[0] + "\"\n").getBytes());
//            mgmt = managementFormalName[0];
//        }

        // to do 
        // commented out - using default for now
        
        String soilHttpPtr = r2db + "/" + soilPtr.replace("\\","/") + ".xml";
        r2run.prepareSoilsFile(soilHttpPtr, new File(getWorkspaceDir(), "soils_file0.xml"), true);
        
        String cliHttpPtr = r2db + "/" + climatePtr.replace("\\", "/") + ".xml";
        r2run.prepareClimateFile(cliHttpPtr, new File(getWorkspaceDir(), "cli_file0.xml"));


        // Rusle2 report
        //fos.write("READ \"rusle2_report.rsh\"\n".getBytes());

        fos.close();

        // Unpack the report rsh file in the work space dir 
        Binaries.unpackResourceAbsolute("/bin/win-x86/" + REPORT_RSH_FILENAME, getWorkspaceDir().toString() + "/" + REPORT_RSH_FILENAME);

        climate = climatePtr;
        soil = soilPtr;
//        mgmt = mgmtPtr;

        LOG.info("R2 script: " + file.toString());
    }

    private String getFilenumber(String filename) {
        return filename.substring(R2_TMP_FILENAME.length(), filename.length() - R2_TMP_FILEEXT.length());
    }

    @Override
    protected JSONArray createReport() throws Exception {
        String sessionWorkDir = getWorkspaceDir().toString();
        String reportJSON = Binaries.unpackResourceAbsolute("/bin/win-x86/" + REPORT_JSON_TEMPLATE_FILENAME, sessionWorkDir + "/" + REPORT_JSON_TEMPLATE_FILENAME).toString();
        File reportTemplate = new File(sessionWorkDir + "/" + REPORT_JSON_TEMPLATE_FILENAME);
        String sReportJSON = FileUtils.readFileToString(reportTemplate);
        JSONArray reportItemsFromTemplate = new JSONArray(sReportJSON);
        JSONArray reportItemsOutput = new JSONArray();

        for (int i = 0; i < reportItemsFromTemplate.length(); i++) {
            JSONObject obj = (JSONObject) reportItemsFromTemplate.get(i);
            String itemName = obj.getString(REPORT_NAME);
            String dimension = JSONUtils.getJSONString(obj, REPORT_DIM, null);
            String type = JSONUtils.getJSONString(obj, REPORT_TYPE, "");
            //LOG.info("Attempting to fetch results for:" + itemName);
            String value = r2run.getResult("\"" + itemName + "\"");
            //LOG.info("the value='" + value + "'");
            if ((value != null) && (value.equals("null"))) {
                obj.put(REPORT_VALUE, (String) null);
                // if there is no value, then item should be removed from the array
            } else {
                if ((dimension != null) && (dimension.length() > 0) && (value != null) && (!value.equals("null"))) {
                    try {
                        obj.put(REPORT_VALUE, new JSONArray(value));
                    } catch (JSONException je) {
                        obj = processReportElement(obj, type, value);
                    }
                } else {
                    obj = processReportElement(obj, type, value);
                }
                obj.remove(REPORT_TYPE);
                //LOG.info("units output is='" + JSONUtils.getString(obj,REPORT_UNITS,null) + "'");
                //LOG.info("obj to add=" + obj.toString());
                reportItemsOutput.put(obj);
            }
        }

        return reportItemsOutput;
        //LOG.info("report jsonarray=" + reportItemsOutput.toString());        
        //LOG.info("Rusle2 report output obj=" + reportObj.toString());        
    }

    private JSONObject processReportElement(JSONObject obj, String type, String value) throws Exception {
        if ((type.equals("TEXT")) || (type.equals("FILENAME")) || (type.equals("DATE"))) {
            // because the RomeShell returns escaped strings, and the JSONObject in Java re-escapes them
            // we have to remove one level of "escaping" !!! 
            String newString = StringEscapeUtils.unescapeJava(value);
            obj.put(REPORT_VALUE, Services.removeFirstLastChar(newString));
        }
        if (type.equals("INTEGER") && (value != null) && (!value.equals("null")) && (!value.equals("\"\""))) {
            obj.put("value", new Double(value).intValue());
        }
        if (type.equals("REAL") && (value != null)) {
            if ((!value.equals("null")) && (!value.equals("\"\""))) {
                obj.put(REPORT_VALUE, new Double(value).doubleValue());
            }
            if (value.equals("\"\"")) {
                obj.put(REPORT_VALUE, new Double(0).doubleValue());
            }
        }
        if (type.equals("")) {
            obj.put(REPORT_VALUE, value);
        }
        return obj;
    }
}