/** * Created by Robert Pastel on 11/12/2016. */ /** * Grab gets the http-builder jar from the maven site * * If you get the error * * error groovyc cannot @Grab without Ivy * * then * 1. Download the binary for Ivy at * http://ant.apache.org/ivy/ * 2. Unzip and extract the jar * 3. Put it in a nearby directory * 4. Add it as a module to the project by * i. File -> Project structure -> Modules -> Dependencies * ii. Add by clicking on the "+" on the right, select JARs * iii. Navigate to where you put the Ivy jar * Reference * https://intellij-support.jetbrains.com/hc/en-us/community/posts/206913575-Installing-Ivy-plugin- */ @Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7') import groovyx.net.http.HTTPBuilder import static groovyx.net.http.ContentType.* import static groovyx.net.http.Method.* /** * Make the post request * * Note that the HTTPBulider should reference only the site * and the path specifies the path to script. * This avoids 403 (Forbidden) * * Documentation for HTTPBuilder is at * https://github.com/jgritman/httpbuilder/wiki */ def http = new HTTPBuilder( 'https://forest.moscowfsl.wsu.edu' ) // You can find the post variables and value, by submitting a request from the website // then inspect -> Networks -> click wd.pl -> Form Data. // You can even copy and paste from the inspector to your script. def postBody = [ me:'' , units:'ft', description:'' , climyears:'10', Climate:'../climates/al010831', achtung:'WEPP run', SoilType:'clay', UpSlopeType:'OldForest', ofe1_top_slope:'0', ofe1_length:'50', ofe1_pcover:'100', ofe1_rock:'20', ofe1_mid_slope:'30', LowSlopeType:'OldForest', ofe2_top_slope:'30', ofe2_length:'50', ofe2_pcover:'100', ofe2_rock:'20', ofe2_bot_slope:'5', climate_name:'BIRMINGHAM WB AP AL', Units:'m', actionw:'Run WEPP' ] // Make the post request and get back the GPath for the html. // Note the path to the script. It is necessary to split up the URI this way. def html = http.post(path: '/cgi-bin/fswepp/wd/wd.pl', body: postBody) // Now get the table of interest results using GPATH // See http://groovy-lang.org/processing-xml.html#_gpath def erodeTable = html.BODY.FONT.BLOCKQUOTE.CENTER.P.TABLE // Note that sometimes the GPath hierarchy is broken, // but you can always make the depth first searches /** * Map of Maps * * We want a map like this: * analysis[period][variable] -> value * * also want to make a Map from variables to untis * * Note that this should work for any value of "years to simulate". */ // create the analysis Map def analysis = [:] // Gather the keys and make the units map def i = 0 // counts table rows, so we can do something special for the first table row def periods = [] def variables = [] def units = [:] // Note that erodeTable is a GPathResult, so we can search it. erodeTable."**".findAll{it.name() == "TR"}.each{tr -> // The first table row lists the variables with their untis if ( i == 0){ for(j = 0; j < tr.TH.size(); j++){ // We want to sikp the first header if(j > 0){ String variable_unit = tr.TH[j] // Some regular expression to extract variable names and units // See http://groovy-lang.org/operators.html#_regular_expression_operators // and http://www.regular-expressions.info/ // Variable names will have only alphabets and units are inside parenthesis def m = variable_unit =~ /([A-Za-z]+)\((.+)\)/ if (m){ // Note that the capture groups are Strings variables[j-1] = m[0][1] units.put(variables[j-1], m[0][2]) } } } } // Table rows greater than 0 contains a periods else if(i > 0){ // We will want to use the table header as a key to a map, // so we MUST use toString method so that the hashing works properly. // Java hashes Objects different from Strings periods[i-1] = tr.TH.toString() } i++ } //println periods //println variables //println units // Now construct the analysis table from the bottom up i = 0 // for tracking the periods and table row erodeTable."**".findAll{it.name() == "TR"}.each{ tr -> // construct the period_variable map if (i > 0) { // skip the first row because it is a header row def j = 0; // for tracking the variables def period_variable = [:] tr."**".findAll { it.name() == "TD" }.each { td -> period_variable.put(variables[j], td) j++ } analysis.put(periods[i-1], period_variable) // use i-1 because we skip the first table row } i++ } // now we can access the analysis table like this String period = "Average" String variable = "Runoff" println "The ${period} ${variable} is ${analysis[period][variable]} ${units[variable]}" /** * Find the average annual sediment leaving profile in units t/ha * It is in function showextendedoutput() that is evoked by "WEPP results" link * * Note that there is only one occurrence of "t/ha" in the entire response. * */ def scriptNode = html.HEAD.SCRIPT // extract the SCRIPT String script = scriptNode.toString() // Make sure it is a string // create the variables to save captures from matches def leavingLine def leavingValues = [] def leavingUnits = ["t/ha", "ha"] // Use regular expressions to get line, Note that quotes delineate the line //def m = script =~ /".+t\/ha.+"/ // this works, but the slash must be escaped // We can also use groovy strings and then the / is escaped for us def m = script =~ /".+${leavingUnits[0]}.+"/ if(m){ // Found it, so clean up the leavingLine. Note the use of regular expressions in the replaceAll leavingLine = m[0] leavingLine = leavingLine.replaceAll(/ +/, ' ') // remove extra spaces leavingLine = leavingLine.replaceAll(/" /,'') // remove leading quote leavingLine = leavingLine.replaceAll(/"/,'') // remove remaining quotes // Now extract the values m = leavingLine =~ /([\d\.]+) ${leavingUnits[0]} \([\w\s]+([\d\.]+) ${leavingUnits[1]}/ if(m){ leavingValues[0] = m[0][1] leavingValues[1] = m[0][2] } } // Now we can use our regular expression captures like this //println leavingLine println "The average annual sediment leaving is ${leavingValues[0]} ${leavingUnits[0]}"