// Copyright 2015 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.plugin.genericevents.commands; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import de.ugoe.cs.autoquest.CommandHelpers; import de.ugoe.cs.autoquest.eventcore.Event; import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric; import de.ugoe.cs.util.console.Command; import de.ugoe.cs.util.console.Console; import de.ugoe.cs.util.console.GlobalDataContainer; /** *

* TODO comment *

* * @author Patrick Harms */ public class CMDexportSogouQTaskTrees implements Command { /* (non-Javadoc) * @see de.ugoe.cs.util.console.Command#run(java.util.List) */ @Override public void run(List parameters) { String taskTreeName = null; String path = null; String numberStr = null; boolean withTimestamp = false; try { for (int i = 0; i < parameters.size(); i++) { String parameter = (String) parameters.get(i); if (!parameter.startsWith("-")) { if (taskTreeName == null) { taskTreeName = parameter; } else if (path == null) { path = parameter; } else if (numberStr == null) { numberStr = parameter; } else { throw new IllegalArgumentException("unrecognized parameter: " + parameter); } } else { if ("-withTimestamp".equals(parameter)) { withTimestamp = true; } else { throw new IllegalArgumentException("unrecognized parameter: " + parameter); } } } } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException("could not process parameters", e); } if (path == null) { throw new IllegalArgumentException("no path to directory provided"); } if (taskTreeName == null) { throw new IllegalArgumentException("no task tree specified"); } if (numberStr == null) { throw new IllegalArgumentException("no number of tasks to be exported provided"); } int tasksToExport; try{ tasksToExport = Integer.parseInt(numberStr); } catch (Exception e) { throw new IllegalArgumentException("invalid number of tasks to be exported: " + numberStr, e); } File directory = new File(path); if (!directory.isDirectory()) { Console.printerrln(path + " is not a directory"); return; } if (directory.exists() && (directory.list().length > 0)) { Console.printerrln(path + " is not empty"); return; } Object dataObject = GlobalDataContainer.getInstance().getData(taskTreeName); if (dataObject == null) { CommandHelpers.objectNotFoundMessage(taskTreeName); return; } if (!(dataObject instanceof ITaskModel)) { CommandHelpers.objectNotType(taskTreeName, "ITaskTree"); return; } ITaskModel taskModel = (ITaskModel) dataObject; LinkedList sequencesToExport = determineSequencesToExport(tasksToExport, taskModel); int index = 1; int width = Integer.toString(tasksToExport).length(); for (ISequence sequence : sequencesToExport) { export(String.format("%0" + width + "d", index++), sequence, taskModel, directory, withTimestamp); } } /** *

* TODO: comment *

* * @param tasksToExport * @param taskModel * @return */ private LinkedList determineSequencesToExport(int tasksToExport, ITaskModel taskModel) { LinkedList sequencesToExport = new LinkedList<>(); for (ITask task : taskModel.getTasks()) { if (task instanceof ISequence) { int coverage = taskModel.getTaskInfo(task).getMeasureValue(TaskMetric.EVENT_COVERAGE); boolean inserted = false; ListIterator it = sequencesToExport.listIterator(); while (it.hasNext()) { ISequence candidate = it.next(); int candidateCoverage = taskModel.getTaskInfo(candidate).getMeasureValue(TaskMetric.EVENT_COVERAGE); if (coverage > candidateCoverage) { it.previous(); it.add((ISequence) task); inserted = true; break; } } if (!inserted) { sequencesToExport.add((ISequence) task); } while (sequencesToExport.size() > tasksToExport) { sequencesToExport.removeLast(); } } } return sequencesToExport; } /** *

* TODO: comment *

* * @param index * @param sequence * @param path * @param withTimestamp */ private void export(String folderPrefix, ISequence sequence, ITaskModel taskModel, File directory, boolean withTimestamp) { File exportDirectory = new File(directory, folderPrefix + "_pattern" + sequence.getId()); if (exportDirectory.exists()) { throw new IllegalArgumentException(exportDirectory + " already exists"); } else { exportDirectory.mkdirs(); } createPatternFile(sequence, taskModel, withTimestamp, exportDirectory); createSessionFiles(sequence, taskModel, withTimestamp, exportDirectory); Console.println("exported " + sequence); } /** *

* TODO: comment *

* * @param sequence * @param taskModel * @param withTimestamp * @param exportDirectory */ private void createPatternFile(ISequence sequence, ITaskModel taskModel, boolean withTimestamp, File exportDirectory) { // write the pattern file File patternFile = new File(exportDirectory, "patternDetails.txt"); PrintStream out = null; try { out = new PrintStream(new FileOutputStream(patternFile)); out.println("########################################################################"); out.println("# Pattern"); out.println("########################################################################"); out.println(); dumpSequence(sequence, out); out.println(); out.println("########################################################################"); out.println("# Statistics"); out.println("########################################################################"); out.println(); int value = taskModel.getTaskInfo(sequence).getMeasureValue(TaskMetric.COUNT); out.println(" count: " + TaskMetric.COUNT.formatValue(value)); value = taskModel.getTaskInfo(sequence).getMeasureValue(TaskMetric.EVENT_COVERAGE); out.println(" coverage: " + TaskMetric.EVENT_COVERAGE.formatValue(value)); value = taskModel.getTaskInfo(sequence).getMeasureValue(TaskMetric.EVENT_COVERAGE_RATIO); out.println("coverage ratio: " + TaskMetric.EVENT_COVERAGE_RATIO.formatValue(value)); if (withTimestamp) { long averageTimeBetweenEvents = getAverageTimeBetweenEvents(sequence); out.println("\naverage event time interval: " + averageTimeBetweenEvents + "ms"); } out.println(); out.println(); out.println("########################################################################"); out.println("# Execution variants"); out.println("########################################################################"); out.println(); int index = 1; for (Collection variant : sequence.getExecutionVariants()) { dumpExecutionVariant(index++, variant.size(), variant.iterator().next(), out); out.println(); } } catch (IOException e) { throw new IllegalArgumentException("could not write into file " + patternFile); } finally { if (out != null) { try { out.close(); } catch (Exception e) { // ignore } } } } /** *

* TODO: comment *

* * @param sequence * @return */ private long getAverageTimeBetweenEvents(ISequence sequence) { int intervalCount = 0; long cummulativeTime = 0; for (ITaskInstance instance : sequence.getInstances()) { final List timestamps = new ArrayList<>(); instance.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { timestamps.add(eventTaskInstance.getEvent().getTimestamp()); } }); for (int i = 1; i < timestamps.size(); i++) { cummulativeTime += timestamps.get(i) - timestamps.get(i - 1); intervalCount++; } } return (cummulativeTime / intervalCount); } /** *

* TODO: comment *

* * @param sequence * @param stream */ private void dumpSequence(ISequence sequence, final PrintStream stream) { final String[] indent = new String[1]; indent[0] = ""; sequence.accept(new DefaultTaskTraversingVisitor() { @Override public void visit(IEventTask eventTask) { stream.print(indent[0]); stream.println(eventTask.toString()); } @Override public void visit(IStructuringTemporalRelationship relationship) { stream.print(indent[0]); stream.print(relationship.toString()); stream.println(" {"); String indentBefore = indent[0]; indent[0] = indentBefore + " "; super.visit(relationship); indent[0] = indentBefore; stream.print(indent[0]); stream.println("}"); } @Override public void visit(IMarkingTemporalRelationship relationship) { stream.print(indent[0]); stream.print(relationship.toString()); stream.println(" {"); String indentBefore = indent[0]; indent[0] = indentBefore + " "; super.visit(relationship); indent[0] = indentBefore; stream.print(indent[0]); stream.println("}"); } }); } /** * */ private void dumpExecutionVariant(int variantIndex, int count, ITaskInstance instance, final PrintStream stream) { stream.println("\nVariant " + variantIndex + " (executed " + count + " times)\n"); final String[] indent = new String[1]; indent[0] = " "; instance.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { stream.print(indent[0]); stream.println(eventTaskInstance.toString()); } @Override public void visit(ITaskInstanceList taskInstanceList) { stream.print(indent[0]); stream.print(taskInstanceList.toString()); stream.println(" {"); String indentBefore = indent[0]; indent[0] = indentBefore + " "; super.visit(taskInstanceList); indent[0] = indentBefore; stream.print(indent[0]); stream.println("}"); } }); } /** *

* TODO: comment *

* * @param instance * @param taskModel */ private void createSessionFiles(final ISequence sequence, final ITaskModel taskModel, final boolean withTimestamp, final File exportDirectory) { final File sessionsDirectory = new File(exportDirectory, "sessions"); if (sessionsDirectory.exists()) { throw new IllegalArgumentException(sessionsDirectory + " already exists"); } else { sessionsDirectory.mkdirs(); } for (IUserSession session : taskModel.getUserSessions()) { for (ITaskInstance instance : session) { instance.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(ISequenceInstance sequenceInstance) { if (sequence == sequenceInstance.getTask()) { createSessionFile(sequenceInstance, withTimestamp, sessionsDirectory); } super.visit(sequenceInstance); } }); } } } /** *

* TODO: comment *

* * @param instance * @param taskModel */ private void createSessionFile(final ISequenceInstance instance, final boolean withTimestamp, final File exportDirectory) { // write the session file final String userId = getUserOf(instance, exportDirectory); final File sessionsDirectory = new File(exportDirectory, userId); if (!sessionsDirectory.exists()) { sessionsDirectory.mkdirs(); } File sessionFile; // a user may have many sessions, search for the next id of it. int index = 0; do { sessionFile = new File (sessionsDirectory, "session_" + String.format("%09d", ++index) + ".txt"); } while (sessionFile.exists()); PrintStream out = null; try { out = new PrintStream(new FileOutputStream(sessionFile)); final PrintStream finalOut = out; instance.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { Event event = eventTaskInstance.getEvent(); if (withTimestamp) { finalOut.print(event.getTimestamp()); finalOut.print("\t"); } finalOut.print(userId); finalOut.print("\t"); finalOut.print(event.getParameter("query")); finalOut.print("\t"); finalOut.print(event.getParameter("selectedResultPage")); finalOut.print(" "); finalOut.print(event.getParameter("selectedResultIndex")); finalOut.print("\t"); finalOut.println(event.getParameter("selectedResult")); } }); } catch (IOException e) { throw new IllegalArgumentException("could not write into file " + sessionFile); } finally { if (out != null) { try { out.close(); } catch (Exception e) { // ignore } } } } /** *

* TODO: comment *

* * @param instance * @return */ private String getUserOf(ISequenceInstance instance, final File exportDirectory) { final String[] result = new String[1]; instance.accept(new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { if (result[0] == null) { result[0] = eventTaskInstance.getEvent().getParameter("userId"); } } @Override public void visit(ITaskInstanceList taskInstanceList) { if (result[0] == null) { super.visit(taskInstanceList); } } }); if (result[0] == null) { // no user id stored with events --> generate generic unused one File sessionFile = null; int index = 0; do { sessionFile = new File (exportDirectory, "session_" + String.format("%09d", ++index) + ".txt"); } while (sessionFile.exists()); result[0] = String.format("%09d", index); } return result[0]; } /* (non-Javadoc) * @see de.ugoe.cs.util.console.Command#help() */ @Override public String help() { return "exportSogouQTaskTrees " + "{-withTimestamp}"; } }