diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java b/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java index 6bad2608731d2011c684a1cfd71366d563e737b9..1e04b318e3a8010b883e00b2b3f6a7809d5e8303 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java @@ -208,10 +208,10 @@ public class Analyzer { final List<SequenceFull> newSequences = new ArrayList<>(topicCount * windowCount); final List<TopicFull> newTopics = new ArrayList<>(topicCount); - topicModel.setWordCount(wordCount); - topicModel.setWindowCount(windowCount); - topicModel.setArticleCount(articleCount); - topicModel.setTopicCount(topicCount); + topicModel.setWordCount((long) wordCount); + topicModel.setWindowCount((long) windowCount); + topicModel.setArticleCount((long) articleCount); + topicModel.setTopicCount((long) topicCount); final boolean seqRelativeCutoff = modelConfig.getMinRelativeProbability() > 0; 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 0859d490657617e1600dc6b4e8bfb65c11cd7d3c..c3ca6cbe58493901ebc5dd61742b84d592200f54 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 @@ -300,8 +300,10 @@ public class ImportCommand implements Command { * update topic model */ topicModelFull.setWindows(filebase.getWindowIndex().getWindows()); - topicModelFull.setArticleCount(filebase.getIdDateIndex().size()); - topicModelFull.setWordCount(filebase.getWordIndex().size()); + topicModelFull.setArticleCount((long) filebase.getIdDateIndex().size()); + topicModelFull.setWordCount((long) filebase.getWordIndex().size()); + final long entityCount = dbEntities.count(QueryBuilder.builder().eq("topicModel", topicModel)); + topicModelFull.setEntityCount(entityCount); dbTopicModels.replaceSingle(topicModelFull); /* diff --git a/vipra-ui/app/html/directives/article-link.html b/vipra-ui/app/html/directives/article-link.html index ddd58cc3834bed20bad53ebb9fbc1957356988a1..1e7d04ab33a358c2f2b348966165bc4ba860294a 100644 --- a/vipra-ui/app/html/directives/article-link.html +++ b/vipra-ui/app/html/directives/article-link.html @@ -1,7 +1,7 @@ <div class="link-wrapper"> <span class="menu-padding ellipsis"> <div class="pull-right article-dropdown"> - <i class="fa text-muted pointer" ng-class="{'fa-chevron-down':!excerptShown,'fa-chevron-up':excerptShown}" ng-click="toggleExcerpt()" ng-if="::showExcerpt" analytics-on analytics-event="Article excerpt" analytics-category="Article actions"></i> + <i class="fa text-muted pointer" ng-class="{'fa-chevron-down':!detailsShown,'fa-chevron-up':detailsShown}" ng-click="toggleDetails()" ng-if="::showDetails" analytics-on analytics-event="Article details" analytics-category="Article actions"></i> <span class="badge" ng-bind="::article.topicsCount" ng-attr-title="{{::article.topicsCount}} topic(s)" ng-if="::showBadge"></span> </div> <a class="article-link" ui-sref="articles.show({id:article.id})" ng-attr-title="{{::article.title}}"> @@ -10,5 +10,10 @@ </a> </span> <article-menu class="menu-button" article="article" ng-if="::showMenu" /> - <div ng-bind="excerpt" ng-if="excerptShown" class="excerpt"></div> + <div ng-if="detailsShown" class="details"> + <span ng-bind="::articleDetails.text"></span> + <div> + <a ui-sref="topics.show({id:topic.topic.id})" class="badge" ng-bind="::topic.topic.name" ng-attr-title="::topic.topic.name" ng-repeat="topic in articleDetails.topics"></a> + </div> + </div> </div> \ No newline at end of file diff --git a/vipra-ui/app/html/index.html b/vipra-ui/app/html/index.html index bae0a628f73caf6aa7fddac3b8b568f4829a63b8..fac08c019dd57c2012badca338c9859630d43580 100644 --- a/vipra-ui/app/html/index.html +++ b/vipra-ui/app/html/index.html @@ -45,7 +45,7 @@ <div class="col-md-8 text-center"> <h4>Latest articles</h4> <ul class="list-unstyled"> - <article-link article="::article" badge="false" menu="false" excerpt="false" ng-repeat="article in latestArticles"/> + <article-link article="::article" badge="false" menu="false" details="false" ng-repeat="article in latestArticles"/> </ul> <p class="text-center" ng-if="!latestArticles">Loading...</p> <p class="text-center" ng-if="latestArticles&&!latestArticles.length">No Articles</p> diff --git a/vipra-ui/app/html/partials/topicmodel-popover.html b/vipra-ui/app/html/partials/topicmodel-popover.html new file mode 100644 index 0000000000000000000000000000000000000000..ab7142f10a066bdc9f262d1871cbb1a61af42927 --- /dev/null +++ b/vipra-ui/app/html/partials/topicmodel-popover.html @@ -0,0 +1,30 @@ +<table class="td-padded"> + <tr> + <th>Articles</th> + <td ng-bind="::topicModel.articleCount"></td> + </tr> + <tr> + <th>Topics</th> + <td ng-bind="::topicModel.topicCount"></td> + </tr> + <tr> + <th>Words</th> + <td ng-bind="::topicModel.wordCount"></td> + </tr> + <tr> + <th>Entities</th> + <td ng-bind="::topicModel.entityCount"></td> + </tr> + <tr> + <th>Windows</th> + <td ng-bind="::topicModel.windowCount"></td> + </tr> + <tr> + <th>Last generated</th> + <td ng-bind-template="{{::Vipra.formatDateTime(topicModel.lastGenerated)}}"></td> + </tr> + <tr> + <th>Last indexed</th> + <td ng-bind-template="{{::Vipra.formatDateTime(topicModel.lastIndexed)}}"></td> + </tr> +</table> \ No newline at end of file diff --git a/vipra-ui/app/index.html b/vipra-ui/app/index.html index e84523615bf43d5b9d5502d94814a7ae10e54aeb..4cbc1dc855c6ebccc7d4ec9d4502d8b2aff8275b 100644 --- a/vipra-ui/app/index.html +++ b/vipra-ui/app/index.html @@ -86,12 +86,17 @@ <div class="modal-body"> <ul class="list-group nomargin" ng-show="topicModels.length" ng-cloak> <button type="button" class="list-group-item topic-model" ng-repeat="topicModel in topicModels" 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" ng-bind="topicModel.articleCount" ng-show="topicModel.articleCount" ng-attr-title="{{topicModel.articleCount + ' article(s)'}}" ng-cloak></span> - <span class="badge" ng-bind="topicModel.topicCount" ng-show="topicModel.topicCount" ng-attr-title="{{topicModel.topicCount + ' topic(s)'}}" ng-cloak></span> - <span class="badge" ng-if="!topicModel.lastGenerated" title="Model was never generated">Non-generated</span> - <span ng-bind="topicModel.name"></span> + <span class="badge" bs-popover popover-title="{{::topicModel.name}}" popover-template="partials/topicmodel-popover.html" popover-placement="left"> + <i class="fa fa-info"></i> + </span> + <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> + <small ng-bind="::topicModel.modelConfig.description"></small> </button> </ul> <p class="text-center" ng-show="loading.any" ng-cloak> diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js index 1913324ccaaf6a774b45e46df703c4aa0ce1f77d..d0d60338f6a11fefbb3b1e9b8109df2ad2178acb 100644 --- a/vipra-ui/app/js/app.js +++ b/vipra-ui/app/js/app.js @@ -260,6 +260,40 @@ } }); + // hold onto the drop down menu + var dropdownMenu; + + // and when you show it, move it to the body + $(window).on('show.bs.dropdown', function (e) { + + // grab the menu + dropdownMenu = $(e.target).find('.dropdown-menu'); + + // detach it and append it to the body + $('body').append(dropdownMenu.detach()); + + // grab the new offset position + var eOffset = $(e.target).offset(); + + // make sure to place it where it would normally go (this could be improved) + dropdownMenu.css({ + 'display': 'block', + 'top': eOffset.top + $(e.target).outerHeight(), + 'left': eOffset.left + }); + }); + + // and when you hide it, reattach the drop down, and hide it normally + $(window).on('hide.bs.dropdown', function (e) { + $(e.target).append(dropdownMenu.detach()); + dropdownMenu.hide(); + }); + + $(window).on('resize', function() { + if(dropdownMenu) + dropdownMenu.hide(); + }); + $(document).ready(function() { $('.btn-file :file').on('fileselect', function(event, numFiles, label) { var input = $(this).parents('.input-group').find(':text'), diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js index 7d0db883fdce180a0c2c81e0ae32e74b9f5c2f73..b3b0665114f3f7695e8996ec55c4b1d0f6663a5f 100644 --- a/vipra-ui/app/js/directives.js +++ b/vipra-ui/app/js/directives.js @@ -50,7 +50,7 @@ return { scope: { article: '=', - excerpt: '@', + details: '@', badge: '@', menu: '@' }, @@ -59,24 +59,24 @@ transclude: true, templateUrl: 'html/directives/article-link.html', link: function($scope) { - $scope.showExcerpt = $scope.excerpt !== 'false'; + $scope.showDetails = $scope.details !== 'false'; $scope.showBadge = $scope.badge !== 'false'; $scope.showMenu = $scope.menu !== 'false'; - $scope.toggleExcerpt = function() { - if (!$scope.excerptShown) { - if ($scope.excerpt) { - $scope.excerptShown = true; + $scope.toggleDetails = function() { + if (!$scope.detailsShown) { + if ($scope.details) { + $scope.detailsShown = true; } else { ArticleFactory.get({ id: $scope.article.id, excerpt: true }, function(data) { - $scope.excerpt = data.text; - $scope.excerptShown = true; + $scope.articleDetails = data; + $scope.detailsShown = true; }); } } else { - $scope.excerptShown = false; + $scope.detailsShown = false; } }; } diff --git a/vipra-ui/app/js/helpers.js b/vipra-ui/app/js/helpers.js index 63dfc88a141f0b67cf7eeed848c6412197d5f150..a3df6c4ee795a6d38710bea35d0eb77ba2a2dd4f 100644 --- a/vipra-ui/app/js/helpers.js +++ b/vipra-ui/app/js/helpers.js @@ -18,8 +18,7 @@ }; Vipra.formatDateTime = function(date) { - date = new Date(date); - return date.toLocaleDateString() + " " + date.toLocaleTimeString(); + return new Date(date).toLocaleString(); }; Vipra.toPercent = function(input, nums) { diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less index 754b620ac0ea06bef7e88361afc959334f3232b0..abe1031957f48f889cb89ae46511ab5b525caa3f 100644 --- a/vipra-ui/app/less/app.less +++ b/vipra-ui/app/less/app.less @@ -703,7 +703,7 @@ entity-menu { } } -.excerpt { +.details { padding-top: 10px; color: #555; } @@ -943,6 +943,27 @@ entity-menu { padding: 0 !important; } +.td-padded th + td { + padding-left: 5px; +} + +.badge-group { + font-size: 0; + + .badge-part { + font-size: 12px; + padding: 0 5px; + } + + .badge-part + .badge-part { + border-left: 1px solid #ddd; + } +} + +.article-dropdown { + position: relative; +} + @keyframes spin { 100% { -webkit-transform: rotateY(360deg); diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicModelFull.java b/vipra-util/src/main/java/de/vipra/util/model/TopicModelFull.java index ac0615c908001c440f8fa93f858fd69edfa9780b..8ae0afae9919186bbcc67860659b0d4e1a638220 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicModelFull.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicModelFull.java @@ -25,13 +25,15 @@ public class TopicModelFull implements Model<ObjectId>, Comparable<TopicModelFul private String name; - private Integer topicCount; + private Long topicCount; - private Integer articleCount; + private Long articleCount; - private Integer wordCount; + private Long wordCount; - private Integer windowCount; + private Long entityCount; + + private Long windowCount; private Date lastGenerated; @@ -80,35 +82,43 @@ public class TopicModelFull implements Model<ObjectId>, Comparable<TopicModelFul this.name = name; } - public Integer getTopicCount() { + public Long getTopicCount() { return topicCount; } - public void setTopicCount(final Integer topicCount) { + public void setTopicCount(final Long topicCount) { this.topicCount = topicCount; } - public Integer getArticleCount() { + public Long getArticleCount() { return articleCount; } - public void setArticleCount(final Integer articleCount) { + public void setArticleCount(final Long articleCount) { this.articleCount = articleCount; } - public Integer getWordCount() { + public Long getWordCount() { return wordCount; } - public void setWordCount(final Integer wordCount) { + public void setWordCount(final Long wordCount) { this.wordCount = wordCount; } - public Integer getWindowCount() { + public Long getEntityCount() { + return entityCount; + } + + public void setEntityCount(Long entityCount) { + this.entityCount = entityCount; + } + + public Long getWindowCount() { return windowCount; } - public void setWindowCount(final Integer windowCount) { + public void setWindowCount(final Long windowCount) { this.windowCount = windowCount; } @@ -142,7 +152,7 @@ public class TopicModelFull implements Model<ObjectId>, Comparable<TopicModelFul public void setWindows(final List<Window> windows) { this.windows = windows; - windowCount = windows != null ? windows.size() : 0; + windowCount = (long) (windows != null ? windows.size() : 0); } public Date getCreated() { diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicModelState.java b/vipra-util/src/main/java/de/vipra/util/model/TopicModelState.java new file mode 100644 index 0000000000000000000000000000000000000000..68f4f7f80d94e048fb595e66a59a7d69a126b991 --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicModelState.java @@ -0,0 +1,7 @@ +package de.vipra.util.model; + +public enum TopicModelState { + NONE, + MODELING, + INDEXING +}