package edu.isi.karma.controller.command.publish;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jgrapht.graph.DirectedWeightedMultigraph;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.isi.karma.controller.command.CommandException;
import edu.isi.karma.controller.command.CommandType;
import edu.isi.karma.controller.command.WorksheetCommand;
import edu.isi.karma.controller.history.CommandHistory;
import edu.isi.karma.controller.history.CommandHistory.HistoryArguments;
import edu.isi.karma.controller.history.HistoryJsonUtil;
import edu.isi.karma.controller.update.AbstractUpdate;
import edu.isi.karma.controller.update.InfoUpdate;
import edu.isi.karma.controller.update.UpdateContainer;
import edu.isi.karma.modeling.alignment.Alignment;
import edu.isi.karma.modeling.alignment.AlignmentManager;
import edu.isi.karma.rep.Worksheet;
import edu.isi.karma.rep.Workspace;
import edu.isi.karma.rep.alignment.ColumnNode;
import edu.isi.karma.rep.alignment.Label;
import edu.isi.karma.rep.alignment.LabeledLink;
import edu.isi.karma.rep.alignment.Node;
import edu.isi.karma.view.VWorksheet;
import edu.isi.karma.view.VWorkspace;
import edu.isi.karma.webserver.ContextParametersRegistry;
import edu.isi.karma.webserver.ServletContextParameterMap;
import edu.isi.karma.webserver.ServletContextParameterMap.ContextParameter;
public class PublishReportCommand extends WorksheetCommand {
private enum JsonKeys {
updateType, fileUrl, worksheetId
}
private String worksheetName;
// Logger object
private static Logger logger = LoggerFactory.getLogger(PublishReportCommand.class);
public PublishReportCommand(String id, String model, String worksheetId) {
super(id, model, worksheetId);
}
@Override
public String getCommandName() {
return this.getClass().getSimpleName();
}
@Override
public String getTitle() {
return "Publish Report";
}
@Override
public String getDescription() {
return worksheetName;
}
@Override
public CommandType getCommandType() {
return CommandType.notInHistory;
}
@Override
public UpdateContainer doIt(Workspace workspace) throws CommandException {
final ServletContextParameterMap contextParameters = ContextParametersRegistry.getInstance().getContextParameters(workspace.getContextId());
final Worksheet worksheet = workspace.getWorksheet(worksheetId);
this.worksheetName = worksheet.getTitle();
// Prepare the file path and names
final String newWorksheetName = worksheetName;
final String fileName = newWorksheetName + ".md";
final String fileLocalPath = contextParameters.getParameterValue(ContextParameter.REPORT_PUBLISH_DIR) +
fileName;
final String relFilename = contextParameters.getParameterValue(ContextParameter.REPORT_PUBLISH_RELATIVE_DIR) + fileName;
final Workspace finalWorkspace = workspace;
UpdateContainer uc = new UpdateContainer(new AbstractUpdate() {
public void generateJson(String prefix, PrintWriter pw,
VWorkspace vWorkspace) {
try {
File f = new File(fileLocalPath);
File parentDir = f.getParentFile();
parentDir.mkdirs();
PrintWriter fileWriter = new PrintWriter(f);
fileWriter.println("# " + worksheet.getTitle());
fileWriter.println();
fileWriter.println("## Add Column");
writeAddColumns(finalWorkspace, worksheet, fileWriter);
fileWriter.println();
fileWriter.println("## Add Node/Literal");
writeAddNodes(finalWorkspace, worksheet, fileWriter);
fileWriter.println();
fileWriter.println("## PyTransforms");
writePyTransforms(finalWorkspace, worksheet, fileWriter);
fileWriter.println();
fileWriter.println("## Selections");
writeSelections(finalWorkspace, worksheet, fileWriter);
fileWriter.println();
fileWriter.println("## Semantic Types");
VWorksheet viewWorksheet = vWorkspace.getViewFactory().getVWorksheetByWorksheetId(worksheetId);
writeSemanticTypes(vWorkspace, viewWorksheet, fileWriter);
fileWriter.println();
fileWriter.println("## Links");
writeLinks(finalWorkspace, worksheet, fileWriter);
fileWriter.close();
JSONObject outputObject = new JSONObject();
outputObject.put(JsonKeys.updateType.name(), "PublishReportUpdate");
outputObject.put(JsonKeys.fileUrl.name(), relFilename);
outputObject.put(JsonKeys.worksheetId.name(), worksheetId);
pw.println(outputObject.toString(4));
pw.println(",");
new InfoUpdate("Succesfully generated report").generateJson(prefix, pw, vWorkspace);
} catch(Exception e) {
pw.println("Error generating report");
logger.error("Error generating report", e);
}
}
});
return uc;
}
private void writePyTransforms(Workspace workspace, Worksheet worksheet, PrintWriter pw) throws IOException {
//Map for py transforms, so only the latest pyTransform for a column gets written
LinkedHashMap<String, String> pyTransformMap = new LinkedHashMap<>();
String linebreak = System.getProperty("line.separator");
String historyFile = CommandHistory.getHistorySaver(workspace.getId()).getHistoryFilepath(worksheetId);
try {
JSONArray history = CommandHistory.getHistorySaver(workspace.getId()).loadHistory(historyFile);
for(int i=0; i<history.length(); i++) {
try {
JSONObject command = history.getJSONObject(i);
JSONArray inputParamArr = command.getJSONArray(HistoryArguments.inputParameters.name());
String commandName = command.getString(HistoryArguments.commandName.name());
if(commandName.equalsIgnoreCase("SubmitPythonTransformationCommand") || commandName.equalsIgnoreCase("SubmitEditPythonTransformationCommand")) {
String code = HistoryJsonUtil.getStringValue("transformationCode", inputParamArr);
String columnName = getColumnNameFromInputParams(inputParamArr);
StringBuffer pyTransform = new StringBuffer("#### _" + columnName + "_");
pyTransform.append(linebreak);
String invocationColumnName = getFullColumnPath(inputParamArr);
pyTransform.append("From column: _" + invocationColumnName + "_").append(linebreak);
pyTransform.append("``` python").append(linebreak);
pyTransform.append(code).append(linebreak);
pyTransform.append("```").append(linebreak);
pyTransform.append(linebreak);
pyTransformMap.put(columnName, pyTransform.toString());
}
} catch(Exception e) {
logger.error("Error writing PyTransforms for Publish Report");
}
}
} catch(Exception e) {
logger.error("Error reading history file:" + historyFile);
}
//Now get all transforms from the map and write them to the writer
for(Map.Entry<String, String> stringStringEntry : pyTransformMap.entrySet()) {
pw.print(stringStringEntry.getValue());
}
}
private void writeSelections(Workspace workspace, Worksheet worksheet, PrintWriter pw) throws IOException {
//Map for py transforms, so only the latest pyTransform for a column gets written
String linebreak = System.getProperty("line.separator");
String historyFile = CommandHistory.getHistorySaver(workspace.getId()).getHistoryFilepath(worksheetId);
try {
JSONArray history = CommandHistory.getHistorySaver(workspace.getId()).loadHistory(historyFile);
for(int i=0; i<history.length(); i++) {
try {
JSONObject command = history.getJSONObject(i);
JSONArray inputParamArr = command.getJSONArray(HistoryArguments.inputParameters.name());
String commandName = command.getString(HistoryArguments.commandName.name());
if(commandName.equalsIgnoreCase("OperateSelectionCommand")) {
String code = HistoryJsonUtil.getStringValue("pythonCode", inputParamArr);
String selectionName = HistoryJsonUtil.getStringValue("selectionName", inputParamArr);
StringBuffer pyTransform = new StringBuffer("#### _" + selectionName + "_");
pyTransform.append(linebreak);
String invocationColumnName = getFullColumnPath(inputParamArr);
String operation = HistoryJsonUtil.getStringValue("operation", inputParamArr);
pyTransform.append("From column: _" + invocationColumnName + "_").append(linebreak);
pyTransform.append("<br>Operation: `" + operation + "`").append(linebreak);
pyTransform.append("``` python").append(linebreak);
pyTransform.append(code).append(linebreak);
pyTransform.append("```").append(linebreak);
pyTransform.append(linebreak);
pw.print(pyTransform.toString());
}
} catch(Exception e) {
logger.error("Error writing Selections for Publish Report");
}
}
} catch(Exception e) {
logger.error("Error reading history file:" + historyFile);
}
}
private String getColumnNameFromInputParams(JSONArray inputParamArr) {
String columnName = HistoryJsonUtil.getStringValue("newColumnName", inputParamArr);
if ("".equals(columnName)) {
JSONArray columnNames = HistoryJsonUtil.getJSONArrayValue("targetHNodeId", inputParamArr);
for(int j=0; j<columnNames.length(); j++) {
JSONObject columnNameObj = columnNames.getJSONObject(j);
columnName =columnNameObj.getString("columnName");
}
}
return columnName;
}
private String getFullColumnPath(JSONArray inputParamArr) {
//Pedro: throw-away code, ought to have a better way to construct column paths.
JSONArray hNodeIdArray = HistoryJsonUtil.getJSONArrayValue("hNodeId", inputParamArr);
String invocationColumnName = "";
String sep = "";
for(int j=0; j<hNodeIdArray.length(); j++) {
JSONObject columnNameObj = hNodeIdArray.getJSONObject(j);
String name =columnNameObj.getString("columnName");
invocationColumnName += sep + name;
sep = " / ";
}
return invocationColumnName;
}
private void writeAddColumns(Workspace workspace, Worksheet worksheet, PrintWriter pw) throws IOException {
//Map for py transforms, so only the latest pyTransform for a column gets written
LinkedHashMap<String, String> pyTransformMap = new LinkedHashMap<>();
String linebreak = System.getProperty("line.separator");
String historyFile = CommandHistory.getHistorySaver(workspace.getId()).getHistoryFilepath(worksheetId);
try {
JSONArray history = CommandHistory.getHistorySaver(workspace.getId()).loadHistory(historyFile);
for(int i=0; i<history.length(); i++) {
try {
JSONObject command = history.getJSONObject(i);
JSONArray inputParamArr = command.getJSONArray(HistoryArguments.inputParameters.name());
String commandName = command.getString(HistoryArguments.commandName.name());
if(commandName.equals("AddColumnCommand")) {
String columnName = getColumnNameFromInputParams(inputParamArr);
String value = HistoryJsonUtil.getStringValue("defaultValue", inputParamArr);
StringBuffer pyTransform = new StringBuffer("#### _" + columnName + "_");
pyTransform.append(linebreak);
String invocationColumnName = getFullColumnPath(inputParamArr);
pyTransform.append("From column: _" + invocationColumnName + "_").append(linebreak);
pyTransform.append("<br/>Value: `" + value + "`").append(linebreak);
pyTransform.append(linebreak);
pyTransformMap.put(columnName, pyTransform.toString());
}
} catch(Exception e) {
logger.error("Error writing AddColumns for Publish Report");
}
}
} catch(Exception e) {
logger.error("Error reading history file:" + historyFile);
}
//Now get all transforms from the map and write them to the writer
for(Map.Entry<String, String> stringStringEntry : pyTransformMap.entrySet()) {
pw.print(stringStringEntry.getValue());
}
}
private void writeAddNodes(Workspace workspace, Worksheet worksheet, PrintWriter pw) throws IOException {
//Map for py transforms, so only the latest pyTransform for a column gets written
LinkedHashMap<String, String> pyTransformMap = new LinkedHashMap<>();
String linebreak = System.getProperty("line.separator");
String historyFile = CommandHistory.getHistorySaver(workspace.getId()).getHistoryFilepath(worksheetId);
try {
JSONArray history = CommandHistory.getHistorySaver(workspace.getId()).loadHistory(historyFile);
for(int i=0; i<history.length(); i++) {
try {
JSONObject command = history.getJSONObject(i);
JSONArray inputParamArr = command.getJSONArray(HistoryArguments.inputParameters.name());
String commandName = command.getString(HistoryArguments.commandName.name());
if(commandName.equals("AddNodeCommand")) {
String nodeLabel = HistoryJsonUtil.getStringValue("label", inputParamArr);
String node_uri = HistoryJsonUtil.getStringValue("uri", inputParamArr);
String node_id = HistoryJsonUtil.getStringValue("id", inputParamArr);
StringBuffer pyTransform = new StringBuffer("#### Node: `" + nodeLabel + "`");
pyTransform.append(linebreak);
pyTransform.append("Uri: `" + node_uri + "`").append(linebreak);
pyTransform.append("<br/>Id: `" + node_id + "`").append(linebreak);
pyTransform.append(linebreak);
pyTransformMap.put(nodeLabel, pyTransform.toString());
} else if(commandName.equals("AddLiteralNodeCommand")) {
String value = HistoryJsonUtil.getStringValue("literalValue", inputParamArr);
String literalType = HistoryJsonUtil.getStringValue("literalType", inputParamArr);
String language = HistoryJsonUtil.getStringValue("language", inputParamArr);
boolean isUri = HistoryJsonUtil.getBooleanValue("isUri", inputParamArr);
StringBuffer pyTransform = new StringBuffer("#### Literal Node: `" + value + "`");
pyTransform.append(linebreak);
pyTransform.append("Literal Type: `" + literalType + "`").append(linebreak);
pyTransform.append("<br/>Language: `" + language + "`").append(linebreak);
pyTransform.append("<br/>isUri: `" + isUri + "`").append(linebreak);
pyTransform.append(linebreak);
pyTransformMap.put(value, pyTransform.toString());
}
} catch(Exception e) {
logger.error("Error writing AddNodes for Publish Report");
}
}
} catch(Exception e) {
logger.error("Error reading history file:" + historyFile);
}
//Now get all transforms from the map and write them to the writer
for(Map.Entry<String, String> stringStringEntry : pyTransformMap.entrySet()) {
pw.print(stringStringEntry.getValue());
}
}
private void writeSemanticTypes(VWorkspace vWorkspace, VWorksheet vWorksheet, PrintWriter pw) {
pw.println("| Column | Property | Class |");
pw.println("| ----- | -------- | ----- |");
List<String> hNodeIdList = vWorksheet.getHeaderVisibleLeafNodes();
String alignmentId = AlignmentManager.Instance().constructAlignmentId(
vWorkspace.getWorkspace().getId(), vWorksheet.getWorksheetId());
Alignment alignment = AlignmentManager.Instance().getAlignment(alignmentId);
HashSet<String> provenanceProperties = new HashSet<>();
if(alignment != null) {
List<Node> columnNodes = new ArrayList<>();
for(String hNodeId: hNodeIdList) {
Node columnNode = alignment.getNodeById(hNodeId);
if(columnNode != null)
columnNodes.add(columnNode);
}
Collections.sort(columnNodes, new Comparator<Node>() {
@Override
public int compare(Node n1, Node n2) {
String l1name = getClassName(n1);
String l2name = getClassName(n2);
return l1name.compareTo(l2name);
}
});
if(columnNodes != null) {
for(Node node : columnNodes) {
if(node instanceof ColumnNode) {
ColumnNode columnNode = (ColumnNode)node;
String columnName = getClassName(columnNode);
List<LabeledLink> links = alignment.getGraphBuilder().getIncomingLinks(node.getId());
for(LabeledLink link : links) {
String property = "`" + getPropertyName(link.getLabel()) + "`";
String classname = getClassName(link.getSource());
if(link.isProvenance()) {
property = property + "<BR> - _specified provenance_";
String provProperty = columnName + "-" + property;
if(provenanceProperties.contains(provProperty))
continue;
provenanceProperties.add(provProperty);
}
pw.println("| _" + columnName + "_ | " + property + " | `" + classname + "`|");
}
}
}
}
}
pw.println();
}
private void writeLinks(Workspace workspace, Worksheet worksheet, PrintWriter pw) {
pw.println("| From | Property | To |");
pw.println("| --- | -------- | ---|");
Alignment alignment = AlignmentManager.Instance().getAlignment(workspace.getId(), worksheet.getId());
HashSet<String> provenanceProperties = new HashSet<>();
if(alignment != null) {
DirectedWeightedMultigraph<Node, LabeledLink> alignmentGraph = alignment.getSteinerTree();
if(alignmentGraph != null) {
List<LabeledLink> links = Arrays.asList(alignmentGraph.edgeSet().toArray(new LabeledLink[0]));
Collections.sort(links, new Comparator<LabeledLink>() {
@Override
public int compare(LabeledLink l1, LabeledLink l2) {
String l1name = getClassName(l1.getSource());
String l2name = getClassName(l2.getSource());
return l1name.compareTo(l2name);
}
});
for (LabeledLink link : links) {
Node target = link.getTarget();
if(target instanceof ColumnNode) { //ALready covered in semantic types
continue;
}
Node source = link.getSource();
String property = "`" + getPropertyName(link.getLabel()) + "`";
String to = getClassName(target);
String from = getClassName(source);
if(link.isProvenance()) {
property = property + "<BR> - _specified provenance_";
String provProperty = property + "-" + to;
if(provenanceProperties.contains(provProperty))
continue;
provenanceProperties.add(provProperty);
}
pw.println("| `" + from + "` | " + property + " | `" + to + "`|");
}
}
}
}
private String getClassName(Node node) {
String name = (node instanceof ColumnNode) ? ((ColumnNode)node).getColumnName() : node.getDisplayId();
return name;
}
private String getPropertyName(Label label) {
String name = label.getDisplayName();
if(name.equals("km-dev:classLink"))
name = "uri";
return name;
}
@Override
public UpdateContainer undoIt(Workspace workspace) {
return null;
}
}