de.bund.bfr.knime.fsklab.deprecatednodes/src-prerakip/de/bund/bfr/knime/pmm/fskx/reader/FskxReaderNodeModel.java
/*******************************************************************************
* Copyright (c) 2015 Federal Institute for Risk Assessment (BfR), Germany
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If
* not, see <http://www.gnu.org/licenses/>.
*
* Contributors: Department Biological Safety - BfR
*******************************************************************************/
package de.bund.bfr.knime.pmm.fskx.reader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.xml.stream.XMLStreamException;
import org.apache.commons.io.IOUtils;
import org.jdom2.JDOMException;
import org.knime.core.data.DataTableSpec;
import org.knime.core.node.BufferedDataContainer;
import org.knime.core.node.BufferedDataTable;
import org.knime.core.node.CanceledExecutionException;
import org.knime.core.node.ExecutionContext;
import org.knime.core.node.ExecutionMonitor;
import org.knime.core.node.InvalidSettingsException;
import org.knime.core.node.NodeLogger;
import org.knime.core.node.NodeModel;
import org.knime.core.node.NodeSettingsRO;
import org.knime.core.node.NodeSettingsWO;
import org.knime.core.node.defaultnodesettings.SettingsModelString;
import org.knime.core.node.port.PortObject;
import org.knime.core.node.port.PortObjectSpec;
import org.knime.core.node.port.PortType;
import org.knime.core.util.FileUtil;
import org.knime.ext.r.node.local.port.RPortObject;
import org.knime.ext.r.node.local.port.RPortObjectSpec;
import org.rosuda.REngine.REXPMismatchException;
import org.sbml.jsbml.AssignmentRule;
import org.sbml.jsbml.Model;
import org.sbml.jsbml.Parameter;
import org.sbml.jsbml.SBMLDocument;
import org.sbml.jsbml.UnitDefinition;
import org.sbml.jsbml.util.filters.Filter;
import org.sbml.jsbml.xml.stax.SBMLReader;
import de.bund.bfr.fskml.RMetaDataNode;
import de.bund.bfr.knime.fsklab.nodes.Variable;
import de.bund.bfr.knime.fsklab.nodes.Variable.DataType;
import de.bund.bfr.knime.fsklab.nodes.port.FskPortObject;
import de.bund.bfr.knime.fsklab.nodes.port.FskPortObjectSpec;
import de.bund.bfr.knime.fsklab.r.client.IRController.RException;
import de.bund.bfr.knime.fsklab.r.client.LibRegistry;
import de.bund.bfr.knime.fsklab.r.client.LibRegistry.NoInternetException;
import de.bund.bfr.knime.fsklab.r.client.RController;
import de.bund.bfr.knime.pmm.fskx.FskMetaData;
import de.bund.bfr.knime.pmm.fskx.FskMetaDataTuple;
import de.bund.bfr.knime.pmm.fskx.URIS;
import de.bund.bfr.pmfml.sbml.Limits;
import de.bund.bfr.pmfml.sbml.LimitsConstraint;
import de.bund.bfr.pmfml.sbml.Metadata;
import de.bund.bfr.pmfml.sbml.MetadataAnnotation;
import de.bund.bfr.pmfml.sbml.ModelRule;
import de.bund.bfr.pmfml.sbml.PMFCompartment;
import de.bund.bfr.pmfml.sbml.PMFSpecies;
import de.bund.bfr.pmfml.sbml.SBMLFactory;
import de.unirostock.sems.cbarchive.ArchiveEntry;
import de.unirostock.sems.cbarchive.CombineArchive;
import de.unirostock.sems.cbarchive.CombineArchiveException;
public class FskxReaderNodeModel extends NodeModel {
private static final NodeLogger LOGGER = NodeLogger.getLogger(FskxReaderNodeModel.class);
// configuration keys
public static final String CFGKEY_FILE = "filename";
// defaults for persistent state
private static final String DEFAULT_FILE = "c:/temp/foo.numl";
// defaults for persistent state
private final SettingsModelString filename = new SettingsModelString(CFGKEY_FILE, DEFAULT_FILE);
private static final PortType[] inPortTypes = {};
private static final PortType[] outPortTypes =
{FskPortObject.TYPE, RPortObject.TYPE, BufferedDataTable.TYPE};
// Specs
private static final FskPortObjectSpec fskSpec = FskPortObjectSpec.INSTANCE;
private static final RPortObjectSpec rSpec = RPortObjectSpec.INSTANCE;
private static final DataTableSpec metadataSpec = FskMetaDataTuple.createSpec();
public FskxReaderNodeModel() {
super(inPortTypes, outPortTypes);
}
/**
* {@inheritDoc}
*
* @throws RException
* @throws REXPMismatchException
* @throws InvalidPathException
* @throws IOException
* @throws NoInternetException
*/
@Override
protected PortObject[] execute(final PortObject[] inData, final ExecutionContext exec)
throws CombineArchiveException, REXPMismatchException, RException, InvalidPathException,
IOException, NoInternetException {
FskPortObject portObj = new FskPortObject();
File archiveFile = FileUtil.getFileFromURL(FileUtil.toURL(filename.getStringValue()));
try (CombineArchive archive = new CombineArchive(archiveFile)) {
// Gets annotation
RMetaDataNode node = new RMetaDataNode(archive.getDescriptions().get(0).getXmlDescription());
// Gets model script
if (node.getMainScript() != null) {
ArchiveEntry entry = archive.getEntry(node.getMainScript());
portObj.model = loadScriptFromEntry(entry);
}
// Gets parameters script
if (node.getParametersScript() != null) {
ArchiveEntry entry = archive.getEntry(node.getParametersScript());
portObj.param = loadScriptFromEntry(entry);
}
// Gets visualization script
if (node.getVisualizationScript() != null) {
ArchiveEntry entry = archive.getEntry(node.getVisualizationScript());
portObj.viz = loadScriptFromEntry(entry);
}
// Gets workspace file
if (node.getWorkspaceFile() != null) {
ArchiveEntry entry = archive.getEntry(node.getWorkspaceFile());
try {
portObj.workspace = FileUtil.createTempFile("workspace", ".r");
entry.extractFile(portObj.workspace);
} catch (IOException e) {
LOGGER.warn("Workspace could not be restored. Please rerun model to obtain results.");
}
}
// Gets model meta data
if (archive.getNumEntriesWithFormat(URIS.pmf) == 1) {
ArchiveEntry entry = archive.getEntriesWithFormat(URIS.pmf).get(0);
try {
File f = FileUtil.createTempFile("metaData", ".pmf");
entry.extractFile(f);
SBMLDocument doc = new SBMLReader().readSBML(f);
portObj.template = processMetadata(doc);
} catch (IOException | XMLStreamException e) {
LOGGER.error("Metadata document could not be read: " + entry.getFileName());
}
}
portObj.template.software = FskMetaData.Software.R;
{
// Set type of dependent variable
// TODO: usually the type of the depvar is numeric although it
// should be checked
portObj.template.dependentVariable.type = DataType.numeric;
/*
* TODO: FskMetaData is keeping only numeric types for independent variables so it does not
* make sense to try to obtain the type here since it will always be numeric. Once the rest
* of types are supported in FskMetaData the following code should be update to retrieve the
* types.
*/
// Set independent variables types and values
Map<String, String> vars = getVariablesFromAssignments(portObj.param);
for (Variable v : portObj.template.independentVariables) {
v.type = DataType.numeric;
v.value = vars.get(v.name);
}
}
// Gets R libraries
// Gets library names from the zip entries in the CombineArchive
List<String> libNames = archive.getEntriesWithFormat(URIS.zip).stream()
.map(entry -> entry.getFileName().split("\\_")[0]).collect(Collectors.toList());
if (!libNames.isEmpty()) {
LibRegistry libRegistry = LibRegistry.instance();
// Filters and installs missing libraries
libRegistry.install(libNames);
// Converts and return set of Paths returned from plugin to set
Set<File> libs =
libRegistry.getPaths(libNames).stream().map(Path::toFile).collect(Collectors.toSet());
portObj.libs.addAll(libs);
}
} catch (IOException | JDOMException | ParseException e) {
e.printStackTrace();
}
// Meta data port
BufferedDataContainer fsmrContainer = exec.createDataContainer(metadataSpec);
FskMetaDataTuple metaDataTuple = new FskMetaDataTuple(portObj.template);
// Validate model
try (RController controller = new RController()) {
String fullScript = portObj.param + "\n" + portObj.model;
controller.eval(fullScript, false);
} catch (RException e) {
throw new RException("Input model is not valid", e);
}
fsmrContainer.addRowToTable(metaDataTuple);
fsmrContainer.close();
// RPortObject rObj = new RPortObject(portObj.workspace);
RPortObject rObj;
if (portObj.workspace == null) {
rObj = new RPortObject(FileUtil.createTempFile("workspace", ".r"));
} else {
rObj = new RPortObject(portObj.workspace);
}
return new PortObject[] {portObj, rObj, fsmrContainer.getTable()};
}
private static String loadScriptFromEntry(final ArchiveEntry entry) throws IOException {
// Create temporary file with a random name. The name does not matter,
// since the file will be
// deleted by KNIME itself.
File f = FileUtil.createTempFile("script", ".r");
entry.extractFile(f);
// Read script from f and return script
try (FileInputStream fis = new FileInputStream(f)) {
return IOUtils.toString(fis, "UTF-8");
}
}
@Override
protected PortObjectSpec[] configure(final PortObjectSpec[] inSpecs)
throws InvalidSettingsException {
return new PortObjectSpec[] {fskSpec, rSpec, metadataSpec};
}
@Override
protected void saveSettingsTo(final NodeSettingsWO settings) {
filename.saveSettingsTo(settings);
}
@Override
protected void loadValidatedSettingsFrom(final NodeSettingsRO settings)
throws InvalidSettingsException {
filename.loadSettingsFrom(settings);
}
@Override
protected void validateSettings(final NodeSettingsRO settings) throws InvalidSettingsException {
filename.validateSettings(settings);
}
@Override
protected void loadInternals(File nodeInternDir, ExecutionMonitor exec)
throws IOException, CanceledExecutionException {
// nothing
}
@Override
protected void saveInternals(File nodeInternDir, ExecutionMonitor exec)
throws IOException, CanceledExecutionException {
// nothing
}
@Override
protected void reset() {
// does nothing
}
// --- utility ---
// TODO: take functionality out of FSMRUtils processPrevalenceModel
private static FskMetaData processMetadata(final SBMLDocument doc) {
FskMetaData template = new FskMetaData();
Model model = doc.getModel();
AssignmentRule rule = (AssignmentRule) model.getRule(0);
// caches limits
List<Limits> limits = model.getListOfConstraints().stream().map(LimitsConstraint::new)
.map(LimitsConstraint::getLimits).collect(Collectors.toList());
template.modelId = model.getId();
template.modelName = model.getName();
// organism data
{
PMFSpecies species = SBMLFactory.createPMFSpecies(model.getSpecies(0));
template.organism = species.getName();
if (species.isSetDetail()) {
template.organismDetails = species.getDetail();
}
}
// matrix data
{
PMFCompartment compartment = SBMLFactory.createPMFCompartment(model.getCompartment(0));
template.matrix = compartment.getName();
if (compartment.isSetDetail()) {
template.matrixDetails = compartment.getDetail();
}
}
// creator
Metadata metadataAnnot = new MetadataAnnotation(doc.getAnnotation()).getMetadata();
if (metadataAnnot.isSetGivenName()) {
template.creator = metadataAnnot.getGivenName();
}
// family name
if (metadataAnnot.isSetFamilyName()) {
template.familyName = metadataAnnot.getFamilyName();
}
// contact
if (metadataAnnot.isSetContact()) {
template.contact = metadataAnnot.getContact();
}
// reference description link
if (metadataAnnot.isSetReferenceLink()) {
template.referenceDescriptionLink = metadataAnnot.getReferenceLink();
}
// created date
if (metadataAnnot.isSetCreatedDate()) {
String dateAsString = metadataAnnot.getCreatedDate();
try {
template.createdDate = FskMetaData.dateFormat.parse(dateAsString);
} catch (ParseException e) {
System.err.println(dateAsString + " is not a valid date");
e.printStackTrace();
}
}
// modified date
if (metadataAnnot.isSetModifiedDate()) {
String dateAsString = metadataAnnot.getModifiedDate();
try {
template.modifiedDate = FskMetaData.dateFormat.parse(dateAsString);
} catch (ParseException e) {
System.err.println(dateAsString + " is not a valid date");
e.printStackTrace();
}
}
// model rights
if (metadataAnnot.isSetRights()) {
template.rights = metadataAnnot.getRights();
}
// model type
if (metadataAnnot.isSetType()) {
template.type = metadataAnnot.getType();
}
template.subject = new ModelRule(rule).getModelClass();
// FIXME: Broken UTF-8 characters
// // model notes
// if (model.isSetNotes()) {
// try {
// template.notes = Jsoup.parse(model.getNotesString()).text();
// } catch (XMLStreamException e) {
// System.err.println("Error accesing the notes of " + model);
// e.printStackTrace();
// }
// }
// dependent variable data
{
String depId = rule.getVariable();
// Gets parameter for the dependent variable and sets it
Parameter param = model.getParameter(depId);
template.dependentVariable.name = param.getName();
// Gets and sets dependent variable unit
String unitId = param.getUnits();
if (!unitId.equals("dimensionless")) {
UnitDefinition unitDef = model.getUnitDefinition(unitId);
if (unitDef != null) {
template.dependentVariable.unit = unitDef.getName();
}
}
// Sets dependent variable min & max
for (Limits lim : limits) {
if (lim.getVar().replaceAll("\\_", "\\.").equals(depId)) {
if (lim.getMin() != null)
template.dependentVariable.min = lim.getMin().toString();
if (lim.getMax() != null)
template.dependentVariable.max = lim.getMax().toString();
break;
}
}
}
// independent variable data
{
String depId = rule.getVariable();
List<Parameter> indepParams = model.getListOfParameters().filterList(new Filter() {
@Override
public boolean accepts(Object o) {
return !((Parameter) o).getId().equals(depId);
}
});
final int numParams = indepParams.size();
for (int p = 0; p < numParams; p++) {
Parameter param = indepParams.get(p);
Variable variable = new Variable();
variable.name = param.getName().trim();
// unit
String unitId = param.getUnits();
variable.unit = "";
if (!unitId.equals("dimensionless")) {
UnitDefinition unitDef = model.getUnitDefinition(unitId);
if (unitDef != null) {
variable.unit = unitDef.getName();
}
}
Limits paramLimits =
limits.stream().filter(lim -> lim.getVar().equals(param.getId())).findFirst().get();
variable.min = paramLimits.getMin().toString();
variable.max = paramLimits.getMax().toString();
variable.value = "";
template.independentVariables.add(variable);
}
}
template.hasData = false;
return template;
}
private static class Assignment {
enum Type {
/** R command with the = assignment operator. E.g. x = value */
equals,
/** R command with the <- assignment operator. E.g. x <- value */
left,
/** R command with the <<- scoping assignment operator. E.g. x <<- value */
super_left,
/** R command with the -> assignment operator. E.g. value -> x */
right,
/** R command with the ->> assignment operator. E.g. value ->> x */
super_right
}
String variable;
String value;
public Assignment(String line, Assignment.Type type) {
if (type == Type.equals) {
String[] tokens = line.split("||");
variable = tokens[0].trim();
value = tokens[1].trim();
} else if (type == Type.left) {
String[] tokens = line.split("<-");
variable = tokens[0].trim();
value = tokens[1].trim();
} else if (type == Type.super_left) {
String[] tokens = line.split("<<-");
variable = tokens[0].trim();
value = tokens[1].trim();
} else if (type == Type.right) {
String[] tokens = line.split("->");
variable = tokens[1].trim();
value = tokens[0].trim();
} else if (type == Type.super_right) {
String[] tokens = line.split("->>");
variable = tokens[1].trim();
value = tokens[0].trim();
}
}
}
private Map<String, String> getVariablesFromAssignments(String paramScript) {
Map<String, String> vars = new HashMap<>();
for (String line : paramScript.split("\\r?\\n")) {
line = line.trim();
if (line.startsWith("#"))
continue;
if (line.indexOf("=") != -1) {
Assignment a = new Assignment(line, Assignment.Type.equals);
vars.put(a.variable, a.value);
} else if (line.indexOf("<-") != -1) {
Assignment a = new Assignment(line, Assignment.Type.left);
vars.put(a.variable, a.value);
} else if (line.indexOf("<<-") != -1) {
Assignment a = new Assignment(line, Assignment.Type.super_left);
vars.put(a.variable, a.value);
} else if (line.indexOf("->>") != -1) {
Assignment a = new Assignment(line, Assignment.Type.right);
vars.put(a.variable, a.value);
} else if (line.indexOf("->") != -1) {
Assignment a = new Assignment(line, Assignment.Type.super_right);
vars.put(a.variable, a.value);
}
}
return vars;
}
}