rebaseline_server: allow substring filtering for builder and test
authorepoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 29 Oct 2013 15:49:40 +0000 (15:49 +0000)
committerepoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 29 Oct 2013 15:49:40 +0000 (15:49 +0000)
If the user clicks on a particular value for one of these fields, that field's
value will go into the filtering box (so you will only see results matching
that field value).

(SkipBuildbotRuns)

R=borenet@google.com

Review URL: https://codereview.chromium.org/47423002

git-svn-id: http://skia.googlecode.com/svn/trunk@12000 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/rebaseline_server/static/loader.js
gm/rebaseline_server/static/view.html

index 8cfdfec..04b0236 100644 (file)
@@ -17,7 +17,7 @@ Loader.filter(
   'removeHiddenItems',
   function() {
     return function(unfilteredItems, hiddenResultTypes, hiddenConfigs,
-                    viewingTab) {
+                    builderSubstring, testSubstring, viewingTab) {
       var filteredItems = [];
       for (var i = 0; i < unfilteredItems.length; i++) {
         var item = unfilteredItems[i];
@@ -26,6 +26,8 @@ Loader.filter(
         // Besides, I don't think we have access to $scope in here...
         if (!(true == hiddenResultTypes[item.resultType]) &&
             !(true == hiddenConfigs[item.config]) &&
+            !(-1 == item.builder.indexOf(builderSubstring)) &&
+            !(-1 == item.test.indexOf(testSubstring)) &&
             (viewingTab == item.tab)) {
           filteredItems.push(item);
         }
@@ -99,7 +101,14 @@ Loader.controller(
           'no-comparison': true,
           'succeeded': true,
         };
+        $scope.allResultTypes = Object.keys(data.categories['resultType']);
         $scope.hiddenConfigs = {};
+        $scope.allConfigs = Object.keys(data.categories['config']);
+
+        // Associative array of partial string matches per category.
+        $scope.categoryValueMatch = {};
+        $scope.categoryValueMatch.builder = "";
+        $scope.categoryValueMatch.test = "";
 
         $scope.updateResults();
         $scope.loadingMessage = "";
@@ -239,6 +248,8 @@ Loader.controller(
                     $scope.testData,
                     $scope.hiddenResultTypes,
                     $scope.hiddenConfigs,
+                    $scope.categoryValueMatch.builder,
+                    $scope.categoryValueMatch.test,
                     $scope.viewingTab
                 ),
                 $scope.sortColumn);
@@ -269,6 +280,46 @@ Loader.controller(
       $scope.updateResults();
     }
 
+    /**
+     * Set $scope.categoryValueMatch[name] = value, and update results.
+     *
+     * @param name
+     * @param value
+     */
+    $scope.setCategoryValueMatch = function(name, value) {
+      $scope.categoryValueMatch[name] = value;
+      $scope.updateResults();
+    }
+
+    /**
+     * Update $scope.hiddenResultTypes so that ONLY this resultType is showing,
+     * and update the visible results.
+     *
+     * @param resultType
+     */
+    $scope.showOnlyResultType = function(resultType) {
+      $scope.hiddenResultTypes = {};
+      // TODO(epoger): Maybe change $scope.allResultTypes to be a Set like
+      // $scope.hiddenResultTypes (rather than an array), so this operation is
+      // simpler (just assign or add allResultTypes to hiddenResultTypes).
+      $scope.toggleValuesInSet($scope.allResultTypes, $scope.hiddenResultTypes);
+      $scope.toggleValueInSet(resultType, $scope.hiddenResultTypes);
+      $scope.updateResults();
+    }
+
+    /**
+     * Update $scope.hiddenConfigs so that ONLY this config is showing,
+     * and update the visible results.
+     *
+     * @param config
+     */
+    $scope.showOnlyConfig = function(config) {
+      $scope.hiddenConfigs = {};
+      $scope.toggleValuesInSet($scope.allConfigs, $scope.hiddenConfigs);
+      $scope.toggleValueInSet(config, $scope.hiddenConfigs);
+      $scope.updateResults();
+    }
+
 
     //
     // Operations for sending info back to the server.
@@ -392,6 +443,19 @@ Loader.controller(
       }
     }
 
+    /**
+     * For each value in valueArray, call toggleValueInSet(value, set).
+     *
+     * @param valueArray
+     * @param set
+     */
+    $scope.toggleValuesInSet = function(valueArray, set) {
+      var arrayLength = valueArray.length;
+      for (var i = 0; i < arrayLength; i++) {
+        $scope.toggleValueInSet(valueArray[i], set);
+      }
+    }
+
 
     //
     // Array operations; similar to our Set operations, but operate on a
index 818d7de..3058bc8 100644 (file)
@@ -33,9 +33,6 @@
           new results and tell the user when new results are available
           (the user can reload the page if he wants to see them).
         </li><li>
-          Add ability to filter builder and test names
-          (using a free-form text field, with partial string match)
-        </li><li>
           Add pixel diffs, and sorting by percentage of different pixels
         </li><li>
           Add ability to sort/filter by reviewed-by-human. Depends on
             http://jsfiddle.net/vojtajina/js64b/14/
           </a>
         </li><li>
+          For the text-filtered categories, allow regular expression matching
+          (or Unix-style wildcard matching) instead of simple substring match?
+          <!-- In order to do this efficiently, we should probably do the
+               expression matching over the list of categories returned,
+               use that to generate a list of category values that fulfill the
+               regex, and when filtering the results just look for category
+               values within that list. -->
+        </li><li>
           Right now, if you change which column is used to
           sort the data, the column widths may fluctuate based on the
           longest string <i>currently visible</i> within the top {{displayLimit}}
@@ -80,7 +85,7 @@
     <!-- We only show the filters/settings table on the Unfiled tab. -->
     <table ng-hide="viewingTab != defaultTab" border="1">
     <tr>
-      <th colspan="2">
+      <th colspan="4">
         Filters
       </th>
       <th>
                  ng-click="toggleValueInSet(resultType, hiddenResultTypes); setUpdatesPending(true)">
           {{resultType}} ({{count}})<br>
         </label>
+        <button ng-click="hiddenResultTypes = {}; updateResults()">
+          all
+        </button>
+        <button ng-click="hiddenResultTypes = {}; toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()">
+          none
+        </button>
+        <button ng-click="toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()">
+          toggle
+        </button>
+      </td>
+      <td ng-repeat="category in ['builder', 'test']">
+        {{category}}
+        <br>
+        <input type="text"
+               ng-model="categoryValueMatch[category]"
+               ng-change="setUpdatesPending(true)"/>
+        <br>
+        <button ng-click="setCategoryValueMatch(category, '')"
+                ng-disabled="('' == categoryValueMatch[category])">
+          clear (show all)
+        </button>
       </td>
       <td>
         config<br>
                  ng-click="toggleValueInSet(config, hiddenConfigs); setUpdatesPending(true)">
           {{config}} ({{count}})<br>
         </label>
+        <button ng-click="hiddenConfigs = {}; updateResults()">
+          all
+        </button>
+        <button ng-click="hiddenConfigs = {}; toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()">
+          none
+        </button>
+        <button ng-click="toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()">
+          toggle
+        </button>
       </td>
       <td><table>
         <tr><td>
             <!-- item-selection checkbox column -->
           </th>
         </tr>
+
+        <!-- For most columns... if the user clicks on the cell, and we are on
+             the default tab, update the filter to only show results with the
+             same value for this category.
+             This is made a bit tricky by the fact that AngularJS expressions
+             do not allow control flow statements.  See
+             http://docs.angularjs.org/guide/expression -->
         <tr ng-repeat="result in limitedTestData">
-          <td ng-repeat="categoryName in ['resultType', 'builder', 'test', 'config']">
+          <td ng-click="(viewingTab != defaultTab) || showOnlyResultType(result.resultType)">
+            {{result.resultType}}
+          </td>
+          <td ng-repeat="categoryName in ['builder', 'test']"
+              ng-click="(viewingTab != defaultTab) || setCategoryValueMatch(categoryName, result[categoryName])">
             {{result[categoryName]}}
           </td>
+          <td ng-click="(viewingTab != defaultTab) || showOnlyConfig(result.config)">
+            {{result.config}}
+          </td>
           <td>
             <a ng-repeat="bug in result['bugs']"
                href="https://code.google.com/p/skia/issues/detail?id={{bug}}"