V1_0.java [src/java/reports] Revision:   Date:
/*
 * 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 reports;

import com.lowagie.text.DocumentException;
import csip.ModelDataService;
import csip.api.server.ServiceException;
import csip.annotations.*;
import static csip.annotations.ResourceType.OUTPUT;
import static csip.annotations.State.RELEASED;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Iterator;
import java.util.logging.Level;
import javax.ws.rs.Path;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.apache.velocity.tools.generic.NumberTool;
import org.codehaus.jettison.json.JSONArray;

/**
 * This service returns PDF versions of two WEPP reports. The template files are
 * HTML files created using the Apache velocity engine markup.
 *
 * The data resources referenced include:
 * <ul>
 * <li>WEPPHillslopeProfileReport.htm - template for the WEPP hillslope report
 * this is for a single hillslope simulation. </li>
 * <li>328f.htm - template for the multiple WEPP run report. </li>
 * <li>image001.gif - NRCS logo to insert in reports.</li>
 * <li>img9.jpg - USDA logo to insert in reports.</li>
 * </ul>
 *
 * @author jrf
 */
@Name("wepp2012")
@Description("WEPP model PDF Reports - version 2/27/2018")
@State(RELEASED)
@Path("reports/summary/1.0")
@Polling(first = 2000, next = 2000)

// template resources
@Resource(file = "/data/WEPPHillslopeProfileReport.htm", id = "profileReportTemplate")
@Resource(file = "/data/image001.gif", id = "profileReportTemplateLogo")
@Resource(file = "/data/328f.htm", id = "profileReportTemplate328")
@Resource(file = "/data/img9.jpg", id = "profileReportTemplateLogo328")

// Output to capture
@Resource(file = "*stdout.txt *stderr.txt", type = OUTPUT)

public class V1_0 extends ModelDataService {

  private String sessionWorkDir;
  private String pdfResourceName;
  private JSONObject templateInputJson;
  private String outputName;
  private String outputHTML;
  private double[] xdistData;
  private double[] soilLossData;
  private double[] elevData;
  private double[] detachData;

  static final String WEPP_KEY_PDF_RESOURCE = "pdfResourceName";
  static final String WEPP_KEY_JSON_INPUTS = "templateInputJson";
  static final String PDF_OUTPUT_NAME = "outputName";
  static final String HTML_OUTPUT_NAME = "outputHTML";
  static final String WEPP_KEY_DETACHDATA = "detachData";

  // data for graph 
  static final String WEPP_KEY_XDISTDATA = "xdistData";
  static final String WEPP_KEY_SOILLOSSDATA = "soilLossData";
  static final String WEPP_KEY_ELEVATIONDATA = "elevData";


  /**
   * Initialize WEPP-PDF workspace. This requires creating a couple
   * subdirectories.
   *
   * @throws ServiceException any problem creating subdirectories
   * @return true if directories created.
   */
  private boolean init() throws ServiceException {
    LOG.info("Initializing WEPP Service...\n");
    sessionWorkDir = workspace().getDir().toString();

    File file = workspace().getFile("runs");
    if (!file.exists()) {
      file.mkdir();
    }
    if (!file.exists()) {
      LOG.severe("Could not create runs directory");
    }
    file = workspace().getFile("output");
    if (!file.exists()) {
      file.mkdir();
    }

    if (!file.exists()) {
      LOG.severe("Could not create output directory");
    }

    outputName = "report.pdf";
    outputHTML = "report.html";

    return true;
  }


  /**
   * Get any parameters from service request for PDF generation.
   *
   * @throws ServiceException any problems getting JSON parameters.
   */
  private void loadRequiredParameters() throws ServiceException {
    try {
      // get run options
      if (parameter().has(WEPP_KEY_PDF_RESOURCE)) {
        pdfResourceName = parameter().getString(WEPP_KEY_PDF_RESOURCE);
      }
      if (parameter().has(WEPP_KEY_JSON_INPUTS)) {
        templateInputJson = parameter().getJSON(WEPP_KEY_JSON_INPUTS);
      }
      if (parameter().has(PDF_OUTPUT_NAME)) {
        outputName = parameter().getString(PDF_OUTPUT_NAME);
      }
      if (parameter().has(HTML_OUTPUT_NAME)) {
        outputHTML = parameter().getString(HTML_OUTPUT_NAME);
      }
      if (outputName.equals("wepp-summary.pdf")) {
        // get data for xdist             
        xdistData = getGraphData(WEPP_KEY_XDISTDATA);
        // get data for soil loss 
        soilLossData = getGraphData(WEPP_KEY_SOILLOSSDATA);
        // get data for elevation
        elevData = getGraphData(WEPP_KEY_ELEVATIONDATA);
        // get data for soil depth detach/deposit
        detachData = getGraphData(WEPP_KEY_DETACHDATA);
      }
    } catch (Exception e) {
      throw new ServiceException("WEPP error: error processing model run parameters.");
    }
  }


  /**
   * Get data from JSON for a particular graph.
   *
   * @param param type of graph
   * @return array of values
   * @throws ServiceException
   */
  private double[] getGraphData(String param) throws ServiceException {
    double[] res = null;
    try {
      JSONArray JSONArr = parameter().getJSONArray(param, null);
      res = new double[JSONArr.length()];
      for (int i = 0; i < JSONArr.length(); i++) {
        res[i] = JSONArr.getDouble(i);
      }
    } catch (Exception e) {
      throw new ServiceException("WEPP error: error processing model run parameters.");
    }
    return res;
  }


  /**
   * Use the velocity engine to take an HTML template and JSON data to produce a
   * PDF.
   *
   * @param outputPath PDF output file
   * @param htmlPath HTML output file
   * @param pdfResourceName HTML template file
   * @param templateInputJson JSON data to fill into template
   * @throws ServiceException if any
   * @throws IOException if any
   */
  private void buildReportPDF(String outputPath, String htmlPath, String pdfResourceName, JSONObject templateInputJson) throws ServiceException, IOException {

    VelocityContext injectedFields = new VelocityContext();

    final Iterator<String> keys = templateInputJson.keys();
    while (keys.hasNext()) {
      try {
        final String key = keys.next();
        injectedFields.put(key, templateInputJson.get(key));
      } catch (JSONException ex) {
        throw new RuntimeException(ex);
      }
    }
    injectedFields.put("numberTool", new NumberTool());

    File pdfFile = new File(outputPath);
    File htmlFile = new File(htmlPath);

    File templateHTMLFolder = new File(sessionWorkDir + "/htmltemplate");
    //templateHTMLFolder.mkdir();
    //new File(templateHTMLFolder.getPath() + "/Report_files/").mkdir();

    final File resourceFile = resources().getFile(pdfResourceName);
    final File resourceFileLogo = resources().getFile("profileReportTemplateLogo");
    final File resourceFileLogo328 = resources().getFile("profileReportTemplateLogo328");
    final String templateHTMLFilepath = templateHTMLFolder.getPath() + "/template.vm";
    final String pdfHTMLFilepath = templateHTMLFolder.getPath() + "/pdfresult.html";
    try {
      Files.copy(resourceFile.toPath(), Paths.get(templateHTMLFilepath), StandardCopyOption.REPLACE_EXISTING);
      Files.copy(resourceFileLogo.toPath(), Paths.get(templateHTMLFolder.getPath() + "/Report_files/image001.gif"), StandardCopyOption.REPLACE_EXISTING);
      Files.copy(resourceFileLogo328.toPath(), Paths.get(templateHTMLFolder.getPath() + "/Report_files/img9.jpg"), StandardCopyOption.REPLACE_EXISTING);
    } catch (IOException ex) {
      LOG.log(Level.SEVERE, null, ex);
    }
    VelocityEngine velocityEngine = PDFUtils.createVelocityEngine(templateHTMLFolder);
    try (FileWriter writer = new FileWriter(pdfHTMLFilepath)) {
      velocityEngine.getTemplate("template.vm").merge(injectedFields, writer);
    }
    try {
      PDFUtils.createPDFFromHTML(new File(pdfHTMLFilepath).toURI().toURL().toString(), pdfFile.getPath());
    } catch (DocumentException ex) {
      LOG.log(Level.SEVERE, null, ex);
    }
    try {
      Files.copy(new File(pdfHTMLFilepath).toPath(), htmlFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
    } catch (IOException ex) {
      LOG.log(Level.SEVERE, null, ex);
    }
  }


  /**
   * Get any parameters from request.
   *
   * @throws Exception if any
   */
  @Override
  protected void preProcess() throws Exception {
    init();
    loadRequiredParameters();
  }


  /**
   * Calls functions to make graphs and create PDF.
   *
   * @throws Exception if any
   */
  @Override
  protected void doProcess() throws Exception {
    File templateHTMLFolder = new File(sessionWorkDir,"htmltemplate");
    templateHTMLFolder.mkdir();
    new File(templateHTMLFolder.getPath(),"Report_files").mkdir();
    if (outputName.equals("wepp-summary.pdf")) {
      String soilLossImage = sessionWorkDir + "/htmltemplate/Report_files/soilLoss.jpeg";
      String elevImage = sessionWorkDir + "/htmltemplate/Report_files/elevation.jpeg";
      String depthImage = sessionWorkDir + "/htmltemplate/Report_files/depthLoss.jpeg";
      PDFGraph.generateJPEGByXY(xdistData, soilLossData, soilLossImage, "Soil Loss(ton/A/yr)");
      PDFGraph.generateJPEGByXY(xdistData, elevData, elevImage, "Elevation(ft)");
      PDFGraph.generateJPEGByXY(xdistData, detachData, depthImage, "Detachment/Deposition Depth (in/yr)");
    }
    buildReportPDF(sessionWorkDir + "/output/" + outputName, sessionWorkDir + "/output/" + outputHTML, pdfResourceName, templateInputJson);
  }


  /**
   * File names in response.
   *
   * @throws Exception if any
   */
  @Override
  protected void postProcess() throws Exception {
    LOG.info("Starting postProcess...");
    results().put(workspace().getFile("/output/" + outputName), "WEPP PDF Report Output");
    results().put(workspace().getFile("/output/" + outputHTML), "WEPP HTML Report Output");
    LOG.info("Finished with postProcess");
  }
}