V1_0.java [src/java/m/svap/svap05b_svaphydrob] Revision: default  Date:
/*
 * $Id$
 *
 * This baseFlowOutFile is part of the Cloud Services Integration Platform (CSIP),
 * a Model-as-a-Service framework, API, and application suite.
 *
 * 2012-2017, OMSLab, Colorado State University.
 *
 * OMSLab licenses this baseFlowOutFile to you under the MIT license.
 * See the LICENSE baseFlowOutFile in the project root for more information.
 */
package m.svap.svap05b_svaphydrob;

import csip.Client;
import csip.Config;
import csip.ModelDataService;
import csip.ServiceException;
import csip.annotations.Polling;
import csip.utils.JSONUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import javax.ws.rs.Path;
import csip.annotations.Description;
import csip.annotations.Name;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import svap.utils.SVAPUtils;

/**
 * SVAP-05b: Get Stream Hydrology for Watershed Description
 *
 * This service computes the number of wetted monthlyCount if a stream is
 * intermittent, or number of monthlyCount at baseflow if the stream is
 * perennial.
 *
 * @author Rumpal Sidhu
 */
@Name("SVAP-05b: Get Stream Hydrology for Watershed Description")
@Description("This service computes the number of wetted months if a stream is "
        + "intermittent, or number of months at baseflow if the stream is perennial.")
@Path("m/svap/svaphydrob/1.0")
@Polling(first = 10000, next = 2000)

public class V1_0 extends ModelDataService {

    private int assessmentId;
    private String stationId, organization;
    private ArrayList<StreamFlow> streamFlowList = new ArrayList();
    private int[] monthlyCount = new int[12];
    private int numberOfMonths;
    private static final String BASEFLOW_URL = "http://csip.engr.colostate.edu:8088/csip-cfa/m/cfa/baseflow/1.0";

    @Override
    protected void preProcess() throws ServiceException {
        assessmentId = parameter().getInt("assessment_id");
        stationId = parameter().getString("station_id");
        organization = parameter().getString("organization");
        validateSource();
    }

    @Override
    protected void doProcess() throws Exception {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String endDate = simpleDateFormat.format(calendar.getTime());
        calendar.add(Calendar.YEAR, -1);
        String beginDate = simpleDateFormat.format(calendar.getTime());

        JSONObject requestJSON = createRequestJSON(beginDate, endDate);
        JSONObject responseJSON = callBaseFlowService(requestJSON);

        String baseFlowFileName = getBaseFlowFileName(responseJSON);

        File baseFlowOutFile = getBaseFlowFile(baseFlowFileName);
        parseBaseFlowFile(baseFlowOutFile);

        if (isPerennial()) {
            getBaseFlow();
        } else {
            getWettedMonths();
        }

        for (int i = 0; i < 12; i++) {
            if (monthlyCount[i] > 0) {
                numberOfMonths++;
            }
        }
    }

    @Override
    protected void postProcess() throws JSONException {
        results().put("assessment_id", assessmentId, "Assessment Identifier");
        results().put("station_id", stationId, "Station Identifier");
        results().put("organization", organization, "Organization that the flow station belongs to (USGS | STORET | CDSN)");
        results().put("stream_type", isPerennial() ? "Perennial" : "Intermittent", "Stream type (intermittent or perennial)");
        String name = "number_of_" + (isPerennial() ? "months_at_baseflow" : "wetted_months");
        String description = "Number of " + (isPerennial() ? "months at baseflow" : "wetted months");
        results().put(name, numberOfMonths, description);
        JSONArray monthArray = new JSONArray();
        name = "number_of_" + (isPerennial() ? "days_at_baseflow_" : "wetted_days_");
        description = "Number of " + (isPerennial() ? "days at baseflow in " : "wetted days in ");
        for (int i = 0; i < 12; i++) {
            monthArray.put(JSONUtils.dataDesc(name + SVAPUtils.MONTH_NAMES[i].toLowerCase().substring(0, 3), monthlyCount[i], description + SVAPUtils.MONTH_NAMES[i]));
        }
        results().put("month_list", monthArray);
    }

    private void validateSource() throws ServiceException {
        if (!organization.equals("USGS") && !organization.equals("STORET") && !organization.equals("CDSN")) {
            throw new ServiceException("Invalid Database");
        }
    }

    private JSONObject createRequestJSON(String beginDate, String endDate) throws ServiceException, JSONException {
        JSONObject requestJSON = new JSONObject();
        JSONObject metainfo = new JSONObject();
        metainfo.put("MultipartRequest", "Bundled Service Request");
        metainfo.put("OriginalSource", request().getHost());
        metainfo.put("OriginalRequest", request().getURL());
        metainfo.put("OriginalSUID", getSUID());
        requestJSON.put("metainfo", metainfo);
        JSONArray parameter = new JSONArray();
        parameter.put(JSONUtils.data("database", organization));
        parameter.put(JSONUtils.data("org_id", ""));
        parameter.put(JSONUtils.data("station_id", stationId));
        parameter.put(JSONUtils.data("station_name", ""));
        parameter.put(JSONUtils.data("begin_date", beginDate));
        parameter.put(JSONUtils.data("end_date", endDate));
        parameter.put(JSONUtils.data("ndmin", 10));
        parameter.put(JSONUtils.data("ndmax", 10));
        parameter.put(JSONUtils.data("drainage_area", 0));
        parameter.put(JSONUtils.data("model_type", "BFLOW"));
        parameter.put(JSONUtils.data("merge_datasets", false));
        parameter.put(JSONUtils.data("merge_method", ""));
        parameter.put(JSONUtils.data("user_data", ""));
        requestJSON.put("parameter", parameter);
        return requestJSON;
    }

    private JSONObject callBaseFlowService(JSONObject requestJSON) throws Exception {
        String url = Config.getString("service.csip.baseflow.url", BASEFLOW_URL);
        EntityBuilder builder = EntityBuilder.create();
        builder.setText(requestJSON.toString());
        builder.setContentType(ContentType.APPLICATION_JSON);

        HttpPost post = new HttpPost(url);
        post.setEntity(builder.build());

        String response;
        try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();) {
            try (CloseableHttpResponse httpResponse = httpClient.execute(post)) {
                InputStream responseStream = httpResponse.getEntity().getContent();
                response = IOUtils.toString(responseStream);
            } catch (IOException ex) {
                throw new SQLException("Communication error with SDM host: " + ex.getMessage(), ex);
            } finally {
                post.releaseConnection();
                post.completed();
            }
        }
        return new JSONObject(response);
    }

    private String getBaseFlowFileName(JSONObject responseJSON) throws JSONException {
        JSONArray resultArray = responseJSON.getJSONArray("result");
        Map<String, JSONObject> topLevel = JSONUtils.preprocess(resultArray);
        String fileName = JSONUtils.getStringParam(topLevel, "baseflow.out", null);
        return fileName;
    }

    private File getBaseFlowFile(String fileName) throws Exception {
        Client client = new Client();
        File file = new File("test.txt");
        client.doGET(fileName, file);
        return file;
    }

    private void parseBaseFlowFile(File file) throws FileNotFoundException, IOException, ParseException {
        BufferedReader br = new BufferedReader(new FileReader(file));
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 2; i++) {
            br.readLine();
        }

        String line;
        while ((line = br.readLine()) != null) {
            String[] split = line.split(",");
            if (split.length > 0 && Integer.parseInt(split[0].trim()) > 0) {
                Date date = formatter.parse(split[0].trim() + "-" + split[1].trim() + "-" + split[2].trim());
                double streamFlow = Double.parseDouble(split[3].trim());
                double baseflow = Double.parseDouble(split[4].trim());
                streamFlowList.add(new StreamFlow(date, streamFlow, baseflow));
            }
        }
    }

    private boolean isPerennial() {
        for (StreamFlow stm : streamFlowList) {
            if (stm.streamFlow <= 0) {
                return false;
            }
        }
        return true;
    }

    private void getWettedMonths() {
        for (StreamFlow stm : streamFlowList) {
            if (stm.streamFlow > 0) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(stm.date);
                int month = cal.get(Calendar.MONTH);
                monthlyCount[month]++;
            }
        }
    }

    private void getBaseFlow() {
        for (StreamFlow stm : streamFlowList) {
            if (stm.streamFlow == stm.baseFlow) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(stm.date);
                int month = cal.get(Calendar.MONTH);
                monthlyCount[month]++;
            }
        }
    }

    static class StreamFlow {

        Date date;
        double streamFlow;
        double baseFlow;

        public StreamFlow(Date date, double streamFlow, double baseFlow) {
            this.date = date;
            this.streamFlow = streamFlow;
            this.baseFlow = baseFlow;
        }

    }
}