Management.java [src/nodes] Revision: default 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 nodes;
import static csip.ModelDataServiceConstants.ERROR;
import static csip.ModelDataServiceConstants.KEY_METAINFO;
import static csip.ModelDataServiceConstants.KEY_PARAMETER;
import static csip.ModelDataServiceConstants.KEY_RESULT;
import static csip.ModelDataServiceConstants.KEY_STATUS;
import static csip.ModelDataServiceConstants.KEY_VALUE;
import static csip.ModelDataServiceConstants.VALUE;
import csip.api.client.ModelDataServiceCall;
import csip.api.server.ServiceException;
import csip.utils.Client;
import csip.utils.Parallel;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.time.LocalDate;
import static java.time.temporal.ChronoUnit.DAYS;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.xml.sax.SAXException;
import translators.Translator.FORMAT;
import static utils.Constants.*;
import utils.TranslatorException;
import utils.Urls;
import utils.Util;
/**
*
* @author Brad
*/
public abstract class Management {
protected String name;
protected int rotationYears;
protected final List<Event> eventData;
protected final Urls urls;
public Management(Urls urls) {
eventData = new CopyOnWriteArrayList<>();
this.urls = urls;
}
public int getRotationYears() {
return rotationYears;
}
public List<Event> getManagementData() {
return eventData;
}
public void readJSONData(JSONObject data, FORMAT type) throws JSONException, TranslatorException {
JSONArray rotations;
JSONObject managementTemplate;
JSONArray events;
JSONObject jdatum;
Event datum;
int yearoffset = 0;
LocalDate latest = null;
rotationYears = data.getInt(DURATION);
rotations = data.getJSONArray(MANAGEMENTS);
for (int i = 0; i < rotations.length(); i++) {
managementTemplate = rotations.getJSONObject(i);
events = managementTemplate.getJSONArray(EVENTS);
for (int j = 0; j < events.length(); j++) {
jdatum = events.getJSONObject(j);
datum = new Event();
datum.readJSONData(jdatum, type);
eventData.add(datum);
if (eventData.size() == 1) {
yearoffset = eventData.get(0).getDate().getYear() - 1;
}
datum.setDate(datum.getDate().minusYears(yearoffset));
// This event should be before the last event
if (eventData.size() > 2 && datum.getDate().isBefore(latest)) {
throw new TranslatorException("Management Error: management " + i + " has an event out of order at position " + (j));
}
latest = datum.getDate();
}
}
}
public void fillLMODData(FORMAT type) throws SQLException, XMLStreamException, ParserConfigurationException, SAXException, IOException, FileNotFoundException, TranslatorException, JSONException, Exception {
Parallel.run(
() -> {
fillLMODOperationData(type);
},
() -> {
fillLMODCropData(type);
},
() -> {
fillLMODResidueData(type);
}
);
}
protected void fillLMODOperationData(FORMAT type) throws Exception {
JSONArray operations;
JSONObject joperation = null;
Map<String, JSONObject> fileData;
JSONArray jvalues = new JSONArray();
for (Event datum : eventData) {
if (type == FORMAT.KEY) {
jvalues.put(datum.getOperation().getName());
} else {
jvalues.put(datum.getOperation().getID());
}
}
ModelDataServiceCall r = new ModelDataServiceCall()
.put(NATIVE_FORMATS, true)
.put(LIMIT, "all")
.put(((type == FORMAT.KEY) ? NAME : ID), jvalues)
.withDefaultLogger()
.url(urls.getOperations())
.call();
if (r.serviceFinished()) {
operations = r.getJSONObject("crlmod").getJSONArray(OPERATIONS);
fileData = Util.mapOpResultsByName(operations, type);
for (Event datum : eventData) {
if (type == FORMAT.KEY) {
joperation = fileData.get(datum.getOperation().getName());
} else {
joperation = fileData.get(datum.getOperation().getID());
}
if (joperation == null) {
throw new ServiceException("Operation: " + datum.getOperation().getID() + " " + datum.getOperation().getName() + " is not in CR_LMOD");
}
// If we only had the name then we need to fill in the key
if (type == FORMAT.KEY) {
datum.getOperation().setID(joperation.getString(ID));
}
datum.getOperation().addProperty("crop", datum.getCrop());
datum.getOperation().parseLMODData(joperation);
}
} else {
throw new ServiceException("op service error: " + r.getError());
}
}
protected void fillLMODCropData(FORMAT type) throws Exception {
JSONArray crops;
Crop crop;
JSONObject jcrop = null;
Map<String, JSONObject> fileData;
JSONArray jvalues = new JSONArray();
for (Event datum : eventData) {
crop = datum.getCrop();
if (crop != null) {
if (type == FORMAT.KEY) {
jvalues.put(datum.getCrop().getName());
} else {
jvalues.put(datum.getCrop().getID());
}
}
}
ModelDataServiceCall r = new ModelDataServiceCall()
.put(NATIVE_FORMATS, true)
.put(LIMIT, "all")
.put(((type == FORMAT.KEY) ? NAME : ID), jvalues)
.withDefaultLogger()
.url(urls.getCrops())
.call();
if (r.serviceFinished()) {
crops = r.getJSONObject("crlmod").getJSONArray(CROPS);
fileData = Util.mapCropResultsByName(crops, type);
for (Event datum : eventData) {
if (datum.getCrop() != null) {
if (type == FORMAT.KEY) {
jcrop = fileData.get(datum.getCrop().getName());
} else {
jcrop = fileData.get(datum.getCrop().getID());
}
if (jcrop == null) {
throw new ServiceException("Crop: " + datum.getCrop().getName() + " is not in CR_LMOD");
}
// If we only had the name then we need to fill in the key
if (type == FORMAT.KEY) {
datum.getCrop().setID(jcrop.getString(ID));
}
datum.getCrop().parseLMODData(jcrop);
}
}
} else {
throw new ServiceException("op service error: " + r.getError());
}
}
protected void fillLMODResidueData(FORMAT type) throws Exception {
Residue residue;
JSONArray residues;
JSONObject jresidue = null;
Map<String, JSONObject> fileData;
JSONArray jvalues = new JSONArray();
for (Event datum : eventData) {
residue = datum.getResidue();
if (residue != null) {
if (type == FORMAT.KEY) {
jvalues.put(datum.getResidue().getName());
} else {
jvalues.put(datum.getResidue().getID());
}
}
}
ModelDataServiceCall r = new ModelDataServiceCall()
.put(NATIVE_FORMATS, true)
.put(LIMIT, "all")
.put(((type == FORMAT.KEY) ? NAME : ID), jvalues)
.withDefaultLogger()
.url(urls.getResidue())
.call();
if (r.serviceFinished()) {
residues = r.getJSONObject("lmod").getJSONArray(RESIDUES);
fileData = Util.mapResidueResultsByName(residues, type);
for (Event datum : eventData) {
if (datum.getResidue() != null) {
if (type == FORMAT.KEY) {
jresidue = fileData.get(datum.getResidue().getName());
} else {
jresidue = fileData.get(datum.getResidue().getID());
}
if (jresidue == null) {
throw new ServiceException("Residue: " + datum.getResidue().getName() + " is not in CR_LMOD");
}
// If we only had the name then we need to fill in the key
if (type == FORMAT.KEY) {
datum.getResidue().setID(jresidue.getString(ID));
}
datum.getResidue().parseLMODData(jresidue);
}
}
} else {
throw new ServiceException("op service error: " + r.getError());
}
}
public void rotateEvents() {
TreeSet<Event> eventSet = new TreeSet();
TreeSet<Event> newEventSet = new TreeSet();
int totalCalendarYears = 0;
int firstYear, lastYear;
double totalYearDiff;
int lastOperationDayOfYear;
for (int i = 0; i < eventData.size(); i++) {
eventSet.add(eventData.get(i));
}
Event lastEvent = eventSet.last();
Event firstEvent = eventSet.first();
firstYear = firstEvent.getDate().getYear();
lastYear = lastEvent.getDate().getYear();
lastOperationDayOfYear = lastEvent.getDate().getDayOfYear();
totalCalendarYears = lastYear - firstYear + 1;
long daysBetween = DAYS.between(firstEvent.getDate(), lastEvent.getDate()) + 1;
totalYearDiff = daysBetween / 365.0;
// Can the rotation be squished into fewer years than the years provided?
if (Math.ceil(totalYearDiff) < totalCalendarYears) {
if (firstEvent.getDate().getDayOfYear() > lastEvent.getDate().getDayOfYear()) {
lastYear = (int) Math.ceil(totalYearDiff);
// Need to reorder the list and reset the duration...
totalCalendarYears = lastYear;
Iterator it = eventSet.iterator();
//Move last operation up to firstYear.
int count = 0;
int formerYear = -1;
while (it.hasNext()) {
Event tEvent = (Event) it.next();
// Make sure to increment the counter to reset year values.
if ((formerYear != tEvent.getDate().getYear()) && (count < totalCalendarYears) && (firstYear < tEvent.getDate().getYear())) {
count++;
formerYear = tEvent.getDate().getYear();
}
if (firstYear == tEvent.getDate().getYear()) {
if (tEvent.getDate().getDayOfYear() >= lastOperationDayOfYear) {
tEvent.setDate(tEvent.getDate().withYear(lastYear));
}
} else {
tEvent.setDate(tEvent.getDate().withYear(count));
}
newEventSet.add(tEvent);
}
eventSet.clear();
eventSet.addAll(newEventSet);
}// Else...don't need to do anything??
}
eventData.clear();
eventData.addAll(eventSet);
rotationYears = totalCalendarYears;
}
}