From c42adf287edea6a9a8c6f9ea06eaafe846ba5c53 Mon Sep 17 00:00:00 2001
From: Eike Cochu <eike@cochu.com>
Date: Thu, 18 Feb 2016 14:36:47 +0100
Subject: [PATCH] updated info resource, added about page

updated info resource to return more info
added about page with description, license and infos
added moment.js to parse info dates and durations
---
 .../de/vipra/rest/resource/InfoResource.java  |  28 +++--
 vipra-ui/app/html/about.html                  | 101 ++++++++++++++++++
 vipra-ui/app/index.html                       |   8 ++
 vipra-ui/app/js/app.js                        |   6 ++
 vipra-ui/app/js/controllers.js                |  15 +++
 vipra-ui/app/js/factories.js                  |   4 +
 vipra-ui/app/less/app.less                    |   4 +
 vipra-ui/bower.json                           |   3 +-
 vipra-ui/gulpfile.js                          |   3 +-
 9 files changed, 163 insertions(+), 9 deletions(-)
 create mode 100644 vipra-ui/app/html/about.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 0b24151d..0ea760c5 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
@@ -11,10 +11,17 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
+import org.bson.types.ObjectId;
+
 import de.vipra.rest.model.Wrapper;
 import de.vipra.util.BuildInfo;
+import de.vipra.util.Config;
 import de.vipra.util.NestedMap;
 import de.vipra.util.StringUtils;
+import de.vipra.util.model.Article;
+import de.vipra.util.model.Topic;
+import de.vipra.util.model.Word;
+import de.vipra.util.service.MongoService;
 
 @Path("info")
 public class InfoResource {
@@ -22,13 +29,20 @@ public class InfoResource {
 	@Context
 	UriInfo uri;
 
-	private static final NestedMap info = new NestedMap();
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getInfo() {
+		Wrapper<NestedMap> res = new Wrapper<>();
+		NestedMap info = new NestedMap();
 
-	static {
 		try {
 			RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
 			Runtime rt = Runtime.getRuntime();
 			BuildInfo buildInfo = new BuildInfo();
+			Config config = Config.getConfig();
+			MongoService<Article, ObjectId> dbArticles = MongoService.getDatabaseService(config, Article.class);
+			MongoService<Topic, ObjectId> dbTopics = MongoService.getDatabaseService(config, Topic.class);
+			MongoService<Word, String> dbWords = MongoService.getDatabaseService(config, Word.class);
 
 			// vm info
 			info.put("vm.starttime", rb.getStartTime());
@@ -48,15 +62,15 @@ public class InfoResource {
 			info.put("app.gitsha1", buildInfo.getGitSHA1());
 			info.put("app.version", buildInfo.getVersion());
 			info.put("app.builddate", buildInfo.getBuildDate());
+
+			// database info
+			info.put("db.articles", dbArticles.count());
+			info.put("db.topics", dbTopics.count());
+			info.put("db.words", dbWords.count());
 		} catch (Exception e) {
 			info.put("error", e.getMessage());
 		}
-	}
 
-	@GET
-	@Produces(MediaType.APPLICATION_JSON)
-	public Response getInfo() {
-		Wrapper<NestedMap> res = new Wrapper<>();
 		return res.ok(info);
 	}
 }
diff --git a/vipra-ui/app/html/about.html b/vipra-ui/app/html/about.html
new file mode 100644
index 00000000..2f363e4a
--- /dev/null
+++ b/vipra-ui/app/html/about.html
@@ -0,0 +1,101 @@
+<div ui-view ng-cloak>
+  <div class="page-header">
+    <h1>About</h1>
+  </div>
+
+  <p><strong>Vipra</strong></p>
+
+  <p>Created by Eike Cochu</p>
+  
+  <h3>Description</h3>
+
+  <p>The Vipra application is a topic modeling based search system with a frontend web application, a backend REST service and a maintenance tool for data import and modeling. It attempts to leverage automatically discovered topic informations in document collections to ease collection browsing and organization. The search system relies on ElasticSearch and Apache Lucene.</p>
+
+  <p>This application was created by Eike Cochu for his master's degree thesis in computer science, 2015-2016 at the Freie Universit&auml;t in Berlin, Germany.</p>
+  
+  <h3>License</h3>
+
+  <p>The MIT License (MIT)</p>
+
+  <p>Copyright 2016 Eike Cochu</p>
+
+  <p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
+
+  <p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
+
+  <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
+
+  <hr>
+
+  <h3>Version</h3>
+
+  <table class="table table-bordered table-fixed">
+    <tbody>
+      <tr>
+        <th style="width:33%">Git Version</th>
+        <td ng-bind="::info.app.gitsha1"></td>
+      </tr>
+      <tr>
+        <th>Artifact Version</th>
+        <td ng-bind="::info.app.version"></td>
+      </tr>
+      <tr>
+        <th>Build date</th>
+        <td ng-bind="::buildDate"></td>
+      </tr>
+    </tbody>
+  </table>
+  
+  <h3>Host</h3>
+
+  <table class="table table-bordered table-fixed">
+    <tbody>
+      <tr>
+        <th style="width:33%">CPU Cores</th>
+        <td ng-bind="::info.host.cores"></td>
+      </tr>
+      <tr>
+        <th>Available memory</th>
+        <td ng-bind="::info.host.memory"></td>
+      </tr>
+      <tr>
+        <th>OS</th>
+        <td ng-bind-template="{{::info.host.os.name}} ({{::info.host.os.arch}}) {{::info.host.os.version}}"></td>
+      </tr>
+    </tbody>
+  </table>
+  
+  <h3>VM</h3>
+
+  <table class="table table-bordered table-fixed">
+    <tbody>
+      <tr>
+        <th style="width:33%">Java</th>
+        <td ng-bind-template="{{::info.vm.java.vendor}} {{::info.vm.java.version}}"></td>
+      </tr>
+      <tr>
+        <th>Start time</th>
+        <td ng-bind-template="{{::startTime}} (up {{::upTime}})"></td>
+      </tr>
+    </tbody>
+  </table>
+
+  <h3>Database</h3>
+
+  <table class="table table-bordered table-fixed">
+    <tbody>
+      <tr>
+        <th style="width:33%"># of articles</th>
+        <td ng-bind-template="{{::info.db.articles}}"></td>
+      </tr>
+      <tr>
+        <th># of topics</th>
+        <td ng-bind-template="{{::info.db.topics}}"></td>
+      </tr>
+      <tr>
+        <th># of words</th>
+        <td ng-bind-template="{{::info.db.words}}"></td>
+      </tr>
+    </tbody>
+  </table>
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/index.html b/vipra-ui/app/index.html
index ddbbc379..c13bd0d4 100644
--- a/vipra-ui/app/index.html
+++ b/vipra-ui/app/index.html
@@ -55,6 +55,14 @@
             <li ng-class="{active:$state.includes('topics')}"><a ui-sref="topics">Topics</a></li>
             <li ng-class="{active:$state.includes('words')}"><a ui-sref="words">Words</a></li>
           </ul>
+
+          <ul class="nav navbar-nav navbar-right">
+            <li ng-class="{active:$state.includes('about')}">
+              <a ui-sref="about">
+              <span class="glyphicon glyphicon-question-sign"></span>
+              </a>
+            </li>
+          </ul>
         </div><!-- /.navbar-collapse -->
       </div><!-- /.container-fluid -->
     </nav>
diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js
index d44403e7..ec274b7d 100644
--- a/vipra-ui/app/js/app.js
+++ b/vipra-ui/app/js/app.js
@@ -28,6 +28,12 @@
       controller: 'IndexController'
     });
 
+    $stateProvider.state('about', {
+      url: '/about',
+      templateUrl: Vipra.const.tplBase + '/about.html',
+      controller: 'AboutController'
+    });
+
     $stateProvider.state('network', {
       url: '/network/:type/:id',
       templateUrl: Vipra.const.tplBase + '/network.html',
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index 49ac6552..739050f1 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -50,6 +50,21 @@
 
   }]);
 
+  /**
+   * About controller
+   */
+  app.controller('AboutController', ['$scope', 'InfoFactory',
+    function($scope, InfoFactory) {
+
+    InfoFactory.get(function(response) {
+      $scope.info = response.data;
+      $scope.buildDate = Vipra.formatDateTime(moment($scope.info.app.builddate, 'YYMMDD_HHmm').toDate());
+      $scope.startTime = Vipra.formatDateTime(moment($scope.info.vm.starttime, 'x').toDate());
+      $scope.upTime = moment.duration($scope.info.vm.uptime).humanize();
+    });
+
+  }]);
+
   /**
    * Network controller
    */
diff --git a/vipra-ui/app/js/factories.js b/vipra-ui/app/js/factories.js
index 9224eb92..b6f20328 100644
--- a/vipra-ui/app/js/factories.js
+++ b/vipra-ui/app/js/factories.js
@@ -33,6 +33,10 @@
     });
   }]);
 
+  app.factory('InfoFactory', ['$resource', function($resource) {
+    return $resource(Vipra.config.restUrl + '/info');
+  }]);
+
   // https://gist.github.com/Fluidbyte/4718380
   app.factory('Store', ['$state', function($state) {
     return function(key, value) {
diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less
index 613e9dca..e046f339 100644
--- a/vipra-ui/app/less/app.less
+++ b/vipra-ui/app/less/app.less
@@ -216,6 +216,10 @@ ul.dashed {
   min-height: 48px;
 }
 
+.table-fixed {
+  table-layout: fixed;
+}
+
 [sort-by] {
   .noselect;
   cursor: pointer;
diff --git a/vipra-ui/bower.json b/vipra-ui/bower.json
index 867ff599..53906a9c 100644
--- a/vipra-ui/bower.json
+++ b/vipra-ui/bower.json
@@ -26,6 +26,7 @@
     "highcharts": "^4.2.2",
     "nprogress": "^0.2.0",
     "vis": "https://github.com/almende/vis.git#^4.14.0",
-    "angular-websocket": "^1.0.14"
+    "angular-websocket": "^1.0.14",
+    "moment": "^2.11.2"
   }
 }
diff --git a/vipra-ui/gulpfile.js b/vipra-ui/gulpfile.js
index c694b87e..0ab4b7f6 100644
--- a/vipra-ui/gulpfile.js
+++ b/vipra-ui/gulpfile.js
@@ -18,7 +18,8 @@ var assets = {
     'bower_components/bootstrap/dist/js/bootstrap.min.js',
     'bower_components/highcharts/highcharts.js',
     'bower_components/vis/dist/vis.min.js',
-    'bower_components/nprogress/nprogress.js'
+    'bower_components/nprogress/nprogress.js',
+    'bower_components/moment/min/moment.min.js'
   ],
   css: [
     'bower_components/bootstrap/dist/css/bootstrap.min.css',
-- 
GitLab