Add a job summary table

Add a job summary table to the change screen showing the number of
passed, failed and unstable jobs.  This summary info is only
for the most current report.  This table can be shown or
hidden by setting a global configuration.

Change-Id: Ieb86101217c23c91e3f5cb208577031611bc6295
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/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 fb16e7f..3f31ce9 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -94,13 +94,13 @@
 |:----------------------|:----------|
 |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
 ```
 
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