@@ -3,35 +3,270 @@ |
/** |
* |
* @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.Connection; |
+import java.sql.DriverManager; |
+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.JSONObject; |
|
public class Crop { |
|
+ // Inner Classes |
+ class NutrientApplication { |
+ |
+ class Nutrient { |
+ private String error_msg = ""; |
+ private String nutrientApplied; |
+ private double applicationRate; |
+ |
+ public 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]; |
+ |
+ public 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(Exception 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; |
+ } |
+ } |
+ |
+ |
+ // 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<Nutrient> nutrientApplicationList; |
- |
+ 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, ArrayList<Nutrient> nutrientApplicationList) { |
+ String cropYieldUnits, JSONArray applicationList, String pSoilTestResult, Statement statement){ |
this.mgtCropId = mgtCropId; |
this.cropPlantDate = cropPlantDate; |
this.cropYield = cropYield; |
this.cropYieldUnits = cropYieldUnits; |
- this.nutrientApplicationList = nutrientApplicationList; |
+ 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; |
} |
|
- //Getter methods |
+ //Get methods |
public int getMgtCropId() { |
return this.mgtCropId; |
} |
|
- public Date getCropPlantDate() throws Exception { |
+ 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( Exception 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])); |
@@ -49,4 +284,186 @@ |
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 = nrate + nApplied.getApplicationRate(); |
+ break; |
+ case "Phosphorus": |
+ prate = 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 (Exception 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() ); |
+ } |
} |