From c9842901619a1de3fac0a338da37aae1bd19578f Mon Sep 17 00:00:00 2001
From: Eike Cochu <eike@cochu.com>
Date: Thu, 24 Mar 2016 13:05:57 +0100
Subject: [PATCH] added cmd config wizard for topic models

---
 .../java/de/vipra/cmd/CommandLineOptions.java |  12 +-
 .../src/main/java/de/vipra/cmd/Main.java      |   4 +
 .../de/vipra/cmd/option/EditModelCommand.java |  65 +++++++++
 vipra-ui/app/html/directives/topic-link.html  |   2 +-
 vipra-ui/app/js/controllers.js                |  10 ++
 .../main/java/de/vipra/util/ConsoleUtils.java | 128 +++++++++++++++++-
 .../main/java/de/vipra/util/EnumUtils.java    |  23 ++++
 .../de/vipra/util/model/TopicModelConfig.java |   9 ++
 .../main/java/de/vipra/util/model/Window.java |  12 +-
 9 files changed, 256 insertions(+), 9 deletions(-)
 create mode 100644 vipra-cmd/src/main/java/de/vipra/cmd/option/EditModelCommand.java
 create mode 100644 vipra-util/src/main/java/de/vipra/util/EnumUtils.java

diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/CommandLineOptions.java b/vipra-cmd/src/main/java/de/vipra/cmd/CommandLineOptions.java
index ac6610e5..f89aa19f 100644
--- a/vipra-cmd/src/main/java/de/vipra/cmd/CommandLineOptions.java
+++ b/vipra-cmd/src/main/java/de/vipra/cmd/CommandLineOptions.java
@@ -20,6 +20,8 @@ public class CommandLineOptions {
 	public static final Option ALL = Option.builder("A").longOpt("all").desc("select all models, short for -S all").build();
 	public static final Option CREATE = Option.builder("C").longOpt("create").desc("create new models").hasArgs().argName("models...").build();
 	public static final Option DELETE = Option.builder("D").longOpt("delete").desc("delete existing models").hasArgs().argName("models...").build();
+	public static final Option EDIT = Option.builder("E").longOpt("edit").desc("edit config of selected models").hasArgs().argName("models...")
+			.build();
 	public static final Option IMPORT = Option.builder("I").longOpt("import").desc("import data from json into selected models").hasArgs()
 			.argName("models...").build();
 	public static final Option MODEL = Option.builder("M").longOpt("model").desc("generate topics on selected models").build();
@@ -30,7 +32,7 @@ public class CommandLineOptions {
 	private final String cmdName = "vipra";
 
 	public CommandLineOptions() {
-		final Option[] optionsArray = { CLEAR, DEBUG, HELP, INDEX, LIST, REREAD, SILENT, TEST, ALL, CREATE, DELETE, IMPORT, MODEL, SELECT };
+		final Option[] optionsArray = { CLEAR, DEBUG, HELP, INDEX, LIST, REREAD, SILENT, TEST, ALL, CREATE, DELETE, EDIT, IMPORT, MODEL, SELECT };
 		options = new Options();
 		for (final Option option : optionsArray)
 			options.addOption(option);
@@ -118,6 +120,14 @@ public class CommandLineOptions {
 		return getOptionValues(DELETE);
 	}
 
+	public boolean isEdit() {
+		return hasOption(EDIT);
+	}
+
+	public String[] modelsToEdit() {
+		return getOptionValues(EDIT);
+	}
+
 	public boolean isImport() {
 		return hasOption(IMPORT);
 	}
diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/Main.java b/vipra-cmd/src/main/java/de/vipra/cmd/Main.java
index a6c4906d..096f7612 100644
--- a/vipra-cmd/src/main/java/de/vipra/cmd/Main.java
+++ b/vipra-cmd/src/main/java/de/vipra/cmd/Main.java
@@ -12,6 +12,7 @@ import de.vipra.cmd.option.ClearCommand;
 import de.vipra.cmd.option.Command;
 import de.vipra.cmd.option.CreateModelCommand;
 import de.vipra.cmd.option.DeleteModelCommand;
+import de.vipra.cmd.option.EditModelCommand;
 import de.vipra.cmd.option.ImportCommand;
 import de.vipra.cmd.option.IndexingCommand;
 import de.vipra.cmd.option.ListModelsCommand;
@@ -67,6 +68,9 @@ public class Main {
 		if (opts.isList())
 			commands.add(new ListModelsCommand());
 
+		if (opts.isEdit())
+			commands.add(new EditModelCommand(opts.modelsToEdit()));
+
 		if (opts.isImport())
 			commands.add(new ImportCommand(opts.selectedModels(), opts.filesToImport()));
 
diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/EditModelCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/EditModelCommand.java
new file mode 100644
index 00000000..44552463
--- /dev/null
+++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/EditModelCommand.java
@@ -0,0 +1,65 @@
+package de.vipra.cmd.option;
+
+import de.vipra.util.Config;
+import de.vipra.util.ConsoleUtils;
+import de.vipra.util.Constants.ProcessorMode;
+import de.vipra.util.Constants.WindowResolution;
+import de.vipra.util.ex.DatabaseException;
+import de.vipra.util.model.TopicModelConfig;
+import de.vipra.util.model.TopicModelFull;
+import de.vipra.util.service.MongoService;
+
+public class EditModelCommand implements Command {
+
+	private final String[] names;
+	private MongoService<TopicModelFull, String> dbTopicModels;
+
+	public EditModelCommand(final String[] names) {
+		this.names = names;
+	}
+
+	private void editModel(final TopicModelConfig topicModelConfig) throws DatabaseException {
+		ConsoleUtils.info("editing model: " + topicModelConfig.getName());
+		topicModelConfig.setDescription(ConsoleUtils.readString("description (↲ to skip)", topicModelConfig.getDescription(), true));
+		topicModelConfig.setkTopics(ConsoleUtils.readInt("k topics", topicModelConfig.getkTopics(), 1, null, true));
+		topicModelConfig
+				.setDynamicMinIterations(ConsoleUtils.readInt("dynamic min iterations", topicModelConfig.getDynamicMinIterations(), 1, null, true));
+		topicModelConfig.setDynamicMaxIterations(ConsoleUtils.readInt("dynamic max iterations", topicModelConfig.getDynamicMaxIterations(),
+				topicModelConfig.getDynamicMinIterations(), null, true));
+		topicModelConfig.setStaticIterations(ConsoleUtils.readInt("static iterations", topicModelConfig.getStaticIterations(), 1, null, true));
+		topicModelConfig
+				.setTopicAutoNamingWords(ConsoleUtils.readInt("topic auto naming words", topicModelConfig.getTopicAutoNamingWords(), 1, null, true));
+		topicModelConfig
+				.setMaxSimilarDocuments(ConsoleUtils.readInt("max similar documents", topicModelConfig.getMaxSimilarDocuments(), 0, null, true));
+		topicModelConfig
+				.setDocumentMinimumLength(ConsoleUtils.readInt("document min length", topicModelConfig.getDocumentMinimumLength(), 0, null, true));
+		topicModelConfig.setDocumentMinimumWordFrequency(
+				ConsoleUtils.readInt("document min word frequency", topicModelConfig.getDocumentMinimumWordFrequency(), 0, null, true));
+		topicModelConfig.setSpotlightSupport(ConsoleUtils.readInt("spotlight support", topicModelConfig.getSpotlightSupport(), 0, null, true));
+		topicModelConfig
+				.setSpotlightConfidence(ConsoleUtils.readDouble("spotlight confidence", topicModelConfig.getSpotlightConfidence(), 0.0, 1.0, true));
+		topicModelConfig.setMinRelativeProbability(
+				ConsoleUtils.readDouble("min relative probability", topicModelConfig.getMinRelativeProbability(), 0.0, 1.0, true));
+		topicModelConfig.setRisingDecayLambda(ConsoleUtils.readDouble("rising decay lambda", topicModelConfig.getRisingDecayLambda()));
+		topicModelConfig.setMaxSimilarDocumentsDivergence(
+				ConsoleUtils.readDouble("max similar documents divergence", topicModelConfig.getMaxSimilarDocumentsDivergence(), 0.0, 1.0, true));
+		topicModelConfig
+				.setWindowResolution(ConsoleUtils.readEnum(WindowResolution.class, "window resolution", topicModelConfig.getWindowResolution()));
+		topicModelConfig.setProcessorMode(ConsoleUtils.readEnum(ProcessorMode.class, "processor mode", topicModelConfig.getProcessorMode()));
+
+		final TopicModelFull topicModel = new TopicModelFull(topicModelConfig.getName());
+		topicModel.setModelConfig(topicModelConfig);
+		dbTopicModels.updateSingle(topicModel, "modelConfig");
+	}
+
+	@Override
+	public void run() throws Exception {
+		final Config config = Config.getConfig();
+		dbTopicModels = MongoService.getDatabaseService(config, TopicModelFull.class);
+
+		for (final String name : names) {
+			editModel(config.getTopicModelConfig(name));
+		}
+	}
+
+}
diff --git a/vipra-ui/app/html/directives/topic-link.html b/vipra-ui/app/html/directives/topic-link.html
index 7ad606fe..bcdd3845 100644
--- a/vipra-ui/app/html/directives/topic-link.html
+++ b/vipra-ui/app/html/directives/topic-link.html
@@ -1,5 +1,5 @@
 <span>
-  <topic-menu topic="topic" right="true" />
+  <topic-menu topic="topic" />
   <a class="topic-link" ui-sref="topics.show({id:topic.id})">
     <span ng-bind="topic.name"></span>
     <ng-transclude/>
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index 4be0f3af..1a4e7a39 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -671,6 +671,16 @@
           }, $scope.topic, function(data) {
             $scope.topic = data;
             $scope.isRename = false;
+
+            // if topic list of parent view is loaded, replace name by new name
+            if($scope.$parent.topics) {
+              for(var i = 0, topic; i < $scope.$parent.topics.length; i++) {
+                topic = $scope.$parent.topics[i];
+                if(topic.id === data.id) {
+                  break;
+                }
+              }
+            }
           }, function(err) {
             $scope.errors = err;
           });
diff --git a/vipra-util/src/main/java/de/vipra/util/ConsoleUtils.java b/vipra-util/src/main/java/de/vipra/util/ConsoleUtils.java
index 7e2ee2f0..29cadf29 100644
--- a/vipra-util/src/main/java/de/vipra/util/ConsoleUtils.java
+++ b/vipra-util/src/main/java/de/vipra/util/ConsoleUtils.java
@@ -1,5 +1,9 @@
 package de.vipra.util;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
 import org.fusesource.jansi.Ansi;
 import org.fusesource.jansi.Ansi.Color;
 
@@ -24,7 +28,7 @@ public class ConsoleUtils {
 
 	public static void error(final String msg) {
 		if (!silent)
-			System.err.println(label("ERROR") + " - " + Ansi.ansi().fg(Color.RED).a(msg).reset());
+			System.out.println(label("ERROR") + " - " + Ansi.ansi().fg(Color.RED).a(msg).reset());
 	}
 
 	public static void error(final Throwable t) {
@@ -35,4 +39,126 @@ public class ConsoleUtils {
 		return StringUtils.pad(label, pad);
 	}
 
+	public static double readDouble(String message, final Double def, final Double min, final Double max, final boolean showBounds) {
+		final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+		if (showBounds)
+			message += " (" + (min != null ? min : "-∞") + ".." + (max != null ? max : "∞") + ")";
+		if (def != null)
+			message += " [" + def + "]";
+		message += ": ";
+		while (true) {
+			System.out.print(message);
+			try {
+				final String line = in.readLine();
+				if (line.isEmpty() && def != null)
+					return def;
+				final double d = Double.parseDouble(line);
+				if (min != null && d < min)
+					continue;
+				if (max != null && d > max)
+					continue;
+				return d;
+			} catch (final NumberFormatException e) {} catch (final IOException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
+	public static double readDouble(final String message, final Double def, final Double min, final Double max) {
+		return readDouble(message, def, min, max, true);
+	}
+
+	public static double readDouble(final String message, final Double def) {
+		return readDouble(message, def, null, null, true);
+	}
+
+	public static double readDouble(final String message) {
+		return readDouble(message, null, null, null, false);
+	}
+
+	public static int readInt(String message, final Integer def, final Integer min, final Integer max, final boolean showBounds) {
+		final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+		if (showBounds)
+			message += " (" + (min != null ? min : "-∞") + ".." + (max != null ? max : "∞") + ")";
+		if (def != null)
+			message += " [" + def + "]";
+		message += ": ";
+		while (true) {
+			System.out.print(message);
+			try {
+				final String line = in.readLine();
+				if (line.isEmpty() && def != null)
+					return def;
+				final int i = Integer.parseInt(line);
+				if (min != null && i < min)
+					continue;
+				if (max != null && i > max)
+					continue;
+				return i;
+			} catch (final NumberFormatException e) {} catch (final IOException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
+	public static int readInt(final String message, final Integer def, final Integer min, final Integer max) {
+		return readInt(message, def, min, max, true);
+	}
+
+	public static int readInt(final String message, final Integer def) {
+		return readInt(message, def, null, null, true);
+	}
+
+	public static int readInt(final String message) {
+		return readInt(message, null, null, null, false);
+	}
+
+	public static String readString(String message, final String def, final boolean canBeEmpty) {
+		final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+		message += ": ";
+		while (true) {
+			System.out.print(message);
+			try {
+				final String line = in.readLine();
+				if (line.isEmpty()) {
+					if (def != null)
+						return def;
+					if (!canBeEmpty)
+						continue;
+				}
+				return line;
+			} catch (final IOException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
+	public static String readString(final String message, final String def) {
+		return readString(message, def, false);
+	}
+
+	public static String readString(final String message) {
+		return readString(message, null, false);
+	}
+
+	public static <T extends Enum<?>> T readEnum(final Class<T> enumClass, String message, final T def) {
+		final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+		if (def != null)
+			message += " [" + def + "]";
+		message += ": ";
+		while (true) {
+			System.out.print(message);
+			try {
+				final String line = in.readLine();
+				final T value = EnumUtils.searchEnum(enumClass, line);
+				if (value != null)
+					return value;
+				if (def != null)
+					return def;
+			} catch (final IOException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
 }
diff --git a/vipra-util/src/main/java/de/vipra/util/EnumUtils.java b/vipra-util/src/main/java/de/vipra/util/EnumUtils.java
new file mode 100644
index 00000000..d7e57dc5
--- /dev/null
+++ b/vipra-util/src/main/java/de/vipra/util/EnumUtils.java
@@ -0,0 +1,23 @@
+package de.vipra.util;
+
+public class EnumUtils {
+
+	/**
+	 * Finds an enum value by its name, ignoring case.
+	 *
+	 * @param enumeration
+	 *            Enum to be searched
+	 * @param search
+	 *            Enum value to be searched
+	 * @return the found enum value, or null
+	 */
+	public static <T extends Enum<?>> T searchEnum(final Class<T> enumeration, final String search) {
+		for (final T each : enumeration.getEnumConstants()) {
+			if (each.name().compareToIgnoreCase(search) == 0) {
+				return each;
+			}
+		}
+		return null;
+	}
+
+}
diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java b/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java
index 366972d6..4dde14aa 100644
--- a/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java
+++ b/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java
@@ -17,6 +17,7 @@ import de.vipra.util.Constants.WindowResolution;
 public class TopicModelConfig implements Serializable {
 
 	private String name;
+	private String description;
 	private int kTopics = Constants.K_TOPICS;
 	private int dynamicMinIterations = Constants.DYNAMIC_MIN_ITER;
 	private int dynamicMaxIterations = Constants.DYNAMIC_MAX_ITER;
@@ -61,6 +62,14 @@ public class TopicModelConfig implements Serializable {
 		this.name = name;
 	}
 
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(final String description) {
+		this.description = description;
+	}
+
 	public int getkTopics() {
 		return kTopics;
 	}
diff --git a/vipra-util/src/main/java/de/vipra/util/model/Window.java b/vipra-util/src/main/java/de/vipra/util/model/Window.java
index 3039ca03..b3350267 100644
--- a/vipra-util/src/main/java/de/vipra/util/model/Window.java
+++ b/vipra-util/src/main/java/de/vipra/util/model/Window.java
@@ -26,15 +26,15 @@ public class Window implements Model<Integer>, Serializable, Comparable<Window>
 
 	public Window() {}
 
-	public Window(Integer id) {
+	public Window(final Integer id) {
 		this.id = id;
 	}
 
-	public Window(WindowFull window) {
-		this.id = window.getId();
-		this.startDate = window.getStartDate();
-		this.endDate = window.getEndDate();
-		this.windowResolution = window.getWindowResolution();
+	public Window(final WindowFull window) {
+		id = window.getId();
+		startDate = window.getStartDate();
+		endDate = window.getEndDate();
+		windowResolution = window.getWindowResolution();
 	}
 
 	@Override
-- 
GitLab