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 4adc7dd66711659edcc03dca4ab6098c67fb6671..eaa25a59d9aeab06b8aa517065498dcb79526901 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/Main.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/Main.java @@ -39,7 +39,7 @@ import de.vipra.cmd.option.ConfigCommand; import de.vipra.cmd.option.ImportCommand; import de.vipra.cmd.option.StatsCommand; import de.vipra.cmd.option.TestCommand; -import de.vipra.cmd.option.TopicModelingCommand; +import de.vipra.cmd.option.ModelingCommand; public class Main { @@ -111,7 +111,7 @@ public class Main { commands.add(new ImportCommand(cline.getOptionValues(OPT_IMPORT))); if (cline.hasOption(OPT_MODELING)) - commands.add(new TopicModelingCommand()); + commands.add(new ModelingCommand()); if (cline.hasOption(OPT_STATS)) commands.add(new StatsCommand()); diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/file/FilebaseVocabulary.java b/vipra-cmd/src/main/java/de/vipra/cmd/file/FilebaseVocabulary.java index 2f0ec9317e270c07e9dcf7725ad494d29a620676..8f8b3b6f693714599fb35afdd65b6f54609d1ec2 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/file/FilebaseVocabulary.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/file/FilebaseVocabulary.java @@ -4,8 +4,10 @@ import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import de.vipra.util.Constants; import de.vipra.util.FileUtils; @@ -14,13 +16,19 @@ public class FilebaseVocabulary implements Closeable, Iterable<String> { private File file; private List<String> vocables; + private Map<String, Integer> vocablesMap; + private int nextIndex = 0; public FilebaseVocabulary(File file) throws IOException { this.file = file; if (file.exists()) { vocables = new ArrayList<>(FileUtils.readFile(file)); + vocablesMap = new HashMap<>(vocables.size() + 200); + for (String vocable : vocables) + vocablesMap.put(vocable, nextIndex++); } else { - vocables = new ArrayList<>(); + vocables = new ArrayList<>(500); + vocablesMap = new HashMap<>(500); } } @@ -34,19 +42,20 @@ public class FilebaseVocabulary implements Closeable, Iterable<String> { public void addVocabulary(String[] text) { for (String word : text) { - // TODO fix this - if (!vocables.contains(word)) { + if (!vocablesMap.containsKey(word)) { + vocablesMap.put(word, nextIndex++); vocables.add(word); } } } public int index(String word) { - return vocables.indexOf(word); + Integer index = vocablesMap.get(word); + return index == null ? -1 : index; } public int size() { - return vocables.size(); + return vocablesMap.size(); } @Override 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 d75179fb82bc047f8b18ee07f864ceac165eec6e..bb34530d1eb20afecea39c1aa838379033ce1a7b 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 @@ -5,6 +5,7 @@ import java.io.FileReader; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.List; +import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -188,7 +189,7 @@ public class ImportCommand implements Command { * save words */ out.info("saving words"); - List<Word> importedWords = wordMap.getNewWords(); + Set<Word> importedWords = wordMap.getNewWords(); timer.lap("saving topic refs and indexing"); wordMap.create(); timer.lap("saving words"); diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/TopicModelingCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/ModelingCommand.java similarity index 96% rename from vipra-cmd/src/main/java/de/vipra/cmd/option/TopicModelingCommand.java rename to vipra-cmd/src/main/java/de/vipra/cmd/option/ModelingCommand.java index bc0b184169c92588a04eafdfef78860787f83a2d..435a7ab6cbdff194157c0b0ecd551e5a203b361a 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/TopicModelingCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/ModelingCommand.java @@ -6,6 +6,7 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -32,9 +33,9 @@ import de.vipra.util.model.TopicRef; import de.vipra.util.model.Word; import de.vipra.util.service.DatabaseService; -public class TopicModelingCommand implements Command { +public class ModelingCommand implements Command { - public static final Logger log = LogManager.getLogger(TopicModelingCommand.class); + public static final Logger log = LogManager.getLogger(ModelingCommand.class); public static final Logger out = LogManager.getLogger("shellout"); private Config config; @@ -147,7 +148,7 @@ public class TopicModelingCommand implements Command { * save words */ out.info("saving words"); - List<Word> importedWords = wordMap.getNewWords(); + Set<Word> importedWords = wordMap.getNewWords(); timer.lap("saving topic refs and indexing"); wordMap.create(); timer.lap("saving words"); diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/StatsCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/StatsCommand.java index b6e59868a9aa29ba534eb8ca28be23915ec6d275..8264c003b526dab5e279b2244816fed990890f8f 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/StatsCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/StatsCommand.java @@ -9,7 +9,9 @@ import org.bson.types.ObjectId; import de.vipra.cmd.file.Filebase; import de.vipra.util.Config; import de.vipra.util.StringUtils; -import de.vipra.util.model.TopicFull; +import de.vipra.util.model.Article; +import de.vipra.util.model.Topic; +import de.vipra.util.model.Word; import de.vipra.util.service.DatabaseService; public class StatsCommand implements Command { @@ -19,21 +21,25 @@ public class StatsCommand implements Command { private Config config; private Filebase filebase; - private DatabaseService<TopicFull, ObjectId> dbTopics; + private DatabaseService<Article, ObjectId> dbArticles; + private DatabaseService<Topic, ObjectId> dbTopics; + private DatabaseService<Word, String> dbWords; private void stats() { File modelFile = filebase.getModelFile(); out.info("filebase size: " + StringUtils.humanReadableByteCount(modelFile.length(), true)); - out.info("# of articles: " + filebase.getIndex().size()); - out.info("# of words : " + filebase.getVocab().size()); + out.info("# of articles: " + dbArticles.count()); out.info("# of topics : " + dbTopics.count()); + out.info("# of words : " + dbWords.count()); } @Override public void run() throws Exception { config = Config.getConfig(); filebase = Filebase.getFilebase(config); - dbTopics = DatabaseService.getDatabaseService(config, TopicFull.class); + dbArticles = DatabaseService.getDatabaseService(config, Article.class); + dbTopics = DatabaseService.getDatabaseService(config, Topic.class); + dbWords = DatabaseService.getDatabaseService(config, Word.class); stats(); } diff --git a/vipra-cmd/src/main/resources/config.properties b/vipra-cmd/src/main/resources/config.properties index 822a7005c3cff53f490e50623dc41cdffc9e80ec..0778073f0a04af174ec97a6263bcf433f71a90d6 100644 --- a/vipra-cmd/src/main/resources/config.properties +++ b/vipra-cmd/src/main/resources/config.properties @@ -2,5 +2,5 @@ db.host=localhost db.port=27017 db.name=test tm.processor=corenlp -tm.analyzer=dynnmf +tm.analyzer=jgibb tm.saveallwords=false \ No newline at end of file diff --git a/vipra-ui/app/html/articles/detail/network.html b/vipra-ui/app/html/articles/detail/network.html new file mode 100644 index 0000000000000000000000000000000000000000..5ba521ab1a741ba18346473f29372a4b24b949fe --- /dev/null +++ b/vipra-ui/app/html/articles/detail/network.html @@ -0,0 +1 @@ +<div class="graph" vis-graph="graph" vis-data="data" vis-options="options" vis-select="select" vis-dblclick="open"></div> \ No newline at end of file diff --git a/vipra-ui/app/html/articles/show.html b/vipra-ui/app/html/articles/detail/show.html similarity index 84% rename from vipra-ui/app/html/articles/show.html rename to vipra-ui/app/html/articles/detail/show.html index 13c354fed285907d6d71498d6aa9e97483a918bf..98016a43904610c24ec3cf3a32012795d834053f 100644 --- a/vipra-ui/app/html/articles/show.html +++ b/vipra-ui/app/html/articles/detail/show.html @@ -34,7 +34,13 @@ <th>Word count</th> <td ng-bind="::article.stats.wordCount"></td> </tr> + <tr> + <th>Links</th> + <td> + <a ui-sref="articles.detail.network({id:article.id})">Network graph</a> + </td> + </tr> </tbody> </table> -<p ng-bind="::article.text"></p> \ No newline at end of file +<p ng-bind-html="::article.text"></p> \ No newline at end of file diff --git a/vipra-ui/app/html/articles/index.html b/vipra-ui/app/html/articles/index.html index 5560bfaff51ace0ddeb7c818e2d572d2fa7270fd..5f2bea4bbf5967fda77744308451f1b7ed6e9e0c 100644 --- a/vipra-ui/app/html/articles/index.html +++ b/vipra-ui/app/html/articles/index.html @@ -4,7 +4,7 @@ <ul class="list-unstyled"> <li ng-repeat="article in articles"> - <a ui-sref="articles.show({id: article.id})" ng-bind="::article.title"></a> + <a ui-sref="articles.detail.show({id: article.id})" ng-bind="::article.title"></a> </li> </ul> diff --git a/vipra-ui/app/html/index.html b/vipra-ui/app/html/index.html index 336e7d2f348a602b8c83c30c000aec72297eac34..a1b589f3c65b813c7c7dc0ea28614f0356c3afbf 100644 --- a/vipra-ui/app/html/index.html +++ b/vipra-ui/app/html/index.html @@ -11,7 +11,7 @@ <h4>Latest articles</h4> <ul class="list-unstyled"> <li class="ellipsize" ng-repeat="article in latestArticles"> - <a ui-sref="articles.show({id:article.id})" ng-bind="article.title"></a> + <a ui-sref="articles.detail.show({id:article.id})" ng-bind="article.title"></a> </li> </ul> </div> @@ -50,7 +50,7 @@ <h4>Results <query-time/></h4> <ul class="list-unstyled search-results"> <li class="search-result" ng-repeat="article in searchResults"> - <a ui-sref="articles.show({id:article.id})" ng-bind="article.title"></a> + <a ui-sref="articles.detail.show({id:article.id})" ng-bind="article.title"></a> <p> <span class="text" ng-bind="article.text"></span> <br> diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js index 7b23dad661694e9b82da097c324e87b7244b3f73..29554595c0f76352ac2df53efea8b433c2e5eaee 100644 --- a/vipra-ui/app/js/app.js +++ b/vipra-ui/app/js/app.js @@ -1,11 +1,12 @@ -/* +/****************************************************************************** * Vipra Application * Main application file - */ + ******************************************************************************/ (function() { var app = angular.module('vipra.app', [ 'ngResource', + 'ngSanitize', 'ui.router', 'vipra.controllers', 'vipra.directives', @@ -42,12 +43,24 @@ controller: 'ArticlesIndexController' }); - $stateProvider.state('articles.show', { + $stateProvider.state('articles.detail', { url: '/:id', - templateUrl: tplBase + '/articles/show.html', + abstract: true, + template: '<ui-view/>' + }); + + $stateProvider.state('articles.detail.show', { + url: '', + templateUrl: tplBase + '/articles/detail/show.html', controller: 'ArticlesShowController' }); + $stateProvider.state('articles.detail.network', { + url: '/network', + templateUrl: tplBase + '/articles/detail/network.html', + controller: 'ArticlesNetworkController' + }); + // states: topics $stateProvider.state('topics', { diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js index e92e1afe610cf70eeb8e4945dcf77bd5a8b115bb..8fb6620453bc6f76036551fff831a172f13ed35a 100644 --- a/vipra-ui/app/js/controllers.js +++ b/vipra-ui/app/js/controllers.js @@ -1,7 +1,7 @@ -/* +/****************************************************************************** * Vipra Application * Controllers - */ + ******************************************************************************/ (function() { var app = angular.module('vipra.controllers', [ @@ -49,7 +49,7 @@ }]); /* - * ARTICLES + * Article Controllers */ app.controller('ArticlesIndexController', ['$scope', '$stateParams', 'ArticleFactory', @@ -83,8 +83,134 @@ }]); + app.controller('ArticlesNetworkController', ['$scope', '$state', '$stateParams', 'ArticleFactory', 'TopicFactory', + function($scope, $state, $stateParams, ArticleFactory, TopicFactory) { + + ArticleFactory.get({id: $stateParams.id}, function(response) { + $scope.article = response.data; + + $scope.nodes = new vis.DataSet(); + $scope.edges = new vis.DataSet(); + $scope.data = { + nodes: $scope.nodes, + edges: $scope.edges + }; + $scope.options = { + nodes: { + font: { + size: 11 + }, + scaling: { + label: true + }, + shadow: true + }, + layout: { + randomSeed: 1 + }, + physics: { + barnesHut: { + centralGravity: 0.1, + springLength: 200, + springConstant: 0.01, + avoidOverlap: 0.1 + } + } + }; + + // initial network + var id = 0, + ids = {}, + nodes = [], + edges = [], + topics = $scope.article.topics, + articleColor = '#BBC9D2', + topicColor = '#DBB234'; + + // root node + nodes.push({id: ++id, title: $scope.article.title, label: $scope.article.title.ellipsize(20), type: 'article', article: $scope.article.id, loaded: true, color: {background: articleColor}}); + ids[$scope.article.id] = id; + + // child nodes + for(var i = 0; i < topics.length; i++) { + var topic = topics[i].topic; + nodes.push({id: ++id, label: topic.name, type: 'topic', topic: topic.id, color: {background: topicColor}}); + edges.push({from: 1, to: id}); + ids[topic.id] = id; + } + + $scope.nodes.add(nodes); + $scope.edges.add(edges); + + // on node select + $scope.select = function(props) { + var node = $scope.nodes.get(props.nodes[0]); + if(node && !node.loaded) { + if(node.type === 'topic') { + // node is topic, load topic to get articles + TopicFactory.get({id:node.topic}, function(res) { + if(res.data && res.data.articles) { + var articles = res.data.articles; + var newNodes = []; + var newEdges = []; + for(var i = 0; i < articles.length; i++) { + var article = articles[i]; + if(!ids.hasOwnProperty(article.id)) { + newNodes.push({id: ++id, title: article.title, label: article.title.ellipsize(20), type: 'article', article: article.id, color: {background: articleColor}}); + newEdges.push({from:node.id, to:id}); + ids[article.id] = id; + } + } + if(newNodes.length) + $scope.nodes.add(newNodes); + if(newEdges.length) + $scope.edges.add(newEdges); + } + }); + } else if(node.type === 'article') { + // node is article, load article to get topics + ArticleFactory.get({id:node.article}, function(res) { + if(res.data && res.data.topics) { + var topics = res.data.topics; + var newNodes = []; + var newEdges = []; + for(var i = 0; i < topics.length; i++) { + var topic = topics[i].topic; + if(!ids.hasOwnProperty(topic.id)) { + newNodes.push({id: ++id, title: topic.name, label: topic.name.ellipsize(20), type: 'topic', topic: topic.id, color: {background: topicColor}}); + newEdges.push({from:node.id, to:id}); + ids[topic.id] = id; + } + } + if(newNodes.length) + $scope.nodes.add(newNodes); + if(newEdges.length) + $scope.edges.add(newEdges); + } + }); + } + node.loaded = true; + $scope.nodes.update(node); + } + }; + + // on node open + $scope.open = function(props) { + var node = $scope.nodes.get(props.nodes[0]); + if(node) { + if(node.type === 'article') { + $state.transitionTo('articles.detail.show', {id:node.article}); + } else if(node.type === 'topic') { + $state.transitionTo('topics.show', {id:node.topic}); + } + } + }; + }); + + }]); + /* - * TOPICS + * Topic Controllers */ app.controller('TopicsIndexController', ['$scope', 'TopicFactory', @@ -112,7 +238,7 @@ }]); /* - * WORDS + * Word Controllers */ app.controller('WordsIndexController', ['$scope', 'WordFactory', @@ -139,7 +265,7 @@ }]); /* - * DIRECTIVES + * Directive Controllers */ app.controller('PaginationController', ['$scope', diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js index f37e49881e07ca87873cdada2a9004faa51ff42e..519f1d347c19d9caea1086b2ad96bd82304f6847 100644 --- a/vipra-ui/app/js/directives.js +++ b/vipra-ui/app/js/directives.js @@ -1,7 +1,7 @@ -/* +/****************************************************************************** * Vipra Application * Directives - */ + ******************************************************************************/ (function() { var app = angular.module('vipra.directives', [ @@ -30,7 +30,7 @@ restrict: 'E', replace: true, transclude: true, - template: '<a class="article-link" ui-sref="articles.show({id:article.id})"><span ng-bind="article.title"></span><ng-transclude/></a>' + template: '<a class="article-link" ui-sref="articles.detail.show({id:article.id})"><span ng-bind="article.title"></span><ng-transclude/></a>' } }); @@ -56,4 +56,23 @@ }; }); + app.directive('visGraph', function() { + return { + scope: { + visGraph: '=', + visData: '=', + visOptions: '=', + visSelect: '&', + visDblclick: '&' + }, + link: function($scope, $element) { + $scope.$watchGroup(['visData', 'visOptions'], function() { + $scope.visGraph = new vis.Network($element[0], $scope.visData, $scope.visOptions); + $scope.visGraph.on('selectNode', $scope.visSelect() || function() {}); + $scope.visGraph.on('doubleClick', $scope.visDblclick() || function() {}); + }); + } + }; + }); + })(); \ No newline at end of file diff --git a/vipra-ui/app/js/factories.js b/vipra-ui/app/js/factories.js index b056ecc2c96b13e8c0ba3e3e5730b18cec350085..45284805d380db3e8573aadfb9bf07640d2d6c49 100644 --- a/vipra-ui/app/js/factories.js +++ b/vipra-ui/app/js/factories.js @@ -1,7 +1,7 @@ -/* +/****************************************************************************** * Vipra Application * Factories - */ + ******************************************************************************/ (function() { var app = angular.module('vipra.factories', []); diff --git a/vipra-ui/app/js/filters.js b/vipra-ui/app/js/filters.js index 2be8bfc07f40c33666e2d8d6ad3bd69bd3b02759..ae98ac22d553e57fa2b1f32afc2cfe40ddbd84ed 100644 --- a/vipra-ui/app/js/filters.js +++ b/vipra-ui/app/js/filters.js @@ -1,7 +1,7 @@ -/* +/****************************************************************************** * Vipra Application * Filters - */ + ******************************************************************************/ (function() { var app = angular.module('vipra.filters', []); diff --git a/vipra-ui/app/js/helpers.js b/vipra-ui/app/js/helpers.js index 48126a269a9dbd79815d8f523ac660d6f5e40f44..cf43be3f05b0a0a0358d1f86f828f1f207938160 100644 --- a/vipra-ui/app/js/helpers.js +++ b/vipra-ui/app/js/helpers.js @@ -1,7 +1,7 @@ -/* +/****************************************************************************** * Vipra Application * Helpers - */ + ******************************************************************************/ (function() { window.formatDate = function(date) { @@ -19,4 +19,11 @@ return Math.round(input * 100); }; + String.prototype.ellipsize = function(max) { + if(this.length > max) { + return this.substring(0, max) + '...'; + } + return this; + }; + })(); \ No newline at end of file diff --git a/vipra-ui/app/js/services.js b/vipra-ui/app/js/services.js index 1fba5a4cc0a7ee15de94b109aac433a14f5e8fb3..5f8e95703ea98dc8ae766127dd7a21420231b528 100644 --- a/vipra-ui/app/js/services.js +++ b/vipra-ui/app/js/services.js @@ -1,7 +1,7 @@ -/* +/****************************************************************************** * Vipra Application * Services - */ + ******************************************************************************/ (function() { var app = angular.module('vipra.services', []); diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less index 537ff232c056c30fa94ca042ca28010f6b8d7442..0391c33412f22484b8026fafe0a0f6dc685f428a 100644 --- a/vipra-ui/app/less/app.less +++ b/vipra-ui/app/less/app.less @@ -99,6 +99,14 @@ body { } } +.graph { + position: absolute; + top: 50px; + left: 0; + right: 0; + bottom: 50px; +} + .noselect { -webkit-touch-callout: none; -webkit-user-select: none; diff --git a/vipra-ui/bower.json b/vipra-ui/bower.json index 6104b60a698abadab8bf78da635179af4ee58d4a..ade95723df59097006fdbde8f1c96f560d36d234 100644 --- a/vipra-ui/bower.json +++ b/vipra-ui/bower.json @@ -19,8 +19,10 @@ "dependencies": { "bootstrap": "~3.3.6", "jquery": "^2.2.0", - "angular": "^1.4.9", - "angular-resource": "^1.4.9", - "angular-ui-router": "^0.2.17" + "angular": "^1.5.0", + "angular-resource": "^1.5.0", + "angular-ui-router": "^0.2.17", + "angular-sanitize": "^1.5.0", + "highcharts": "^4.2.2" } } diff --git a/vipra-ui/gulpfile.js b/vipra-ui/gulpfile.js index 20da5abcda1dd2966ca625304963f444ad1ecd22..87ee3e2591413a6ac17a0650df08a19c643f9848 100644 --- a/vipra-ui/gulpfile.js +++ b/vipra-ui/gulpfile.js @@ -2,6 +2,7 @@ var gulp = require('gulp'), less = require('gulp-less'), concat = require('gulp-concat'), uglify = require('gulp-uglify'), + plumber = require('gulp-plumber'), cssnano = require('gulp-cssnano'), webserver = require('gulp-webserver'); @@ -10,11 +11,15 @@ var assets = { 'bower_components/jquery/dist/jquery.min.js', 'bower_components/angular/angular.min.js', 'bower_components/angular-resource/angular-resource.min.js', + 'bower_components/angular-sanitize/angular-sanitize.min.js', 'bower_components/angular-ui-router/release/angular-ui-router.min.js', - 'bower_components/bootstrap/dist/js/bootstrap.min.js' + 'bower_components/bootstrap/dist/js/bootstrap.min.js', + 'bower_components/highcharts/highcharts.js', + 'bower_components/vis/dist/vis.min.js' ], css: [ - 'bower_components/bootstrap/dist/css/bootstrap.min.css' + 'bower_components/bootstrap/dist/css/bootstrap.min.css', + 'bower_components/vis/dist/vis.min.css' ], fonts: [ 'bower_components/bootstrap/dist/fonts/*' @@ -24,12 +29,14 @@ var assets = { gulp.task('less', function() { gulp.src('app/less/**/*.less') + .pipe(plumber()) .pipe(less()) .pipe(gulp.dest('public/css')); }); gulp.task('js', function() { gulp.src('app/js/**/*.js') + .pipe(plumber()) .pipe(concat('app.js')) .pipe(gulp.dest('public/js')); }); @@ -54,11 +61,9 @@ gulp.task('public', function() { gulp.task('assets', function() { gulp.src(assets.js) .pipe(concat('vendor.js')) - .pipe(uglify()) .pipe(gulp.dest('public/js')); gulp.src(assets.css) .pipe(concat('vendor.css')) - .pipe(cssnano()) .pipe(gulp.dest('public/css')); gulp.src(assets.fonts) .pipe(gulp.dest('public/fonts')); diff --git a/vipra-ui/package.json b/vipra-ui/package.json index bc23c8b4d62be957580b6e7a5dfaf1625b1739ff..5be5d8d240dd5417d10174aa7cac157778319a31 100644 --- a/vipra-ui/package.json +++ b/vipra-ui/package.json @@ -10,6 +10,7 @@ "gulp-cssnano": "^2.1.0", "gulp-include": "^2.1.0", "gulp-less": "^3.0.5", + "gulp-plumber": "^1.0.1", "gulp-sourcemaps": "^1.6.0", "gulp-uglify": "^1.5.1", "gulp-webserver": "^0.9.1" 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 4b3a78deff351ce1b7d9cdddf3587c6835324e8f..b03c69994b630fb669fff1ff2f8eddaca5c8ce52 100644 --- a/vipra-util/src/main/java/de/vipra/util/Config.java +++ b/vipra-util/src/main/java/de/vipra/util/Config.java @@ -233,6 +233,12 @@ public class Config { pw.flush(); } + public String hash() { + String config = databaseHost + databasePort + databaseName + processor + analyzer + windowResolution + + saveAllWords; + return DigestUtils.md5(config); + } + public static File getGenericDataDir() { File base = PathUtils.appDataDir(); return new File(base, Constants.FB_DIR); diff --git a/vipra-util/src/main/java/de/vipra/util/DigestUtils.java b/vipra-util/src/main/java/de/vipra/util/DigestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..3fa20e86387c1fcedc655cb972c3e0712ce4351f --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/DigestUtils.java @@ -0,0 +1,26 @@ +package de.vipra.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.xml.bind.annotation.adapters.HexBinaryAdapter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DigestUtils { + + public static final Logger log = LoggerFactory.getLogger(DigestUtils.class); + + public static String md5(String in) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("MD5"); + return (new HexBinaryAdapter()).marshal(md.digest(in.getBytes())); + } catch (NoSuchAlgorithmException e) { + log.error("md5 algorithm not available"); + return null; + } + } + +} diff --git a/vipra-util/src/main/java/de/vipra/util/WordMap.java b/vipra-util/src/main/java/de/vipra/util/WordMap.java index 8d11d931316e97d6c3f4d0a0181047b7e9acfb6b..574f3ac0de3fd84f0f2e5fb0d35976b1689bce5b 100644 --- a/vipra-util/src/main/java/de/vipra/util/WordMap.java +++ b/vipra-util/src/main/java/de/vipra/util/WordMap.java @@ -2,9 +2,11 @@ package de.vipra.util; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,13 +21,13 @@ public class WordMap { private final DatabaseService<Word, String> dbWords; private final Map<String, Word> wordMap; - private final List<Word> newWords; + private final Set<Word> newWords; private boolean createNow = false; public WordMap(DatabaseService<Word, String> dbWords) { this.dbWords = dbWords; this.wordMap = new HashMap<>(); - this.newWords = new ArrayList<>(); + this.newWords = new HashSet<>(); List<Word> words = dbWords.getAll(); for (Word word : words) wordMap.put(word.getWord().toLowerCase(), word); @@ -76,7 +78,7 @@ public class WordMap { this.createNow = createNow; } - public List<Word> getNewWords() { + public Set<Word> getNewWords() { return newWords; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/Article.java b/vipra-util/src/main/java/de/vipra/util/model/Article.java index fc65f56b500b6d1229d99a4af88d036a44706a63..36a13c525e6ffa5fee8fda9036920d2db05e89d0 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/Article.java +++ b/vipra-util/src/main/java/de/vipra/util/model/Article.java @@ -41,4 +41,23 @@ public class Article implements Model<ObjectId>, Serializable { this.title = title; } + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof Article)) + return false; + Article a = (Article) o; + if (id == null) + return a.getId() == null; + return id.equals(a.getId()); + } + + @Override + public int hashCode() { + if (id == null) + return super.hashCode(); + return id.hashCode(); + } + } diff --git a/vipra-util/src/main/java/de/vipra/util/model/ArticleFull.java b/vipra-util/src/main/java/de/vipra/util/model/ArticleFull.java index d2effd84b4e28315107e7e23a7546aacea6c60ed..4380c73381077fca6bee3b2e643552bc71ff2d8b 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/ArticleFull.java +++ b/vipra-util/src/main/java/de/vipra/util/model/ArticleFull.java @@ -238,4 +238,23 @@ public class ArticleFull extends FileModel<ObjectId> implements Serializable { + ", created:" + created + ", modified:" + modified + "]"; } + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof ArticleFull)) + return false; + ArticleFull a = (ArticleFull) o; + if (id == null) + return a.getId() == null; + return id.equals(a.getId()); + } + + @Override + public int hashCode() { + if (id == null) + return super.hashCode(); + return id.hashCode(); + } + } \ No newline at end of file diff --git a/vipra-util/src/main/java/de/vipra/util/model/Topic.java b/vipra-util/src/main/java/de/vipra/util/model/Topic.java index 760510f2032673fe697efb38b4d712f9d7c8bf3d..133be4d4b0ba5d5d4ec3e70f328474a9049772fd 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/Topic.java +++ b/vipra-util/src/main/java/de/vipra/util/model/Topic.java @@ -52,4 +52,23 @@ public class Topic implements Model<ObjectId>, Serializable { this.name = name; } + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof Topic)) + return false; + Topic a = (Topic) o; + if (id == null) + return a.getId() == null; + return id.equals(a.getId()); + } + + @Override + public int hashCode() { + if (id == null) + return super.hashCode(); + return id.hashCode(); + } + } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java b/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java index f004d83788a70049a290eb3049588d1d8346d6c0..99267ff4caaea60305efaf4b1289605be326ad04 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java @@ -127,4 +127,23 @@ public class TopicFull implements Model<ObjectId>, Serializable { return name; } + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof TopicFull)) + return false; + TopicFull a = (TopicFull) o; + if (id == null) + return a.getId() == null; + return id.equals(a.getId()); + } + + @Override + public int hashCode() { + if (id == null) + return super.hashCode(); + return id.hashCode(); + } + } diff --git a/vipra-util/src/main/java/de/vipra/util/model/Word.java b/vipra-util/src/main/java/de/vipra/util/model/Word.java index 7b5c4ed688ebfd12b1facebe2d666648cdfed9c6..268a8dc2b899e10b7ea842fd3bac22892ae12809 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/Word.java +++ b/vipra-util/src/main/java/de/vipra/util/model/Word.java @@ -114,4 +114,23 @@ public class Word implements Model<String>, Serializable { this.created = new Date(); } + @Override + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof Word)) + return false; + Word w = (Word) o; + if (id == null) + return w.getId() == null; + return this.id.equals(w.getId()); + } + + @Override + public int hashCode() { + if (id == null) + return super.hashCode(); + return id.hashCode(); + } + }