Crop.java [src/java/m/wqm/nutappmgtscores] Revision: b8599413dc61e5fbe13f982678cad73e7c05a7a6 Date: Thu Aug 13 12:36:50 MDT 2015
package m.wqm.nutappmgtscores;
/**
*
* @author RUMPAL SIDHU
* @author Shaun Case
*/
import csip.utils.Dates;
import csip.utils.JSONUtils;
import java.util.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
public class Crop {
// Begin Crop Class
public static final int N_APP_RATE_SCORE = 0;
public static final int P_APP_RATE_SCORE = 1;
public static final int N_APP_TIMING_SCORE = 0;
public static final int P_APP_TIMING_SCORE = 1;
private int mgtCropId;
private String cropPlantDate;
private Date plantDate;
private double cropYield;
private String cropYieldUnits;
private ArrayList<Crop.NutrientApplication> nutrientApplicationList;
private m.wqm.nutappmgtscores.Result calc_result;
private Statement statement;
private String cropType = "";
private String error_msg = "";
private Boolean multipleNutrientApplication = false;
private int n_app_timing_score = 100;
private int p_app_timing_score = 100;
private int app_method_score = 0;
private int[] appRateScore = new int[2];
private int[] appNutrientTimingScore = new int[2];
private String pSoilTestResult = "None";
public Crop(int mgtCropId,String cropPlantDate, double cropYield,
String cropYieldUnits, JSONArray applicationList, String pSoilTestResult, Statement statement){
this.mgtCropId = mgtCropId;
this.cropPlantDate = cropPlantDate;
this.cropYield = cropYield;
this.cropYieldUnits = cropYieldUnits;
this.statement = statement;
this.pSoilTestResult = pSoilTestResult;
nutrientApplicationList = new ArrayList<>();
try{
plantDate = getCropPlantDate();
for (int j = 0; j < applicationList.length(); j++) {
Map<String, JSONObject> application = JSONUtils.preprocess(applicationList.getJSONArray(j));
nutrientApplicationList.add(new Crop.NutrientApplication(JSONUtils.getStringParam(application, "nutrient_application_date", "err"), JSONUtils.getStringParam(application, "incorporated", ""), JSONUtils.getJSONArrayParam(application, "application")));
}
}
catch( Exception ex){
error_msg += "Cannot process list of applied nutrients. " + ex.getMessage();
}
multipleNutrientApplication = (nutrientApplicationList.size() > 1);
this.appRateScore[N_APP_RATE_SCORE] = this.appRateScore[P_APP_RATE_SCORE] = -1;
this.appNutrientTimingScore[N_APP_TIMING_SCORE] = this.appNutrientTimingScore[P_APP_TIMING_SCORE] = -1;
}
//Get methods
public int getMgtCropId() {
return this.mgtCropId;
}
public String getErrorMsg(){
for ( Crop.NutrientApplication nutrientApplied: this.nutrientApplicationList ){
error_msg += nutrientApplied.getErrorMsg();
}
return error_msg;
}
public String getCropType(){
if ( (cropType.isEmpty()) && (error_msg.isEmpty()) ){
ResultSet resultSet;
String query = "SELECT wqm_crop_type FROM wqm_crops WHERE wqm_crop_id=" + mgtCropId + " AND wqm_crop_units='" + cropYieldUnits + "';";
try{
resultSet = statement.executeQuery(query);
if (!resultSet.first()){
error_msg += "That crop, " + mgtCropId + ", and crop yield units, "+ cropYieldUnits + ", was not found in the database";
}
else{
cropType = resultSet.getString("wqm_crop_type");
}
}
catch( SQLException ex ) {
error_msg += ex.getMessage();
}
}
return cropType;
}
public final Date getCropPlantDate() throws Exception {
String[] parse = this.cropPlantDate.split("-");
Calendar date = new GregorianCalendar(Integer.parseInt(parse[0]),
Integer.parseInt(parse[1]) - 1, Integer.parseInt(parse[2]));
return date.getTime();
}
public double getCropYield() {
return this.cropYield;
}
public String getCropYieldUnits() {
return this.cropYieldUnits;
}
public ArrayList getNutrientApplicationList() {
return this.nutrientApplicationList;
}
public int[] getNutrientApplicationRateScores(){
if ( ( appRateScore[N_APP_RATE_SCORE] <0 ) || ( appRateScore[P_APP_RATE_SCORE] <0 ) ) {
try{
if ( nutrientApplicationList.isEmpty() ){
ResultSet resultSet;
appRateScore[N_APP_RATE_SCORE] = appRateScore[P_APP_RATE_SCORE] = 0;
String queryNitrogen = "SELECT app_mgt_score FROM wqm_nutrient_application_mgt_scores WHERE nutrient='Nitrogen' AND app_mgt_kind='Rate' AND app_mgt_factor='none';";
String queryPhosporous = "SELECT app_mgt_score FROM wqm_nutrient_application_mgt_scores WHERE nutrient='Phosphorus' AND app_mgt_kind='Rate' AND app_mgt_factor='none' AND soil_test_result='" + pSoilTestResult + "';";
resultSet = statement.executeQuery(queryNitrogen);
if ( resultSet.first() ) {
appRateScore[N_APP_RATE_SCORE] = resultSet.getInt("app_mgt_score");
}
resultSet = statement.executeQuery(queryPhosporous);
if ( resultSet.first() ) {
appRateScore[P_APP_RATE_SCORE] = resultSet.getInt("app_mgt_score");
}
}
else{
ResultSet resultSet;
String query;
double nrate = 0.0;
double prate = 0.0;
double wqm_crop_pct_dmat = 0.0;
double wqm_pct_nitrogen = 0.0;
double wqm_pct_phosphorus = 0.0;
double wqm_crop_yield = 0.0;
int ncrop_app_rate_score = 0;
int pcrop_app_rate_score = 0;
for (Crop.NutrientApplication nutrient : nutrientApplicationList) {
ArrayList<Crop.NutrientApplication.Nutrient> nutrientAppliedList = nutrient.getNutrientList();
for (Crop.NutrientApplication.Nutrient nApplied : nutrientAppliedList) {
switch(nApplied.getNutrientApplied()){ //Note: This switch requires JDK 1.7 or above
case "Nitrogen":
nrate += nApplied.getApplicationRate();
break;
case "Phosphorus":
prate += nApplied.getApplicationRate();
break;
}
}
}
query = "SELECT wqm_crop_pct_dmat, wqm_pct_nitrogen, wqm_pct_phosphorus, wqm_crop_yield FROM wqm_crops WHERE wqm_crop_id=" + mgtCropId + ";";
resultSet = statement.executeQuery(query);
if (resultSet.first()) {
wqm_crop_pct_dmat = resultSet.getDouble("wqm_crop_pct_dmat");
wqm_pct_nitrogen = resultSet.getDouble("wqm_pct_nitrogen");
wqm_pct_phosphorus = resultSet.getDouble("wqm_pct_phosphorus");
wqm_crop_yield = resultSet.getDouble("wqm_crop_yield");
// Added wqm_crop_yield to the calc per bug #462839
double n_growout = cropYield * wqm_crop_yield * wqm_crop_pct_dmat * wqm_pct_nitrogen;
double p_growout = cropYield * wqm_crop_yield * wqm_crop_pct_dmat * wqm_pct_phosphorus;
double n_remove_ratio = nrate / n_growout;
double p_remove_ratio = prate / p_growout;
query = "SELECT app_mgt_score FROM wqm_nutrient_application_mgt_scores WHERE nutrient='" + "Nitrogen" + "' AND app_mgt_kind='" + "Rate" + "' AND app_mgt_factor=";
switch( cropType ){
case "Small Grain": //Note the case conflicts here...
query += "'small grain'";
break;
default:
query += "'none'";
}
query += " AND remove_ratio_1 <= " + n_remove_ratio + "AND remove_ratio_2 > " + n_remove_ratio + ";";
resultSet = statement.executeQuery(query);
if (resultSet.first()) {
ncrop_app_rate_score = resultSet.getInt("app_mgt_score");
}
else{
appRateScore[N_APP_RATE_SCORE] = appRateScore[P_APP_RATE_SCORE] = 0;
}
pcrop_app_rate_score = -1;
//#Compute P application management rate scores based on removal ratio and soil test result
switch (pSoilTestResult){
case "High":
if (p_remove_ratio >= 1.2) {
pcrop_app_rate_score = 0;
}
break;
case "Medium": case "Low":
if (p_remove_ratio >= 1.6) {
pcrop_app_rate_score = 0;
}
break;
case "None":
if (p_remove_ratio >= 1.2) {
pcrop_app_rate_score = 0;
}
break;
}
if ( pcrop_app_rate_score == -1 ){
query = "SELECT app_mgt_score FROM wqm_nutrient_application_mgt_scores WHERE nutrient='Phosphorus' AND app_mgt_kind='Rate' AND app_mgt_factor='app' AND soil_test_result='" + pSoilTestResult +"' AND remove_ratio_1 <= " + p_remove_ratio + " AND remove_ratio_2 > " + p_remove_ratio + ";";
resultSet = statement.executeQuery(query);
if (resultSet.first()) {
pcrop_app_rate_score = resultSet.getInt("app_mgt_score");
}
}
if (pcrop_app_rate_score != -1){
//#Update N and P application management rate scores for the AoA
appRateScore[N_APP_RATE_SCORE] = ncrop_app_rate_score;
appRateScore[P_APP_RATE_SCORE] = pcrop_app_rate_score;
}
else{//We have a problem...no validity in any following computations if we proceed since the last query failed to find any data. What would you like to do?
appRateScore[N_APP_RATE_SCORE] = appRateScore[P_APP_RATE_SCORE] = 0;
}
}
else{ //We have a problem...no validity in any following computations if we proceed. What would you like to do?
appRateScore[N_APP_RATE_SCORE] = appRateScore[P_APP_RATE_SCORE] = 0;
}
}
}
catch (SQLException ex) {
error_msg += ex.getMessage();
}
}
return this.appRateScore;
}
public int[] getNutrientApplicationTimingScores(){
if ( ( appNutrientTimingScore[N_APP_TIMING_SCORE] <0 ) || ( appNutrientTimingScore[P_APP_TIMING_SCORE] <0 ) ) {
int tempNScore = 100;
int tempPScore = 100;
for ( Crop.NutrientApplication nutrientApplication : this.nutrientApplicationList ){
// Call each application compare its results to the last and adjust main score if necessary.
int[] tempAppTimingScores = nutrientApplication.getNutrientTimingScores( this.multipleNutrientApplication );
if ( tempAppTimingScores[N_APP_TIMING_SCORE] < tempNScore ) {
tempNScore = tempAppTimingScores[N_APP_TIMING_SCORE];
}
if ( tempAppTimingScores[P_APP_TIMING_SCORE] < tempPScore ) {
tempPScore = tempAppTimingScores[P_APP_TIMING_SCORE];
}
}
appNutrientTimingScore[P_APP_TIMING_SCORE] = tempPScore;
appNutrientTimingScore[N_APP_TIMING_SCORE] = tempNScore;
}
return this.appNutrientTimingScore;
}
public Boolean allNutrientsIncorporated(){
Boolean ret_val = true;
for( Crop.NutrientApplication nutrientApplication : this.nutrientApplicationList ){
if ( !nutrientApplication.allNutrientsIncorporated() ){
ret_val = false;
break;
}
}
return ret_val;
}
public String processCropData(){
String ret_val = "";
return ret_val;
}
public Boolean validate(){
for( Crop.NutrientApplication tNutrientApplication : nutrientApplicationList ) {
if (!tNutrientApplication.validate()) {
error_msg += "; " + tNutrientApplication.getErrorMsg();
}
}
return ( error_msg.isEmpty() );
}
// Inner Classes
class NutrientApplication {
class Nutrient {
private String error_msg = "";
private final String nutrientApplied;
private final double applicationRate;
Nutrient(String nutrientApplied, double applicationRate) {
this.nutrientApplied = nutrientApplied;
this.applicationRate = applicationRate;
if ( !nutrientApplied.equals("Nitrogen") && !nutrientApplied.equals("Phosphorus") ) {
this.error_msg += "Invalid input data. Bad nutrient name";
}
}
//Get Methods
public String getNutrientApplied() {
return this.nutrientApplied;
}
public double getApplicationRate() {
return this.applicationRate;
}
public String getErrorMsg(){
return this.error_msg;
}
public Boolean validate(){
return (this.error_msg.isEmpty());
}
}
private String error_msg = "";
private String applicationDate;
private boolean incorporated;
private ArrayList<Crop.NutrientApplication.Nutrient> nutrientList;
private int appMethodScore = 0;
private int[] nutrientTimingScores = new int[2];
NutrientApplication(String applicationDate, String incorporated, JSONArray applications) {
this.applicationDate = applicationDate;
nutrientTimingScores[N_APP_TIMING_SCORE] = nutrientTimingScores[P_APP_TIMING_SCORE] = -1;
if (( !"true".equalsIgnoreCase(incorporated) ) && ( !"false".equalsIgnoreCase(incorporated) )) {
this.error_msg += "Invalid incorporation value for nutrient application " + applicationDate;
} else {
this.incorporated = Boolean.parseBoolean(incorporated);
nutrientList = new ArrayList<>();
try {
for (int k = 0; k < applications.length(); k++) {
Map<String, JSONObject> nutrient = JSONUtils.preprocess(applications.getJSONArray(k));
nutrientList.add(new Crop.NutrientApplication.Nutrient(JSONUtils.getStringParam(nutrient, "nutrient_applied", "err"), JSONUtils.getDoubleParam(nutrient, "application_rate", 0)));
}
} catch (JSONException ex) {
this.error_msg += "Cannot process applied nutrient list. " + ex.getMessage();
}
}
}
//Get Methods
public Date getApplicationDate() throws Exception {
String[] parse = this.applicationDate.split("-");
//Some say should set to use UTC first...do we wanna do that?
Calendar date = new GregorianCalendar(Integer.parseInt(parse[0]),
Integer.parseInt(parse[1]) - 1, Integer.parseInt(parse[2]));
return date.getTime();
}
public boolean isIncorporated() {
return this.incorporated;
}
public ArrayList getNutrientList() {
return this.nutrientList;
}
public String getErrorMsg(){
for (Crop.NutrientApplication.Nutrient nutrient : this.nutrientList ){
this.error_msg += nutrient.getErrorMsg();
}
return this.error_msg;
}
public Boolean validate() {
for (Crop.NutrientApplication.Nutrient nutrient : nutrientList) {
if (!nutrient.validate()) {
this.error_msg += "; " + nutrient.getErrorMsg();
}
}
return (this.error_msg.isEmpty());
}
public int[] getNutrientTimingScores(Boolean split) {
if (( nutrientTimingScores[N_APP_TIMING_SCORE] < 0 ) || ( nutrientTimingScores[P_APP_TIMING_SCORE] < 0 )) {
int tempNScore = 100;
int tempPScore = 100;
ResultSet resultSet;
for (Crop.NutrientApplication.Nutrient nutrient : nutrientList) {
int app_timing_score;
try {
long app_day_diff = Dates.diffInMillis( getCropPlantDate(), getApplicationDate() );
app_day_diff = TimeUnit.MILLISECONDS.toDays(app_day_diff);
String query = "SELECT app_mgt_score FROM wqm_nutrient_application_mgt_scores WHERE nutrient='";
query += nutrient.getNutrientApplied() + "' ";
query += "AND app_mgt_kind='Timing' AND app_mgt_factor='" + (split? "split":"nosplit") + "'AND days_fr_plant_1 <= " + app_day_diff + "AND days_fr_plant_2 > " + app_day_diff + ";";
resultSet = statement.executeQuery(query);
if ( !resultSet.first() ) {
app_timing_score = -1;
}
else {
app_timing_score = resultSet.getInt("app_mgt_score");
}
switch (nutrient.getNutrientApplied()) {
case "Nitrogen":
if (app_timing_score < tempNScore) {
tempNScore = app_timing_score;
}
break;
case "Phosphorus":
if (app_timing_score < tempPScore) {
tempPScore = app_timing_score;
}
break;
default:
this.error_msg += "Invalid nutrient name specified";
}
}catch (Exception ex){
this.error_msg += "Cannot calculate nutrient application timing scores: " + ex.getMessage();
}
if ( this.error_msg.isEmpty() ){
nutrientTimingScores[N_APP_TIMING_SCORE] = tempNScore;
nutrientTimingScores[P_APP_TIMING_SCORE] = tempPScore;
}
}
}
return nutrientTimingScores;
}
public Boolean allNutrientsIncorporated(){
return this.incorporated;
}
}
}