Merge "Show history of test results by date"
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/ConfigInfo.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/ConfigInfo.java
index c05a51f..4538e86 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/ConfigInfo.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/ConfigInfo.java
@@ -17,9 +17,11 @@
 import com.google.gwt.core.client.JavaScriptObject;
 
 public class ConfigInfo extends JavaScriptObject {
+  final native boolean showJobsSummaryPanel() /*-{ return this.show_jobs_summary_panel ? true : false; }-*/;
   final native boolean showJobsPanel() /*-{ return this.show_jobs_panel ? true : false; }-*/;
   final native boolean showJobsDropDownPanel() /*-{ return this.show_jobs_drop_down_panel ? true : false; }-*/;
 
+  final native void setShowJobsSummaryPanel(boolean s) /*-{ this.show_jobs_summary_panel = s; }-*/;
   final native void setShowJobsPanel(boolean s) /*-{ this.show_jobs_panel = s; }-*/;
   final native void setShowJobsDropDownPanel(boolean s) /*-{ this.show_jobs_drop_down_panel = s; }-*/;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsPanel.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsPanel.java
index e8ceb89..727da7f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsPanel.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsPanel.java
@@ -61,7 +61,7 @@
           @Override
           public void onSuccess(NativeMap<VerificationInfo> result) {
             if (!result.isEmpty()) {
-              final String patchsetId = change.id() + "," + rev.id();
+              final String patchsetId = change._number() + "/" + rev.id();
               display(patchsetId, result);
             }
           }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsScreen.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsScreen.java
index b42b40d..508ed1a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsScreen.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsScreen.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.plugin.client.Plugin;
 import com.google.gerrit.plugin.client.rpc.RestApi;
 import com.google.gerrit.plugin.client.screen.Screen;
-import com.google.gwt.http.client.URL;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.FlexTable;
@@ -30,19 +29,20 @@
 public class JobsScreen extends VerticalPanel {
   static class Factory implements Screen.EntryPoint {
     @Override
-    public void onLoad(Screen screen) {
-      screen.setPageTitle("Reports for patchset " + screen.getToken(1));
-      screen.show(new JobsScreen(screen.getToken(1)));
+    public void onLoad(final Screen screen) {
+      // get change and revision number from passed in patchsetId of form
+      // $changeNumber/$revisionNumber
+      String input = screen.getToken(1);
+      String[] patchsetId = input.split("/");
+      final String changeNumber = patchsetId[0];
+      final String revisionNumber = patchsetId[1];
+      screen.setPageTitle("Report History for Change " + input);
+      screen.show(new JobsScreen(changeNumber, revisionNumber));
     }
   }
 
-  JobsScreen(final String patchsetId) {
-    setStyleName("verifystatus-panel");
-    String[] id = patchsetId.split(",");
-    String decodedChagneId = URL.decodePathSegment(id[0]);
-    String revId = id[1];
-
-    new RestApi("changes").id(decodedChagneId).view("revisions").id(revId)
+  JobsScreen(String changeNumber, String revisionNumber) {
+    new RestApi("changes").id(changeNumber).view("revisions").id(revisionNumber)
         .view(Plugin.get().getPluginName(), "verifications")
         .addParameter("sort", "DATE")
         .get(new AsyncCallback<NativeMap<VerificationInfo>>() {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsSummaryPanel.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsSummaryPanel.java
new file mode 100644
index 0000000..fc495df
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsSummaryPanel.java
@@ -0,0 +1,133 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.verifystatus.client;
+
+import com.google.gerrit.client.GerritUiExtensionPoint;
+import com.google.gerrit.client.info.ChangeInfo;
+import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
+import com.google.gerrit.client.rpc.NativeMap;
+import com.google.gerrit.plugin.client.Plugin;
+import com.google.gerrit.plugin.client.extension.Panel;
+import com.google.gerrit.plugin.client.rpc.RestApi;
+import com.google.gwt.http.client.URL;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+
+import com.google.gwt.user.client.ui.Label;
+
+/**
+ * Extension for change screen that displays job summary table.
+ */
+public class JobsSummaryPanel extends FlowPanel {
+  static class Factory implements Panel.EntryPoint {
+    @Override
+    public void onLoad(Panel panel) {
+      RevisionInfo rev =
+          panel.getObject(GerritUiExtensionPoint.Key.REVISION_INFO).cast();
+      if (rev.isEdit()) {
+        return;
+      }
+
+      panel.setWidget(new JobsSummaryPanel(panel));
+    }
+  }
+
+  private final static String COLOR_GREEN = "#060";
+  private final static String COLOR_RED = "#F00";
+
+  JobsSummaryPanel(Panel panel) {
+    final ChangeInfo change =
+        panel.getObject(GerritUiExtensionPoint.Key.CHANGE_INFO).cast();
+    String decodedChangeId = URL.decodePathSegment(change.id());
+    final RevisionInfo rev =
+        panel.getObject(GerritUiExtensionPoint.Key.REVISION_INFO).cast();
+    new RestApi("changes").id(decodedChangeId).view("revisions").id(rev.id())
+        .view(Plugin.get().getPluginName(), "verifications")
+        .addParameter("sort", "REPORTER").addParameter("filter", "CURRENT")
+        .get(new AsyncCallback<NativeMap<VerificationInfo>>() {
+          @Override
+          public void onSuccess(NativeMap<VerificationInfo> result) {
+            if (!result.isEmpty()) {
+              display(result);
+            }
+          }
+
+          @Override
+          public void onFailure(Throwable caught) {
+            // never invoked
+          }
+        });
+  }
+
+  private void display(NativeMap<VerificationInfo> jobs) {
+    Grid g = createGrid(2, 3);
+    g.setText(0, 0, "Passed");
+    g.setText(0, 1, "Failed");
+    g.setText(0, 2, "Unstable");
+
+    int pass = 0;
+    int fail = 0;
+    int unstable = 0;
+    for (String key : jobs.keySet()) {
+      int value = jobs.get(key).value();
+      if (value > 0) {
+        pass++;
+      } else if (value < 0) {
+        fail++;
+      } else {
+        unstable++;
+      }
+    }
+
+    Label passedLbl = new Label(Integer.toString(pass));
+    passedLbl.getElement().getStyle().setColor(COLOR_GREEN);
+    g.setWidget(1, 0, passedLbl);
+    Label failedLbl = new Label(Integer.toString(fail));
+    failedLbl.getElement().getStyle().setColor(COLOR_RED);
+    g.setWidget(1, 1, failedLbl);
+    Label unstableLbl = new Label(Integer.toString(unstable));
+    g.setWidget(1, 2, unstableLbl);
+    add(g);
+  }
+
+  private static Grid createGrid(int rows, int columns) {
+    Grid g = new Grid(rows, columns);
+    g.addStyleName("infoBlock");
+    g.addStyleName("changeTable");
+
+    CellFormatter fmt = g.getCellFormatter();
+
+    for (int c = 0; c < columns; c++) {
+      fmt.addStyleName(0, c, "header");
+      fmt.addStyleName(0, c, "topmost");
+    }
+
+    for (int r = 1; r < rows; r++) {
+      fmt.addStyleName(r, 0, "leftMostCell");
+
+      for (int c = 1; c < columns; c++) {
+        fmt.addStyleName(r, c, "dataCell");
+      }
+    }
+
+    for (int c = 0; c < columns; c++) {
+      fmt.addStyleName(rows - 1, c, "bottomheader");
+    }
+
+    return g;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/VerifyStatusPlugin.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/VerifyStatusPlugin.java
index 0446757..2e2575c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/VerifyStatusPlugin.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/VerifyStatusPlugin.java
@@ -34,6 +34,11 @@
         .get(new AsyncCallback<ConfigInfo>() {
           @Override
           public void onSuccess(ConfigInfo info) {
+            if (info.showJobsSummaryPanel()) {
+              Plugin.get().panel(
+                  GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
+                  new JobsSummaryPanel.Factory());
+            }
             if (info.showJobsPanel()) {
               Plugin.get().panel(
                   GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetConfig.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetConfig.java
index bca56b7..8733fb0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetConfig.java
@@ -36,11 +36,13 @@
     ConfigInfo info = new ConfigInfo();
     info.showJobsPanel = cfg.getBoolean("showJobsPanel", true);
     info.showJobsDropDownPanel = cfg.getBoolean("showJobsDropDownPanel", true);
+    info.showJobsSummaryPanel = cfg.getBoolean("showJobsSummaryPanel", true);
     return info;
   }
 
   public static class ConfigInfo {
     Boolean showJobsPanel;
     Boolean showJobsDropDownPanel;
+    Boolean showJobsSummaryPanel;
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/PutConfig.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/PutConfig.java
index 8ca1c4a..9fee1a1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/PutConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/PutConfig.java
@@ -40,6 +40,7 @@
   public static class Input {
     public Boolean showJobsPanel;
     public Boolean showJobsDropDownPanel;
+    public Boolean showJobsSummaryPanel;
   }
 
   private final PluginConfigFactory cfgFactory;
@@ -77,6 +78,12 @@
     } else {
       cfg.unset("plugin", pluginName, "showJobsDropDownPanel");
     }
+    if (input.showJobsSummaryPanel != null) {
+      cfg.setBoolean("plugin", pluginName, "showJobsSummaryPanel",
+          input.showJobsSummaryPanel);
+    } else {
+      cfg.unset("plugin", pluginName, "showJobsSummaryPanel");
+    }
 
     cfg.save();
     cfgFactory.getFromGerritConfig(pluginName, true);
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 55f030a..3f31ce9 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -1,25 +1,55 @@
-The @PLUGIN@ plugin allows CI system to report build and test results back to
-Gerrit. The reports are stored per patchset and are saved onto an external
-database.  Included with this plugin are a set of SSH and REST APIs to automate
-the reporting of test results.  This plugin will also handle displaying of the
-job results on the Gerrit UI.
+The @PLUGIN@ plugin seperates test results from comments.  It provides a
+separate channel for Gerrit to store test metadata and view them on the
+Gerrit UI.  The metadata can be stored in the Gerrit database or in a
+completely separate datastore.
 
+Typically a CI system, like [Jenkins], will post test metadata to Gerrit as a
+comment.  It looks something like this..
+
+```
+Jenkins                     Aug 24 3:57 PM
+Patch Set 5: Verified-1
+Build Failed
+http://jenkins.acme.com/job/python-27-wacko/4/ : FAILURE
+http://jenkins.acme.com/job/python-27-pep8/6/ : SUCCESS
+http://jenkins.acme.com/job/python-27/4/ : SUCCESS
+..
+```
+
+The problem occurs when there are many jobs verifying each patchset.  An
+increased number of reports from Jenkins can overwhelm reviewers who must look
+through the comments and decipher which replies to pay attention to.  After a
+while the CI reports become more like spam.  Usually human reviewers only
+want to view comments that have been posted by other humans reviewers not bots.
+Since humans and bots must share a communications (or comments) channel it's
+difficult to separate the two competing pieces of data for the purposes of
+reviewing, visualizations and even analytics.
+
+This is where the @PLUGIN@ plugin comes in.  It creates a separate channel for
+bots to report info to Gerrit.  It provides a separate database to store the
+info and it provides a set of SSH commands and REST endpoints to save
+and retrieve the data.  It also provides UI components to view the data
+outside of the Gerrit comments.
 
 ### <a id="workflow"></a>
 ### `Workflow`
 
-A typical workflow for @PLUGIN@ plugin:
+Any CI system can be used with the @PLUGIN@ plugin.
 
+A typical workflow:
 1. CI system triggers on a new patchset.
 2. CI system executes build jobs.
-3. CI system reports build job results with @PLUGIN@ [ssh command](cmd-save.md)
-or [rest-api](rest-api-changes.md).
+3. CI system reports build job results with the @PLUGIN@
+[ssh command](cmd-save.md) or [rest-api](rest-api-changes.md).
 4. CI system reports a combined `Verfiied` vote based on the results of each job
 using the review [ssh command](../../../Documentation/cmd-review.html) or
-[rest-api](../../../Documentation/rest-api-changes.html#set-review).
+[rest api](../../../Documentation/rest-api-changes.html#set-review).
 5. Users can view per patch job results on Gerrit UI or retrieve the results
 using the @PLUGIN@ rest api.
 
+_NOTE_: The [verify-status-reporter] plugin documentation contains specific
+instructions on setting up this workflow with the Jenkins CI system.
+
 
 ### <a id="change-screen"></a>
 ### `Change Screen`
@@ -64,13 +94,16 @@
 |:----------------------|:----------|
 |showJobsPanel          | Whether jobs panel should be displayed (default to true)|
 |showJobsDropDownPanel  | Whether jobs drop down panel should be displayed (default to true)|
+|showJobsSummaryPanel   | Whether jobs summary panel should be displayed (default to true)|
 
 
 #### Example
 
 ```
 [plugin "@PLUGIN@"]
-   showJobsPanel = false
    showJobsDropDownPanel = false
 ```
 
+
+[Jenkins]: https://jenkins.io
+[verify-status-reporter]: https://wiki.jenkins-ci.org/display/JENKINS/Verify+Status+Reporter
diff --git a/src/main/resources/Documentation/database.md b/src/main/resources/Documentation/database.md
index cdea76d..852e54f 100644
--- a/src/main/resources/Documentation/database.md
+++ b/src/main/resources/Documentation/database.md
@@ -3,20 +3,20 @@
 
 DESCRIPTION
 -----------
-CI data is stored in a [CI database](#supported-dbs) which can be in the Gerrit
-review database or a completely separate database.
+Test metadata is stored in a [CI database](#supported-dbs) which can be in the
+Gerrit review database or a completely separate database.
 
 ### <a id="schema-initialization"> @PLUGIN@ schema initialization
 
 Schema initialization
 ---------------------
 
-The database is initialized and the schema is created with during an initial
+The database is initialized and the schema is created during an initial
 site creation.
 
 ```
 *** SQL Database for @PLUGIN@
-*** 
+***
 
 Database server type           [h2]: ?
        Supported options are:
@@ -35,13 +35,13 @@
 Schema upgrade
 --------------
 
-Schema upgrade takes place in init plugin step:
+Schema upgrade takes place in the init plugin step:
 
 ```
 *** SQL Database for @PLUGIN@
-*** 
+***
 
-Database server type           [h2]: 
+Database server type           [h2]:
 
 Upgrading schema to 2 ...
 Migrating data to schema 2 ...
@@ -56,8 +56,8 @@
 (../../../Documentation/config-gerrit.html#_file_code_etc_gerrit_config_code)
 file
 
-The [database section](#database-params) configures where the @PLUGIN@ stores
-per patchset CI results.
+The [database section](#database-params) configures where the @PLUGIN@ plugin
+stores per patchset test results.
 
 ```
 [plugin "@PLUGIN@"]
@@ -105,7 +105,7 @@
 Acccesing Database
 ------------------
 
-@PLUGIN@ provides an administrative interface to the database.
+@PLUGIN@ plugin provides an administrative interface to the database.
 
 'ssh' -p <port> <host> '@PLUGIN@ gsql' [--format {PRETTY | JSON | JSON_SINGLE}] [-c QUERY]
 
diff --git a/src/main/resources/Documentation/rest-api-config.md b/src/main/resources/Documentation/rest-api-config.md
index eaf22e5..c4936a5 100644
--- a/src/main/resources/Documentation/rest-api-config.md
+++ b/src/main/resources/Documentation/rest-api-config.md
@@ -71,6 +71,7 @@
 |:------------------------|:----------|
 |show_jobs_panel          | Whether jobs panel should be displayed|
 |show_jobs_drop_down_panel| Whether jobs drop down panel should be displayed|
+|show_jobs_summary_panel  | Whether jobs summary panel should be displayed|
 
 
 SEE ALSO