From 03b1a1e589fa3bec29d236a20e54f8f4142ba6a9 Mon Sep 17 00:00:00 2001
From: Eike Cochu <eike@cochu.com>
Date: Tue, 8 Mar 2016 23:19:12 +0100
Subject: [PATCH] added chart to topics, added similar page to articles

---
 .../de/vipra/rest/resource/InfoResource.java  |  1 +
 .../java/de/vipra/cmd/lda/DTMAnalyzer.java    | 18 +++++++----
 vipra-ui/app/html/about.html                  | 12 +++++++-
 vipra-ui/app/html/articles/show.html          |  5 ++++
 vipra-ui/app/html/articles/similar.html       | 14 +++++++++
 vipra-ui/app/html/topics/show.html            | 25 +++++++++++++++-
 vipra-ui/app/js/app.js                        |  9 ++++++
 vipra-ui/app/js/controllers.js                | 30 +++++++++++++++++++
 .../main/java/de/vipra/util/Constants.java    |  6 ++++
 9 files changed, 113 insertions(+), 7 deletions(-)
 create mode 100644 vipra-ui/app/html/articles/similar.html

diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
index 9f1b8025..818444c1 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
@@ -75,6 +75,7 @@ public class InfoResource {
 			info.put("const.ktopics", Constants.K_TOPICS);
 			info.put("const.ktopicwords", Constants.K_TOPIC_WORDS);
 			info.put("const.minrelprob", Constants.MINIMUM_RELATIVE_PROB);
+			info.put("constminshare", Constants.MINIMUM_SHARE);
 			info.put("const.dynminiter", Constants.DYNAMIC_MIN_ITER);
 			info.put("const.dynmaxiter", Constants.DYNAMIC_MAX_ITER);
 			info.put("const.statiter", Constants.STATIC_ITER);
diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/lda/DTMAnalyzer.java b/vipra-cmd/src/main/java/de/vipra/cmd/lda/DTMAnalyzer.java
index c209d580..5ac5d474 100644
--- a/vipra-cmd/src/main/java/de/vipra/cmd/lda/DTMAnalyzer.java
+++ b/vipra-cmd/src/main/java/de/vipra/cmd/lda/DTMAnalyzer.java
@@ -316,17 +316,25 @@ public class DTMAnalyzer extends Analyzer {
 				final double[] topicDistribution = topicDistributions[idxArticle++];
 
 				// create topic references
+				double reducedShare = 0;
 				final List<TopicRef> newTopicRefs = new ArrayList<>(Constants.K_TOPICS);
 				for (int idxTopic = 0; idxTopic < Constants.K_TOPICS; idxTopic++) {
-					final TopicRef newTopicRef = new TopicRef();
-					final TopicFull topicFull = newTopics.get(idxTopic);
-					newTopicRef.setTopic(new Topic(topicFull.getId()));
-					newTopicRef.setShare(topicDistribution[idxTopic]);
-					newTopicRefs.add(newTopicRef);
+					if (topicDistribution[idxTopic] > 0.01) {
+						reducedShare += topicDistribution[idxTopic];
+						final TopicRef newTopicRef = new TopicRef();
+						final TopicFull topicFull = newTopics.get(idxTopic);
+						newTopicRef.setTopic(new Topic(topicFull.getId()));
+						newTopicRef.setShare(topicDistribution[idxTopic]);
+						newTopicRefs.add(newTopicRef);
+					}
 				}
 
 				// update article
 				if (!newTopicRefs.isEmpty()) {
+					// renormalize share
+					for (TopicRef newTopicRef : newTopicRefs)
+						newTopicRef.setShare(newTopicRef.getShare() / reducedShare);
+
 					Collections.sort(newTopicRefs, Comparator.reverseOrder());
 
 					// update article with topic references (partial update)
diff --git a/vipra-ui/app/html/about.html b/vipra-ui/app/html/about.html
index 1a5b2502..15ba8404 100644
--- a/vipra-ui/app/html/about.html
+++ b/vipra-ui/app/html/about.html
@@ -153,7 +153,17 @@
       </tr>
       <tr class="well">
         <td colspan="2">
-          The minimum relative probability of topic words. Words are accepted into a topic, if their probability exceeds <it>maximum_probability * minimum_relative_probability</it>.
+          The minimum relative probability of topic words. Words are accepted into a topic, if their probability exceeds
+          <it>maximum_probability * minimum_relative_probability</it>.
+        </td>
+      </tr>
+      <tr>
+        <th>Minimum share</th>
+        <td ng-bind-template="{{::info.const.minshare}}"></td>
+      </tr>
+      <tr class="well">
+        <td colspan="2">
+          The minimum share of a topic to be accepted for an article. Topic shares are renormalized after rejecting topics below this threshold.
         </td>
       </tr>
       <tr>
diff --git a/vipra-ui/app/html/articles/show.html b/vipra-ui/app/html/articles/show.html
index 3d0c7e94..d450d474 100644
--- a/vipra-ui/app/html/articles/show.html
+++ b/vipra-ui/app/html/articles/show.html
@@ -8,6 +8,11 @@
             Network graph
           </a>
         </td>
+        <td>
+          <a class="btn btn-default" ui-sref="articles.show.similar({id:article.id})">
+            Similar
+          </a>
+        </td>
       </tr>
     </table>
   </div>
diff --git a/vipra-ui/app/html/articles/similar.html b/vipra-ui/app/html/articles/similar.html
new file mode 100644
index 00000000..dc2fb061
--- /dev/null
+++ b/vipra-ui/app/html/articles/similar.html
@@ -0,0 +1,14 @@
+<div ng-cloak ng-hide="$state.current.name !== 'articles.show.similar'">
+  <div class="page-header">
+    <h1 ng-bind="::article.title"></h1>
+    <table class="item-actions">
+      <tr>
+        <td>
+          <a class="btn btn-default" ui-sref="articles.show({id:article.id})">
+            Back
+          </a>
+        </td>
+      </tr>
+    </table>
+  </div>
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/topics/show.html b/vipra-ui/app/html/topics/show.html
index b94a2078..292542a1 100644
--- a/vipra-ui/app/html/topics/show.html
+++ b/vipra-ui/app/html/topics/show.html
@@ -54,7 +54,7 @@
       </table>
     </div>
   </div>
-  <div ng-show="topic.dynamic">
+  <div ng-if="!topic.dynamic">
     <h3>Words <hide-link target="#words"/></h3>
     <div class="row" id="words">
       <div class="col-md-12">
@@ -81,5 +81,28 @@
       </div>
     </div>
   </div>
+  <div ng-if="topic.dynamic">
+    <h3>Relevance over time <hide-link target="#relevance"/></h3>
+    <div id="relevance">
+      <div class="row">
+        <div class="col-md-12">
+          <div class="well well-sm">
+            <label class="radio-inline">
+              <input type="radio" name="seqstyle" ng-model="seqstyle" value="absolute" ng-model-store="seqStyle" ng-model-default="'absolute'">Absolute
+            </label>
+            <label class="radio-inline">
+              <input type="radio" name="seqstyle" ng-model="seqstyle" value="relative" ng-model-store="seqStyle">Relative
+            </label>
+          </div>
+        </div>
+      </div>
+      <br>
+      <div class="row">
+        <div class="col-md-12">
+          <div class="area-chart" id="topic-seq" highcharts="topicSeq"></div>
+        </div>
+      </div>
+    </div> 
+  </div>
 </div>
 <div ng-cloak ui-view></div>
diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js
index df507477..fe5fbd1c 100644
--- a/vipra-ui/app/js/app.js
+++ b/vipra-ui/app/js/app.js
@@ -74,6 +74,15 @@
       }
     });
 
+    $stateProvider.state('articles.show.similar', {
+      url: '/similar',
+      templateUrl: 'html/articles/similar.html',
+      controller: 'ArticlesSimilarController',
+      ncyBreadcrumb: {
+        label: 'Similar'
+      }
+    });
+
     // states: topics
 
     $stateProvider.state('topics', {
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index b74f7791..0658190d 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -418,6 +418,36 @@
         $scope.topic = data;
         $scope.topicCreated = Vipra.formatDateTime($scope.topic.created);
         $scope.topicModified = Vipra.formatDateTime($scope.topic.modified);
+
+        if($scope.topic.dynamic) {
+          var relevances = [];
+          for(var i = 0, s; i < $scope.topic.sequences.length; i++) {
+            s = $scope.topic.sequences[i];
+            relevances.push([new Date(s.window.startDate).getTime(), s.relevance]);
+          }
+          $scope.topicSeq = {
+            chart: {
+              type: 'areaspline',
+              zoomType: 'x',
+              spacingLeft: 0,
+              spacingRight: 0
+            },
+            title: { text: '' },
+            xAxis: { 
+              type: 'datetime', 
+              title: { text: '' }
+            },
+            yAxis: { title: { text: 'Relevance' } },
+            tooltip: {
+              headerFormat: '',
+              pointFormat: '{point.x:%Y}: {point.y:.4f}'
+            },
+            legend: { enabled: false },
+            credits: { enabled: false },
+            plotOptions: { areaspline: { fillOpacity: 0.5 } },
+            series: [ { name: $scope.topic.name, data: relevances } ]
+          };
+        }
       }, function(err) {
         $scope.errors = err;
       });
diff --git a/vipra-util/src/main/java/de/vipra/util/Constants.java b/vipra-util/src/main/java/de/vipra/util/Constants.java
index dac5d0e5..d245aa3e 100644
--- a/vipra-util/src/main/java/de/vipra/util/Constants.java
+++ b/vipra-util/src/main/java/de/vipra/util/Constants.java
@@ -84,6 +84,12 @@ public class Constants {
 	 */
 	public static final double MINIMUM_RELATIVE_PROB = 0.01;
 
+	/**
+	 * The minimum share of a topic to be accepted for an article. Topic shares
+	 * are renormalized after rejecting topics below this threshold.
+	 */
+	public static final double MINIMUM_SHARE = 0.01;
+
 	/**
 	 * Dynamic minimum iterations. Used for dynamic topic modeling. Default 100.
 	 */
-- 
GitLab