diff --git a/vipra-cmd/runcfg/CMD.launch b/vipra-cmd/runcfg/CMD.launch index 345beb2a746a7c1d1948eb7730ca56f20c95d4ca..7f3fd45a4483d2684146995337cec4f7df7d6ee9 100644 --- a/vipra-cmd/runcfg/CMD.launch +++ b/vipra-cmd/runcfg/CMD.launch @@ -11,7 +11,7 @@ </listAttribute> <stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.m2e.launchconfig.classpathProvider"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="de.vipra.cmd.Main"/> -<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-eC bbc:test"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-tdp"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="vipra-cmd"/> <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.m2e.launchconfig.sourcepathProvider"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/> 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 aa975d692b3f2dbd441b6825ad07d84b4ebb9c33..23a021709650a87db646b18c757e01f2079aeceb 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 TEST = Option.builder("t").longOpt("test").desc("test database connections").build(); public static final Option ALL = Option.builder("A").longOpt("all").desc("select all models (-S all)").build(); public static final Option VERSION = Option.builder("v").longOpt("version").desc("print the version number").build(); + public static final Option PERF = Option.builder("p").longOpt("perf").desc("generate performance statistics").hasArg().optionalArg(true) + .argName("file").build(); public static final Option INDEX = Option.builder("i").longOpt("index").desc("create index for models").hasArgs().argName("[models...]") .optionalArg(true).build(); @@ -31,7 +33,7 @@ public class CommandLineOptions { .optionalArg(true).build(); public static final Option EDIT = Option.builder("E").longOpt("edit").desc("edit config of selected models").hasArgs().argName("[models...]") .optionalArg(true).build(); - public static final Option PRINT = Option.builder("p").longOpt("print").desc("print model configuration").hasArgs().argName("[models...]") + public static final Option PRINT = Option.builder("m").longOpt("print").desc("print model configuration").hasArgs().argName("[models...]") .optionalArg(true).build(); public static final Option IMPORT = Option.builder("I").longOpt("import").desc("import data for models").hasArgs().argName("[models...]") .optionalArg(true).build(); @@ -43,8 +45,9 @@ public class CommandLineOptions { .optionalArg(true).build(); public static final Option SELECT = Option.builder("S").longOpt("select").desc("select models").hasArgs().argName("models...").build(); public static final Option BACKUP = Option.builder("B").longOpt("backup").desc("backup data/filebase").hasArg().argName("path").build(); - public static final Option RESTORE = Option.builder("R").longOpt("restore").desc("restore data/filebase").hasArg().argName("path").build(); - public static final Option LOG = Option.builder("o").longOpt("out").desc("print output to file").hasArg().optionalArg(true).build(); + public static final Option RESTORE = Option.builder("R").longOpt("restore").desc("restore data/filebase").hasArg().argName("file").build(); + public static final Option LOG = Option.builder("o").longOpt("out").desc("print output to file").hasArg().optionalArg(true).argName("file") + .build(); private final Options options; private CommandLine cmd; @@ -52,7 +55,7 @@ public class CommandLineOptions { public CommandLineOptions() { final Option[] optionsArray = { ERASE, DEBUG, HELP, INDEX, LIST, REREAD, SILENT, TEST, ALL, CREATE, DELETE, EDIT, PRINT, IMPORT, MODEL, - RENAME, CLEAR, SELECT, BACKUP, RESTORE, VERSION, LOG }; + RENAME, CLEAR, SELECT, BACKUP, RESTORE, VERSION, PERF, LOG }; options = new Options(); for (final Option option : optionsArray) options.addOption(option); @@ -112,7 +115,7 @@ public class CommandLineOptions { final String[] models = getOptionValues(INDEX); if (models != null && models.length > 0) return stripGroups(models); - return selectedModels(); + return stripGroups(selectedModels()); } public boolean isList() { @@ -127,7 +130,7 @@ public class CommandLineOptions { final String[] models = getOptionValues(REREAD); if (models != null && models.length > 0) return stripGroups(models); - return selectedModels(); + return stripGroups(selectedModels()); } public boolean isSilent() { @@ -149,7 +152,7 @@ public class CommandLineOptions { public String[] modelsToCreate() { final String[] models = getOptionValues(CREATE); if (models != null && models.length > 0) - return stripGroups(models); + return models; return selectedModels(); } @@ -161,7 +164,7 @@ public class CommandLineOptions { final String[] models = getOptionValues(DELETE); if (models != null && models.length > 0) return stripGroups(models); - return selectedModels(); + return stripGroups(selectedModels()); } public boolean isEdit() { @@ -172,7 +175,7 @@ public class CommandLineOptions { final String[] models = getOptionValues(EDIT); if (models != null && models.length > 0) return stripGroups(models); - return selectedModels(); + return stripGroups(selectedModels()); } public boolean isPrint() { @@ -183,18 +186,19 @@ public class CommandLineOptions { final String[] models = getOptionValues(PRINT); if (models != null && models.length > 0) return stripGroups(models); - return selectedModels(); + return stripGroups(selectedModels()); } public boolean isImport() { return hasOption(IMPORT); } + public String[] modelsForImport() { + return stripGroups(selectedModels()); + } + public String[] filesToImport() { - final String[] models = getOptionValues(IMPORT); - if (models != null && models.length > 0) - return stripGroups(models); - return selectedModels(); + return getOptionValues(IMPORT); } public boolean isModel() { @@ -205,7 +209,7 @@ public class CommandLineOptions { final String[] models = getOptionValues(MODEL); if (models != null && models.length > 0) return stripGroups(models); - return selectedModels(); + return stripGroups(selectedModels()); } public boolean isSelect() { @@ -220,7 +224,7 @@ public class CommandLineOptions { models = getOptionValues(SELECT); if (models != null && models.length > 0) - return stripGroups(models); + return models; throw new RuntimeException("select at least one model"); } @@ -241,6 +245,14 @@ public class CommandLineOptions { return getOptionValue(RESTORE); } + public boolean isPerf() { + return hasOption(PERF); + } + + public String perfPath() { + return getOptionValue(PERF); + } + public boolean isVersion() { return hasOption(VERSION); } @@ -253,7 +265,7 @@ public class CommandLineOptions { final String[] models = getOptionValues(RENAME); if (models != null && models.length > 0) return stripGroups(models); - return selectedModels(); + return stripGroups(selectedModels()); } public boolean isClear() { @@ -264,7 +276,7 @@ public class CommandLineOptions { final String[] models = getOptionValues(CLEAR); if (models != null && models.length > 0) return stripGroups(models); - return selectedModels(); + return stripGroups(selectedModels()); } public boolean isLog() { 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 34a78f76d74b32a79285f01b17deb4937a1da64d..85315441c3f7237c709361a8f18ea07daa9b0dcf 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/Main.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/Main.java @@ -28,19 +28,19 @@ import de.vipra.cmd.option.VersionCommand; import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; import de.vipra.util.LockFile; +import de.vipra.util.Statistics; import de.vipra.util.ex.LockFileException; public class Main { private static final LockFile lockFile = new LockFile(); + public static Statistics stats = new Statistics(); static { // set morphia log level MorphiaLoggerFactory.registerLogger(SLF4JLoggerImplFactory.class); - // set corenlp log level, close stderr to mute corenlp messages System.err.close(); - } private static void lock() { @@ -62,6 +62,7 @@ public class Main { } public static void main(final String[] args) { + final CommandLineOptions opts = new CommandLineOptions(); try { opts.parse(args); @@ -76,6 +77,9 @@ public class Main { return; } + if (opts.isPerf()) + stats.begin(); + // logger configuration ConsoleUtils.setSilent(opts.isSilent()); @@ -97,50 +101,54 @@ public class Main { final List<Command> commands = new ArrayList<>(); - if (opts.isVersion()) - commands.add(new VersionCommand()); + try { + if (opts.isVersion()) + commands.add(new VersionCommand()); - if (opts.isTest()) - commands.add(new TestCommand()); + if (opts.isTest()) + commands.add(new TestCommand()); - if (opts.isBackup()) - commands.add(new BackupCommand(opts.backupPath())); + if (opts.isBackup()) + commands.add(new BackupCommand(opts.backupPath())); - if (opts.isRestore()) - commands.add(new RestoreCommand(opts.restorePath())); + if (opts.isRestore()) + commands.add(new RestoreCommand(opts.restorePath())); - if (opts.isErase()) - commands.add(new EraseCommand()); + if (opts.isErase()) + commands.add(new EraseCommand()); - if (opts.isClear()) - commands.add(new ClearModelCommand(opts.modelsToClear())); + if (opts.isClear()) + commands.add(new ClearModelCommand(opts.modelsToClear())); - if (opts.isDelete()) - commands.add(new DeleteModelCommand(opts.modelsToDelete())); + if (opts.isDelete()) + commands.add(new DeleteModelCommand(opts.modelsToDelete())); - if (opts.isCreate()) - commands.add(new CreateModelCommand(opts.modelsToCreate())); + if (opts.isCreate()) + commands.add(new CreateModelCommand(opts.modelsToCreate())); - if (opts.isRename()) - commands.add(new RenameModelCommand(opts.modelsToRename())); + if (opts.isRename()) + commands.add(new RenameModelCommand(opts.modelsToRename())); - if (opts.isList()) - commands.add(new ListModelsCommand()); + if (opts.isList()) + commands.add(new ListModelsCommand()); - if (opts.isEdit()) - commands.add(new EditModelCommand(opts.modelsToEdit())); + if (opts.isEdit()) + commands.add(new EditModelCommand(opts.modelsToEdit())); - if (opts.isPrint()) - commands.add(new PrintModelCommand(opts.modelsToPrint())); + if (opts.isPrint()) + commands.add(new PrintModelCommand(opts.modelsToPrint())); - if (opts.isImport()) - commands.add(new ImportCommand(opts.selectedModels(), opts.filesToImport())); + if (opts.isImport()) + commands.add(new ImportCommand(opts.modelsForImport(), opts.filesToImport())); - if (opts.isModel() || opts.isReread()) - commands.add(new ModelingCommand(opts.selectedModels(), opts.isReread())); + if (opts.isModel() || opts.isReread()) + commands.add(new ModelingCommand(opts.modelsToModel(), opts.isReread())); - if (opts.isIndex()) - commands.add(new IndexingCommand(opts.selectedModels())); + if (opts.isIndex()) + commands.add(new IndexingCommand(opts.modelsToIndex())); + } catch (final Exception e) { + ConsoleUtils.error(e.getMessage()); + } // run @@ -153,6 +161,7 @@ public class Main { locked = true; } try { + Main.stats.start(c.getName()); c.run(); } catch (final Exception e) { final Throwable cause = e.getCause(); @@ -162,6 +171,8 @@ public class Main { ConsoleUtils.error(e.getMessage()); if (opts.isDebug() && !opts.isSilent()) e.printStackTrace(System.out); + } finally { + Main.stats.stop(c.getName()); } } } else { @@ -169,6 +180,14 @@ public class Main { } unlock(); + + if (opts.isPerf()) { + try { + stats.finish(opts.perfPath()); + } catch (final IOException e) { + ConsoleUtils.error("could not write statistics: " + e.getMessage()); + } + } } } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/BackupCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/BackupCommand.java index d7981dd179a8e2206a3674952cc95ad8b1ad54a5..316fe489c492c2b702c31d95cd4a6a70cc63dcdd 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/BackupCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/BackupCommand.java @@ -7,6 +7,7 @@ import org.fusesource.jansi.Ansi; import org.fusesource.jansi.Ansi.Color; import org.zeroturnaround.zip.ZipUtil; +import de.vipra.cmd.Main; import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; import de.vipra.util.FileUtils; @@ -24,27 +25,31 @@ public class BackupCommand implements Command { @Override public void run() throws Exception { try { + Main.stats.start("backup"); final Timer timer = new Timer(); - ConsoleUtils.info("creating backup"); final Config config = Config.getConfig(); final File tmpTarget = FileUtils.getTempFile("vipra-dump"); org.apache.commons.io.FileUtils.deleteDirectory(tmpTarget); + Main.stats.start("backup.database"); ConsoleUtils.infoNOLF(" " + ConsoleUtils.PATH_T + " backup database..."); final Process p = Runtime.getRuntime().exec("mongodump -d " + config.getDatabaseName() + " -h " + config.getDatabaseHost() + " --port " + config.getDatabasePort() + " -o " + new File(tmpTarget, "db")); p.waitFor(); ConsoleUtils.print(Ansi.ansi().fg(Color.GREEN).a("OK").reset().toString()); + Main.stats.next("backup.database", "backup.filebase"); ConsoleUtils.infoNOLF(" " + ConsoleUtils.PATH_T + " backup filebase..."); org.apache.commons.io.FileUtils.copyDirectory(config.getDataDirectory(), new File(tmpTarget, "fb")); ConsoleUtils.print(Ansi.ansi().fg(Color.GREEN).a("OK").reset().toString()); + Main.stats.next("backup.filebase", "backup.configuration"); ConsoleUtils.infoNOLF(" " + ConsoleUtils.PATH_T + " backup configuration..."); org.apache.commons.io.FileUtils.copyDirectory(Config.getGenericConfigDir(), new File(tmpTarget, "config")); ConsoleUtils.print(Ansi.ansi().fg(Color.GREEN).a("OK").reset().toString()); + Main.stats.next("backup.configuration", "backup.compress"); ConsoleUtils.infoNOLF(" " + ConsoleUtils.PATH_L + " compressing..."); File target = new File(path); if (target.exists() && target.isDirectory()) @@ -55,6 +60,7 @@ public class BackupCommand implements Command { ConsoleUtils.info("saved to file: " + target.getAbsolutePath()); ConsoleUtils.info("done in " + StringUtils.timeString(timer.total())); + Main.stats.stop("backup.compress"); } catch (final Exception e) { ConsoleUtils.print(Ansi.ansi().fg(Color.RED).a("FAILED").reset().toString()); if (e.getMessage().contains("mongodump")) { @@ -71,4 +77,9 @@ public class BackupCommand implements Command { return true; } + @Override + public String getName() { + return "backup"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/ClearModelCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/ClearModelCommand.java index d9b8ad9122fea1d727a0f331376523b6c6e95152..a41e96975009c41fd132116e436e2d03be47ef78 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/ClearModelCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/ClearModelCommand.java @@ -7,6 +7,7 @@ import java.util.List; import org.apache.commons.io.FileUtils; import org.bson.types.ObjectId; +import de.vipra.cmd.Main; import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; import de.vipra.util.model.ArticleFull; @@ -42,6 +43,7 @@ public class ClearModelCommand implements Command { final List<TopicModelFull> topicModels = dbTopicModels.getMultiple(QueryBuilder.builder().in("name", Arrays.asList(names)).allFields()); for (final TopicModelFull topicModel : topicModels) { + Main.stats.start("clear." + topicModel.getName()); final File modelDir = new File(config.getDataDirectory(), topicModel.getName()); final QueryBuilder builder = QueryBuilder.builder().eq("topicModel", new TopicModel(topicModel.getId())); @@ -64,6 +66,7 @@ public class ClearModelCommand implements Command { topicModel.setWordCount(0L); ConsoleUtils.info("model cleared: " + topicModel.getName()); + Main.stats.stop("clear." + topicModel.getName()); } dbTopicModels.replaceMultiple(topicModels); @@ -74,4 +77,9 @@ public class ClearModelCommand implements Command { return true; } + @Override + public String getName() { + return "clear"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/Command.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/Command.java index 48af4869f603f45cb26506fae08562c2582ea839..49f51b7390beb12a6e346d6e51ed96e99351443c 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/Command.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/Command.java @@ -6,4 +6,6 @@ public interface Command { public boolean requiresLock(); + public String getName(); + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/CreateModelCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/CreateModelCommand.java index ba1bc1f0d1b8ba871cb8ea5c1867b6ead423a78d..14df7a19542cedd76f590774ee1757e7efd361db 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/CreateModelCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/CreateModelCommand.java @@ -9,6 +9,7 @@ import org.bson.types.ObjectId; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; +import de.vipra.cmd.Main; import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; import de.vipra.util.ex.DatabaseException; @@ -57,6 +58,7 @@ public class CreateModelCommand implements Command { } for (final String name : names) { + Main.stats.start("create." + name); final String msg = config.validModelName(name); if (msg != null) throw new Exception(msg); @@ -69,6 +71,7 @@ public class CreateModelCommand implements Command { final TopicModelFull topicModel = createModel(name, modelConfig, modelDir); ConsoleUtils .info("model created: " + topicModel.getName() + (topicModel.getGroup() == null ? "" : " in group: " + topicModel.getGroup())); + Main.stats.stop("create." + name); } } @@ -77,4 +80,9 @@ public class CreateModelCommand implements Command { return false; } + @Override + public String getName() { + return "create"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/DeleteModelCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/DeleteModelCommand.java index 0d33b269c5c126e69c14915d7633789194bd9bd2..c958ce9d9085ba3c1781c7b5b5c3d24844ca39fe 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/DeleteModelCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/DeleteModelCommand.java @@ -7,6 +7,7 @@ import java.util.stream.Collectors; import org.bson.types.ObjectId; +import de.vipra.cmd.Main; import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; import de.vipra.util.model.ArticleFull; @@ -42,6 +43,7 @@ public class DeleteModelCommand implements Command { final List<TopicModelFull> topicModels = dbTopicModels.getMultiple(QueryBuilder.builder().in("name", Arrays.asList(names))); for (final TopicModelFull topicModel : topicModels) { + Main.stats.start("delete." + topicModel.getName()); final File modelDir = new File(config.getDataDirectory(), topicModel.getName()); final QueryBuilder builder = QueryBuilder.builder().eq("topicModel", new TopicModel(topicModel.getId())); @@ -66,6 +68,7 @@ public class DeleteModelCommand implements Command { if (deleted > 0) ConsoleUtils.info("model deleted: " + topicModel.getName()); + Main.stats.stop("delete." + topicModel.getName()); } dbTopicModels.deleteMultiple(topicModels.stream().map((t) -> t.getId()).collect(Collectors.toList())); @@ -76,4 +79,9 @@ public class DeleteModelCommand implements Command { return true; } + @Override + public String getName() { + return "delete"; + } + } 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 index 600899af46f9373c7d12aaf617b3976394eb47c3..2cb8e1bc21ddca4df208409ef41cf2df571b83d7 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/EditModelCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/EditModelCommand.java @@ -9,6 +9,7 @@ import org.bson.types.ObjectId; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; +import de.vipra.cmd.Main; import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; import de.vipra.util.Constants.WindowResolution; @@ -31,6 +32,7 @@ public class EditModelCommand implements Command { private void editModel(final TopicModelFull topicModel) throws DatabaseException, JsonGenerationException, JsonMappingException, IOException, ConfigException { + Main.stats.start("edit." + topicModel.getName()); ConsoleUtils.info("editing model: " + topicModel.getName()); final TopicModelConfig topicModelConfig = topicModel.getModelConfig(); @@ -79,6 +81,7 @@ public class EditModelCommand implements Command { dbTopicModels.updateSingle(topicModel, "modelConfig"); topicModelConfig.saveToFile(topicModelConfig.getModelDir(config.getDataDirectory())); config.setTopicModelConfig(topicModelConfig); + Main.stats.stop("edit." + topicModel.getName()); } @Override @@ -98,4 +101,9 @@ public class EditModelCommand implements Command { return true; } + @Override + public String getName() { + return "edit"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/EraseCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/EraseCommand.java index edde5a1492331d838f408c9e5a2cec98f934d1a9..8327a409a3ddb890aee3828e0c3259b37d4489a7 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/EraseCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/EraseCommand.java @@ -48,4 +48,9 @@ public class EraseCommand implements Command { return true; } + @Override + public String getName() { + return "erase"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java index 825364be0e87816a8c48fcba57e5a77fa113e79f..427a830610a7b097ffb4a9a539a5a2627426f3d3 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.regex.Pattern; @@ -17,6 +18,7 @@ import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import de.vipra.cmd.Main; import de.vipra.cmd.file.Filebase; import de.vipra.cmd.file.FilebaseException; import de.vipra.cmd.file.FilebaseWindowIndex; @@ -228,12 +230,21 @@ public class ImportCommand implements Command { } } + // insert word links for (final ArticleWord word : processedText.getArticleWords()) { if (blockedWords.contains(word.getWord())) continue; articleText = articleText.replaceAll("(?i)\\b(" + word.getWord() + ")\\b(?![^<]*>|[^<>]*</)", word.aTag("$1")); } + // insert more word links + for (final Map.Entry<String, String> entry : processedText.getLemmas().entrySet()) { + if (blockedWords.contains(entry.getKey().toLowerCase())) + continue; + articleText = articleText.replaceAll("(?i)\\b(" + entry.getKey() + ")\\b(?![^<]*>|[^<>]*</)", + ArticleWord.aTag(entry.getValue(), "$1")); + } + article.setProcessedText(processedText.getWords()); article.setWords(articleWords); article.setTopicModel(new TopicModel(topicModelFull.getId())); @@ -306,6 +317,7 @@ public class ImportCommand implements Command { } private void importForModel(final TopicModelConfig modelConfig) throws Exception { + Main.stats.start("import." + modelConfig.getName()); ConsoleUtils.info("importing for model: " + modelConfig.getName()); this.modelConfig = modelConfig; @@ -391,6 +403,7 @@ public class ImportCommand implements Command { * run information */ ConsoleUtils.info("done in " + StringUtils.timeString(timer.total())); + Main.stats.stop("import." + modelConfig.getName()); } @Override @@ -411,4 +424,9 @@ public class ImportCommand implements Command { return true; } + @Override + public String getName() { + return "import"; + } + } \ No newline at end of file diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/IndexingCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/IndexingCommand.java index f929eb0c538b117b104e4562cf1ba2f5c2354402..92710d5a4015ee732500c5344a8ba1854f718e41 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/IndexingCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/IndexingCommand.java @@ -9,6 +9,7 @@ import org.bson.types.ObjectId; import org.elasticsearch.client.Client; import org.elasticsearch.index.IndexNotFoundException; +import de.vipra.cmd.Main; import de.vipra.cmd.file.FilebaseIDDateIndex; import de.vipra.cmd.file.FilebaseIDDateIndexEntry; import de.vipra.util.Config; @@ -40,6 +41,7 @@ public class IndexingCommand implements Command { } private void indexForModel(final TopicModelConfig modelConfig) throws ParseException, IOException, ConfigException, DatabaseException { + Main.stats.start("indexing." + modelConfig.getName()); ConsoleUtils.info("indexing for model: " + modelConfig.getName()); final Timer timer = new Timer(); @@ -79,6 +81,7 @@ public class IndexingCommand implements Command { // run information ConsoleUtils.info("done in " + StringUtils.timeString(timer.total())); + Main.stats.stop("indexing." + modelConfig.getName()); } @Override @@ -98,4 +101,9 @@ public class IndexingCommand implements Command { return true; } + @Override + public String getName() { + return "indexing"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/ListModelsCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/ListModelsCommand.java index 4852d0e6f5a0c22333def610f9cf3ff1217b2373..c899a979326d816daff6b3eba6a76486cdabecdc 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/ListModelsCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/ListModelsCommand.java @@ -28,4 +28,9 @@ public class ListModelsCommand implements Command { return false; } + @Override + public String getName() { + return "list"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/ModelingCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/ModelingCommand.java index 8340d85fcddd1fa30f53e97bce23ffa953851bb0..f2c95e6397c907be577f27ad35d6737d2342ceb2 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/ModelingCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/ModelingCommand.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.text.ParseException; import java.util.function.Consumer; +import de.vipra.cmd.Main; import de.vipra.cmd.lda.Analyzer; import de.vipra.cmd.lda.AnalyzerException; import de.vipra.util.Config; @@ -27,6 +28,7 @@ public class ModelingCommand implements Command { private void modelForModel(final TopicModelConfig modelConfig, final Consumer<Double> progressCallback) throws AnalyzerException, ConfigException, DatabaseException, ParseException, IOException, InterruptedException { + Main.stats.start("modeling." + modelConfig.getName()); if (reread) ConsoleUtils.info("rereading model: " + modelConfig.getName()); else @@ -48,6 +50,7 @@ public class ModelingCommand implements Command { * run information */ ConsoleUtils.info("done in " + StringUtils.timeString(timer.total())); + Main.stats.stop("modeling." + modelConfig.getName()); } @Override @@ -67,4 +70,9 @@ public class ModelingCommand implements Command { return true; } + @Override + public String getName() { + return "modeling"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/PrintModelCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/PrintModelCommand.java index b335c621ad4b032dac2558134fe47bf5d190ea90..1c255c8ad0d16152f707ab77846f35e17d45a6bb 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/PrintModelCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/PrintModelCommand.java @@ -31,4 +31,9 @@ public class PrintModelCommand implements Command { return false; } + @Override + public String getName() { + return "print"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/RenameModelCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/RenameModelCommand.java index dac38bc986bbf12ab5271c0c540e5288d4dc3b6d..231c584f96c3219845ff1fd47b713285f0f73a7c 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/RenameModelCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/RenameModelCommand.java @@ -66,4 +66,9 @@ public class RenameModelCommand implements Command { return false; } + @Override + public String getName() { + return "rename"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/RestoreCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/RestoreCommand.java index 20371031510ae095e9609ea27f5785a6d02ab5eb..d21276237095992b095226d563cccf962f2b42bc 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/RestoreCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/RestoreCommand.java @@ -7,6 +7,7 @@ import org.fusesource.jansi.Ansi; import org.fusesource.jansi.Ansi.Color; import org.zeroturnaround.zip.ZipUtil; +import de.vipra.cmd.Main; import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; import de.vipra.util.FileUtils; @@ -27,6 +28,7 @@ public class RestoreCommand implements Command { final Timer timer = new Timer(); ConsoleUtils.info("restoring from file"); + Main.stats.start("restore.unzip"); final File zip = new File(path); if (!zip.isFile()) throw new FileNotFoundException(path); @@ -34,20 +36,24 @@ public class RestoreCommand implements Command { ZipUtil.unpack(zip, tmpTarget); final Config config = Config.getConfig(); + Main.stats.next("restore.unzip", "restore.database"); ConsoleUtils.infoNOLF(" " + ConsoleUtils.PATH_T + " restore database..."); final Process p = Runtime.getRuntime().exec( "mongorestore --drop -h " + config.getDatabaseHost() + " --port " + config.getDatabasePort() + " " + new File(tmpTarget, "db")); p.waitFor(); ConsoleUtils.print(Ansi.ansi().fg(Color.GREEN).a("OK").reset().toString()); + Main.stats.next("restore.database", "restore.filebase"); ConsoleUtils.infoNOLF(" " + ConsoleUtils.PATH_T + " restore filebase..."); org.apache.commons.io.FileUtils.copyDirectory(new File(tmpTarget, "fb"), config.getDataDirectory()); ConsoleUtils.print(Ansi.ansi().fg(Color.GREEN).a("OK").reset().toString()); + Main.stats.next("restore.filebase", "restore.configuration"); ConsoleUtils.infoNOLF(" " + ConsoleUtils.PATH_L + " restore configuration..."); org.apache.commons.io.FileUtils.copyDirectory(new File(tmpTarget, "config"), Config.getGenericConfigDir()); ConsoleUtils.print(Ansi.ansi().fg(Color.GREEN).a("OK").reset().toString()); + Main.stats.stop("restore.configuration"); org.apache.commons.io.FileUtils.deleteDirectory(tmpTarget); ConsoleUtils.info("restored"); @@ -68,4 +74,9 @@ public class RestoreCommand implements Command { return true; } + @Override + public String getName() { + return "restore"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/TestCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/TestCommand.java index 04fef5075bc43608f5a4a93ed94c0ef69d22394b..14455611bf62a0d4dbd036f4a77137a5eaef144e 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/TestCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/TestCommand.java @@ -11,6 +11,7 @@ import org.elasticsearch.client.transport.TransportClient; import org.fusesource.jansi.Ansi; import org.fusesource.jansi.Ansi.Color; +import de.vipra.cmd.Main; import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; import de.vipra.util.ESClient; @@ -22,6 +23,7 @@ public class TestCommand implements Command { @Override public void run() throws Exception { try { + Main.stats.start("test command"); ConsoleUtils.info("testing system"); // test if configuration readable @@ -67,6 +69,8 @@ public class TestCommand implements Command { } catch (final Exception e) { ConsoleUtils.print(Ansi.ansi().fg(Color.RED).a("FAILED").reset().toString()); throw e; + } finally { + Main.stats.stop("test command"); } } @@ -75,4 +79,9 @@ public class TestCommand implements Command { return false; } + @Override + public String getName() { + return "test"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/VersionCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/VersionCommand.java index 44bddcac6928d0a977f358910987ff8f6f50d638..7286d5c93b2561f694e4792a62801f2468341057 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/VersionCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/VersionCommand.java @@ -26,4 +26,9 @@ public class VersionCommand implements Command { return false; } + @Override + public String getName() { + return "version"; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/text/ProcessedText.java b/vipra-cmd/src/main/java/de/vipra/cmd/text/ProcessedText.java index 8646b9c97428855bbc1a7ee23e35f20bca619154..d18bc7b4405a721d85f2fa5a731347235b07b8b8 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/text/ProcessedText.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/text/ProcessedText.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import de.vipra.util.CountMap; @@ -17,8 +18,9 @@ public class ProcessedText { private final double reductionRatio; private final CountMap<String> wordCounts; private final List<ArticleWord> articleWords; + private final Map<String, String> lemmas; - public ProcessedText(final String text, final long wordCount) { + public ProcessedText(final String text, final long wordCount, final Map<String, String> lemmas) { final String[] allWords = text.toLowerCase().trim().split("\\s+"); final List<String> wordList = new ArrayList<>(allWords.length); for (final String word : allWords) @@ -28,6 +30,7 @@ public class ProcessedText { originalWordCount = wordCount; reducedWordCount = words.length; reductionRatio = 1 - ((double) reducedWordCount / wordCount); + this.lemmas = lemmas; wordCounts = new CountMap<>(); for (final String word : words) @@ -63,4 +66,8 @@ public class ProcessedText { return wordCounts; } + public Map<String, String> getLemmas() { + return lemmas; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/text/Processor.java b/vipra-cmd/src/main/java/de/vipra/cmd/text/Processor.java index 41723aa8e8ea1bd3b9731435a4460ee54cad0e1f..115502257cfa7234b601f4bac38845d33ea36dc1 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/text/Processor.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/text/Processor.java @@ -1,6 +1,8 @@ package de.vipra.cmd.text; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import de.vipra.util.Constants; @@ -35,9 +37,10 @@ public class Processor { } public ProcessedText process(final TopicModelConfig modelConfig, final String input) throws ProcessorException { - final Annotation doc = new Annotation(input.toLowerCase()); + final Annotation doc = new Annotation(input); nlp.annotate(doc); final StringBuilder sb = new StringBuilder(); + final Map<String, String> lemmas = new HashMap<>(); long wordCount = 0; // loop sentences for (final CoreMap sentence : doc.get(SentencesAnnotation.class)) { @@ -53,15 +56,20 @@ public class Processor { final Long count = word.get(FrequencyAnnotator.class); if (count != null && count >= modelConfig.getDocumentMinimumWordFrequency()) { final String lemma = word.get(LemmaAnnotation.class); - // collect unique words - sb.append(lemma).append(" "); + if (lemma != null) { + // collect unique words + sb.append(lemma.toLowerCase()).append(" "); + + if (!lemma.equals(word.word())) + lemmas.put(word.word(), lemma); + } } } } } final String text = clean(sb.toString()); - return new ProcessedText(text, wordCount); + return new ProcessedText(text, wordCount, lemmas); } public static String clean(final String in) { diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/text/StopwordsAnnotator.java b/vipra-cmd/src/main/java/de/vipra/cmd/text/StopwordsAnnotator.java index b977de033e3a2a91e5b24d0993f1e359158c07f9..2a4c19856e5bc291688548246e6bdd659753aa25 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/text/StopwordsAnnotator.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/text/StopwordsAnnotator.java @@ -20,7 +20,7 @@ public class StopwordsAnnotator implements Annotator, CoreAnnotation<Boolean> { private final Set<String> stopWords; public StopwordsAnnotator(final String input, final Properties props) { - stopWords = new HashSet<String>(Arrays.asList(props.getProperty(NAME).split(" "))); + stopWords = new HashSet<>(Arrays.asList(props.getProperty(NAME).split(" "))); stopWords.addAll(Arrays.asList("-LRB-", "-RRB-", "-LSB-", "-RSB-", "-LCB-", "-RCB-")); } diff --git a/vipra-ui/app/html/articles/show.html b/vipra-ui/app/html/articles/show.html index 1bce4b665b89a6a7389d11c54401bb5f49abf632..26513b5e6b84e84b9009ba867cb8b2e1d7e4b26d 100644 --- a/vipra-ui/app/html/articles/show.html +++ b/vipra-ui/app/html/articles/show.html @@ -99,7 +99,13 @@ </table> <p class="text-muted" ng-hide="article.similarArticles.length">No similar articles</p> <hr> - <div class="text-justify" ng-bind-html="::article.text"></div> + <div class="pull-right hide-links-button" ng-click="articlesShowModels.hideLinks=!articlesShowModels.hideLinks"> + <span class="fa-stack"> + <i class="fa fa-link fa-stack-1x"></i> + <i class="fa fa-ban fa-stack-2x text-danger" ng-show="!articlesShowModels.hideLinks"></i> + </span> + </div> + <div class="text-justify" ng-bind-html="::article.text" ng-class="{'hide-links':articlesShowModels.hideLinks}"></div> </div> </div> <div class="loading" ng-hide="article">Loading...</div> diff --git a/vipra-ui/app/html/directives/topicmodel-button.html b/vipra-ui/app/html/directives/topicmodel-button.html new file mode 100644 index 0000000000000000000000000000000000000000..9e45cceb1cd49b22b12ebd0df7525dbfbda246f5 --- /dev/null +++ b/vipra-ui/app/html/directives/topicmodel-button.html @@ -0,0 +1,10 @@ +<button type="button" class="list-group-item topic-model" ng-click="changeTopicModel(topicModel)" ng-class="{'active selected-model':rootModels.topicModel.id===topicModel.id}" analytics-on analytics-event="Topic model" analytics-category="Topic model actions"> + <span class="badge badge-group"> + <span class="badge-part" ng-if="!topicModel.lastGenerated" title="Model was never generated">Non-generated</span> + <span class="badge-part" ng-bind="::topicModel.articleCount" ng-show="topicModel.articleCount" ng-attr-title="{{::topicModel.articleCount + ' article(s)'}}" ng-cloak></span> + <span class="badge-part" ng-bind="::topicModel.topicCount" ng-show="topicModel.topicCount" ng-attr-title="{{::topicModel.topicCount + ' topic(s)'}}" ng-cloak></span> + </span> + <span ng-bind="::topicModel.name"></span> + <br ng-show="topicModel.modelConfig.description" ng-cloak> + <small ng-bind="::topicModel.modelConfig.description"></small> +</button> \ No newline at end of file diff --git a/vipra-ui/app/index.html b/vipra-ui/app/index.html index 5286a53a7f863c41e0e197d48072377d8607a77f..91122d3fce881df0f9a27164fe9a719fc379df51 100644 --- a/vipra-ui/app/index.html +++ b/vipra-ui/app/index.html @@ -93,22 +93,26 @@ <input tabindex="0" type="text" class="form-control" placeholder="Filter..." ng-model="rootModels.topicModelFilter"> <i class="form-control-feedback glyphicon glyphicon-search text-muted"></i> </div> - <div class="list-group nomargin" ng-show="topicModels.length" ng-cloak> - <button type="button" class="list-group-item topic-model" ng-repeat="topicModel in topicModels | filter:{name:rootModels.topicModelFilter}" ng-click="changeTopicModel(topicModel)" ng-class="{'active selected-model':rootModels.topicModel.id===topicModel.id}" analytics-on analytics-event="Topic model" analytics-category="Topic model actions"> - <span class="badge badge-group"> - <span class="badge-part" ng-if="!topicModel.lastGenerated" title="Model was never generated">Non-generated</span> - <span class="badge-part" ng-bind="::topicModel.articleCount" ng-show="topicModel.articleCount" ng-attr-title="{{::topicModel.articleCount + ' article(s)'}}" ng-cloak></span> - <span class="badge-part" ng-bind="::topicModel.topicCount" ng-show="topicModel.topicCount" ng-attr-title="{{::topicModel.topicCount + ' topic(s)'}}" ng-cloak></span> - </span> - <span ng-bind="::topicModel.name"></span> - <br ng-show="topicModel.modelConfig.description" ng-cloak> - <small ng-bind="::topicModel.modelConfig.description"></small> - </button> + <div class="panel panel-default" ng-repeat="(groupName, group) in groups" ng-show="filtered.length"> + <div class="panel-heading pointer" ng-click="group.collapsed=!group.collapsed"> + <i class="fa folder-icon" ng-class="{'fa-folder-open-o':!group.collapsed,'fa-folder-o':group.collapsed}"></i> + <span ng-bind="::groupName"></span> + <div class="pull-right"> + <span class="badge" ng-bind="::group.length" ng-attr-title="{{::group.length}} topic model(s)"></span> + <i class="fa fa-chevron-down pointer"></i> + </div> + </div> + <div ng-attr-id="{{::groupName}}" class="list-group nomargin collapse in" ng-show="topicModelsCount && !group.collapsed" ng-cloak> + <topicmodel-button ng-repeat="topicModel in filtered = (group | filter:{name:rootModels.topicModelFilter})"></topicmodel-button> + </div> + </div> + <div class="list-group nomargin" ng-show="topicModelsCount" ng-cloak> + <topicmodel-button ng-repeat="topicModel in topicModels | filter:{name:rootModels.topicModelFilter}"></topicmodel-button> </div> <p class="text-center" ng-show="loading.any" ng-cloak> Loading... </p> - <p ng-hide="topicModels.length || loading.any"> + <p ng-hide="topicModelsCount || loading.any"> No topic models in the database. Create a topic model and import data into it to begin. </p> </div> diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js index b39b4bed23639b979c5ab5be1bb08e88ec07976e..372fae3248284d971ea90f42fc991e9f9bc098c6 100644 --- a/vipra-ui/app/js/controllers.js +++ b/vipra-ui/app/js/controllers.js @@ -50,11 +50,23 @@ TopicModelFactory.query({ fields: '_all' }, function(data) { + var groups = {}; + var models = []; for(var i = 0; i < data.length; i++) { data[i].lastGeneratedString = data[i].lastGenerated ? Vipra.formatDateTime(data[i].lastGenerated) : 'never'; data[i].lastIndexedString = data[i].lastIndexed ? Vipra.formatDateTime(data[i].lastIndexed) : 'never'; + if(data[i].group) { + if(!groups.hasOwnProperty(data[i].group)) + groups[data[i].group] = [data[i]]; + else + groups[data[i].group].push(data[i]); + } else { + models.push(data[i]); + } } - $scope.topicModels = data; + $scope.groups = groups; + $scope.topicModels = models; + $scope.topicModelsCount = data.length; }); }; @@ -1256,7 +1268,7 @@ $scope.prepareText = function(text) { var entityBase = $state.href('entities', {}, {absolute: true}), wordBase = $state.href('words', {}, {absolute: true}); - return text.replace(/data-entity="([^"]*)"/g, 'href="' + entityBase + '/$1"').replace(/data-word="([^"]*)"/g, 'href="' + wordBase + '/$1"'); + return text.replace(/<e=([^>]*)/g, '<a href="' + entityBase + '/$1"').replace(/<w=([^>]*)/g, '<a href="' + wordBase + '/$1"'); }; var topicShareChartElement = $('#topic-share'); diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js index 2c59a6159418c53bf263934cd98bf88ae50dd850..9370e889bfc8a14f891c9991d7759a46a2f14ba4 100644 --- a/vipra-ui/app/js/directives.js +++ b/vipra-ui/app/js/directives.js @@ -571,16 +571,10 @@ }; }]); - app.directive('compileHtml', ['$compile', function($compile) { + app.directive('topicmodelButton', [function() { return { - link: function($scope, $elem, $attrs) { - $scope.$watch(function () { - return $scope.$eval($attrs.compileHtml); - }, function (value) { - $elem.html(value); - $compile($elem.contents())($scope); - }); - } + replace: true, + templateUrl: 'html/directives/topicmodel-button.html' }; }]); diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less index f767b795d3a85673a13b194f99c255043470f783..f61828b4c53c6646bfa92dd7daf398dba28d0e47 100644 --- a/vipra-ui/app/less/app.less +++ b/vipra-ui/app/less/app.less @@ -951,6 +951,7 @@ entity-menu { .article-dropdown { position: relative; + padding-left: 10px; } .topic-badge { @@ -1065,6 +1066,25 @@ entity-menu { display: inline-block; } +.folder-icon { + width: 15px; +} + +.hide-links { + a { + color: #333; + } +} + +.hide-links-button { + cursor: pointer; + margin-top: -34px; + border: 1px solid #eee; + border-radius: 5px; + background: #fff; + padding: 0 5px; +} + [ng\:cloak], [ng-cloak], .ng-cloak { display: none !important; } diff --git a/vipra-util/src/main/java/de/vipra/util/Config.java b/vipra-util/src/main/java/de/vipra/util/Config.java index a5aec3b052e7b4dafb5c89d53ad5288927c2f3d6..7c7fe7e08bce278382310456ef247c9a27ab07c7 100644 --- a/vipra-util/src/main/java/de/vipra/util/Config.java +++ b/vipra-util/src/main/java/de/vipra/util/Config.java @@ -203,13 +203,24 @@ public class Config { return new File(base, Constants.FILEBASE_DIR); } + public static File getGenericStatsDir() { + final File dir = new File(getGenericLogDir(), "vipra-stats"); + if (!dir.exists()) + dir.mkdirs(); + return dir; + } + + public static File getGenericLogDir() { + return PathUtils.tempDir(); + } + /** - * Returns a generic logging directory + * Returns a generic logging file * - * @return generic logging directory + * @return generic logging file */ public static File getGenericLogFile() { - final File base = PathUtils.tempDir(); + final File base = getGenericLogDir(); return new File(base, Constants.LOG_FILE); } diff --git a/vipra-util/src/main/java/de/vipra/util/MathUtils.java b/vipra-util/src/main/java/de/vipra/util/MathUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..5c2eecdff8dda9de81f3401f5ac6c59ac462dd3b --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/MathUtils.java @@ -0,0 +1,44 @@ +package de.vipra.util; + +import java.util.Collection; +import java.util.List; + +public class MathUtils { + + public static Double mean(final Collection<Long> numbers) { + double sum = 0; + for (final Long l : numbers) + sum += l; + return sum / numbers.size(); + } + + public static Double median(final List<Long> numbers) { + final int size = numbers.size(); + final int middle = size / 2; + if (size % 2 == 1) { + return (double) numbers.get(middle); + } else { + return (numbers.get(middle - 1) + numbers.get(middle)) / 2.0; + } + } + + public static long mode(final List<Long> numbers) { + long maxValue = 0; + long maxCount = 0; + + for (int i = 0; i < numbers.size(); ++i) { + int count = 0; + for (int j = 0; j < numbers.size(); ++j) { + if (numbers.get(j) == numbers.get(i)) + ++count; + } + if (count > maxCount) { + maxCount = count; + maxValue = numbers.get(i); + } + } + + return maxValue; + } + +} diff --git a/vipra-util/src/main/java/de/vipra/util/MongoUtils.java b/vipra-util/src/main/java/de/vipra/util/MongoUtils.java index ad076611f5de8385c88541cba323f103d8a72e19..6aa2984a8e45db5c861560fddc7ec73ca31253c3 100644 --- a/vipra-util/src/main/java/de/vipra/util/MongoUtils.java +++ b/vipra-util/src/main/java/de/vipra/util/MongoUtils.java @@ -15,7 +15,7 @@ public class MongoUtils { if (sortBy == null) return null; final String[] sortKeys = sortBy.split(","); - final ArrayList<Bson> sorts = new ArrayList<Bson>(sortKeys.length); + final ArrayList<Bson> sorts = new ArrayList<>(sortKeys.length); for (String sort : sortKeys) { if (sort.startsWith("-")) { sorts.add(descending(sort.substring(1))); diff --git a/vipra-util/src/main/java/de/vipra/util/MultiMap.java b/vipra-util/src/main/java/de/vipra/util/MultiMap.java index 8d677f4471a587df88e4b1b45640aa3c5341f62a..4a4e11ccc8972241c790ef6ae781da1563d7371d 100644 --- a/vipra-util/src/main/java/de/vipra/util/MultiMap.java +++ b/vipra-util/src/main/java/de/vipra/util/MultiMap.java @@ -1,41 +1,57 @@ package de.vipra.util; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.Supplier; public class MultiMap<K, V> { - private final Map<K, Set<V>> map; + private final Map<K, Collection<V>> map; + private final Supplier<Collection<V>> supplier; public MultiMap() { - this.map = new HashMap<K, Set<V>>(); + this(false); + } + + public MultiMap(final boolean duplicates) { + if (duplicates) + supplier = () -> new ArrayList<>(); + else + supplier = () -> new HashSet<>(); + this.map = new HashMap<>(); + } + + public MultiMap(final Supplier<Collection<V>> supplier) { + this.supplier = supplier; + this.map = new HashMap<>(); } public void put(final K key, final V value) { - Set<V> set = map.get(key); - if (set == null) - set = new HashSet<>(); - set.add(value); - map.put(key, set); + Collection<V> c = map.get(key); + if (c == null) + c = supplier.get(); + c.add(value); + map.put(key, c); } public void put(final K key, final Collection<V> values) { - Set<V> set = map.get(key); - if (set == null) - set = new HashSet<>(); - set.addAll(values); - map.put(key, set); + Collection<V> c = map.get(key); + if (c == null) + c = supplier.get(); + c.addAll(values); + map.put(key, c); } - public Set<V> get(final K key) { + public Collection<V> get(final K key) { return map.get(key); } - public Set<Entry<K, Set<V>>> entrySet() { + public Set<Entry<K, Collection<V>>> entrySet() { return map.entrySet(); } diff --git a/vipra-util/src/main/java/de/vipra/util/Statistics.java b/vipra-util/src/main/java/de/vipra/util/Statistics.java new file mode 100644 index 0000000000000000000000000000000000000000..064420cdbfada6a1215c645dd60d29cfd11c2548 --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/Statistics.java @@ -0,0 +1,174 @@ +package de.vipra.util; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.databind.JsonMappingException; + +public class Statistics { + + public static class RunInfo { + private final String name; + private final List<Long> times; + private final Double mean; + private final Double median; + private final Long mode; + + public RunInfo(final String name, final Collection<Long> times) { + this.name = name; + this.times = new ArrayList<>(times.size()); + try { + for (final Long time : times) + this.times.add(time); + } catch (final Exception e) { + e.printStackTrace(); + } + + mean = MathUtils.mean(times); + median = MathUtils.median(this.times); + mode = MathUtils.mode(this.times); + } + + public String getName() { + return name; + } + + public List<Long> getTimes() { + return times; + } + + public Double getMean() { + return mean; + } + + public Double getMedian() { + return median; + } + + public Long getMode() { + return mode; + } + } + + private long startTime; + private long stopTime; + private long runTime; + private List<RunInfo> runInfo; + + @JsonIgnore + private final MultiMap<String, Long> times = new MultiMap<>(true); + + @JsonIgnore + private final Map<String, Long> running = new HashMap<>(); + + public Statistics() { + begin(); + } + + public String name() { + return "run-" + startTime; + } + + public void begin() { + startTime = System.nanoTime(); + } + + public void finish(final String path) throws JsonGenerationException, JsonMappingException, IOException { + stopAll(); + stopTime = System.nanoTime(); + runTime = stopTime - startTime; + generateStats(); + save(path); + } + + public void generateStats() { + runInfo = new ArrayList<>(); + for (final Map.Entry<String, Collection<Long>> entry : times.entrySet()) { + runInfo.add(new RunInfo(entry.getKey(), entry.getValue())); + } + } + + public void save(final String path) throws JsonGenerationException, JsonMappingException, IOException { + File target; + if (path != null && !path.isEmpty()) { + final File file = new File(path); + if (file.exists()) { + if (file.isDirectory()) + target = new File(file, name() + ".json"); + else + target = file; + } else { + target = file; + } + } else { + target = new File(Config.getGenericStatsDir(), name() + ".json"); + } + Config.mapper.writeValue(target, this); + } + + public void start(final String name) { + running.put(name, System.nanoTime()); + } + + public void stop(final String name) { + final long curr = System.nanoTime(); + final Long start = running.remove(name); + if (start != null) { + times.put(name, curr - start); + } + } + + public void stopAll() { + final long curr = System.nanoTime(); + for (Map.Entry<String, Long> run : running.entrySet()) + times.put(run.getKey(), curr - run.getValue()); + running.clear(); + } + + public void stop(final String... names) { + final long curr = System.nanoTime(); + for (final String name : names) { + final Long v = running.remove(name); + if (v != null) + times.put(name, curr - v); + } + } + + public void next(final String stopName, final String startName) { + final long curr = System.nanoTime(); + final Long start = running.remove(stopName); + if (start != null) { + times.put(stopName, curr - start); + } + + running.put(startName, curr); + } + + /* + * getters + */ + + public long getStartTime() { + return startTime; + } + + public long getStopTime() { + return stopTime; + } + + public long getRunTime() { + return runTime; + } + + public List<RunInfo> getRunInfo() { + return runInfo; + } + +} diff --git a/vipra-util/src/main/java/de/vipra/util/StringUtils.java b/vipra-util/src/main/java/de/vipra/util/StringUtils.java index f215a12a290c73d7ad80ce7bfd9cf46c5fed281c..ba5db5acdc453b832751cdf447d40acf8c2dedfb 100644 --- a/vipra-util/src/main/java/de/vipra/util/StringUtils.java +++ b/vipra-util/src/main/java/de/vipra/util/StringUtils.java @@ -51,7 +51,7 @@ public class StringUtils { } public static String timeString(long nanos, final boolean showMillis, final boolean compactHMS, final boolean onlyHMS) { - final List<String> parts = new ArrayList<String>(6); + final List<String> parts = new ArrayList<>(6); if (!onlyHMS) { final long days = TimeUnit.NANOSECONDS.toDays(nanos); diff --git a/vipra-util/src/main/java/de/vipra/util/model/ArticleWord.java b/vipra-util/src/main/java/de/vipra/util/model/ArticleWord.java index e3ee8969a5cf990f3fd2c9a7642c0c0b6e87321d..4d2f669a71830ba1d7e29f2248683d9ae2b5717c 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/ArticleWord.java +++ b/vipra-util/src/main/java/de/vipra/util/model/ArticleWord.java @@ -40,7 +40,11 @@ public class ArticleWord implements Comparable<ArticleWord>, Serializable { } public String aTag(final String word) { - return "<a data-word=\"" + this.word + "\">" + word + "</a>"; + return aTag(this.word, word); + } + + public static String aTag(final String word, final String label) { + return "<w=" + word + ">" + label + "</a>"; } @Override diff --git a/vipra-util/src/main/java/de/vipra/util/model/TextEntity.java b/vipra-util/src/main/java/de/vipra/util/model/TextEntity.java index 80f1cdb2ba54ace0a044d3d0cfa4bcb4821d98a7..7bc4c2c53fabba686ff17b28124e373705f80879 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TextEntity.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TextEntity.java @@ -74,10 +74,6 @@ public class TextEntity implements Serializable { this.types = types; } - public String aTag() { - return "<a href=\"" + url + "\">" + entity + "</a>"; - } - public String realEntity() { return TextEntityFull.realEntity(url); } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TextEntityFull.java b/vipra-util/src/main/java/de/vipra/util/model/TextEntityFull.java index 776829e3e012ac6d5df477c6613953bfbc9a92f7..5942cb6e6a583eb31485f524f35f142e2e786779 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TextEntityFull.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TextEntityFull.java @@ -156,7 +156,7 @@ public class TextEntityFull implements Model<String>, Serializable, Comparable<T } public String aTag(final String entity) { - return "<a data-entity=\"" + this.entity + "\">" + entity + "</a>"; + return "<e=" + this.entity + ">" + entity + "</a>"; } public String realEntity() { diff --git a/vipra-util/src/main/java/de/vipra/util/service/MongoService.java b/vipra-util/src/main/java/de/vipra/util/service/MongoService.java index 90ea54693c35ca8f07122f6ffae926946ab12b45..783ce4dd0556169a66e966864b952589871ff5df 100644 --- a/vipra-util/src/main/java/de/vipra/util/service/MongoService.java +++ b/vipra-util/src/main/java/de/vipra/util/service/MongoService.java @@ -238,7 +238,7 @@ public class MongoService<Type extends Model<IdType>, IdType> { public static <Type extends Model<IdType>, IdType> MongoService<Type, IdType> getDatabaseService(final Config config, final Class<Type> clazz) throws ConfigException { final Mongo mongo = config.getMongo(); - return new MongoService<Type, IdType>(mongo, clazz); + return new MongoService<>(mongo, clazz); } public static void dropDatabase(final Config config) throws ConfigException {