aboutsummaryrefslogtreecommitdiffhomepage
path: root/gm
diff options
context:
space:
mode:
authorGravatar epoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-10-02 18:57:48 +0000
committerGravatar epoger@google.com <epoger@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-10-02 18:57:48 +0000
commit5f2bb002bd343e0c6e54455e9203883d7769ff67 (patch)
tree11fc7f5d01aa8b36c69954d888238fd96be3d0f8 /gm
parent55f5682523f66c7a0d9696ff9f7c50cc8a6d6b45 (diff)
More improvements to HTTP baseline viewer (for GM results)
(SkipBuildbotRuns) R=borenet@google.com, bsalomon@google.com Review URL: https://codereview.chromium.org/25555003 git-svn-id: http://skia.googlecode.com/svn/trunk@11581 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'gm')
-rwxr-xr-xgm/rebaseline_server/results.py36
-rw-r--r--gm/rebaseline_server/static/loader.js87
-rw-r--r--gm/rebaseline_server/static/view.html166
3 files changed, 225 insertions, 64 deletions
diff --git a/gm/rebaseline_server/results.py b/gm/rebaseline_server/results.py
index 1336097f1f..6ac397eceb 100755
--- a/gm/rebaseline_server/results.py
+++ b/gm/rebaseline_server/results.py
@@ -124,6 +124,13 @@ class Results(object):
"""
test_data = []
category_dict = {}
+ Results._EnsureIncludedInCategoryDict(category_dict, 'resultType', [
+ gm_json.JSONKEY_ACTUALRESULTS_FAILED,
+ gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED,
+ gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON,
+ gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED,
+ ])
+
for builder in sorted(actual_builder_dicts.keys()):
actual_results_for_this_builder = (
actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
@@ -205,17 +212,13 @@ class Results(object):
"expectedHashDigest": str(expected_image[1]),
}
Results._AddToCategoryDict(category_dict, results_for_this_test)
-
- # TODO(epoger): For now, don't include succeeded results in the raw
- # data. There are so many of them that they make the client too slow.
- if updated_result_type != gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED:
- test_data.append(results_for_this_test)
+ test_data.append(results_for_this_test)
return {"categories": category_dict, "testData": test_data}
@staticmethod
def _AddToCategoryDict(category_dict, test_results):
- """Add test_results to the category dictionary we are building
- (see documentation of self.GetAll() for the format of this dictionary).
+ """Add test_results to the category dictionary we are building.
+ (See documentation of self.GetAll() for the format of this dictionary.)
params:
category_dict: category dict-of-dicts to add to; modify this in-place
@@ -235,3 +238,22 @@ class Results(object):
if not category_dict[category].get(category_value):
category_dict[category][category_value] = 0
category_dict[category][category_value] += 1
+
+ @staticmethod
+ def _EnsureIncludedInCategoryDict(category_dict,
+ category_name, category_values):
+ """Ensure that the category name/value pairs are included in category_dict,
+ even if there aren't any results with that name/value pair.
+ (See documentation of self.GetAll() for the format of this dictionary.)
+
+ params:
+ category_dict: category dict-of-dicts to modify
+ category_name: category name, as a string
+ category_values: list of values we want to make sure are represented
+ for this category
+ """
+ if not category_dict.get(category_name):
+ category_dict[category_name] = {}
+ for category_value in category_values:
+ if not category_dict[category_name].get(category_value):
+ category_dict[category_name][category_value] = 0
diff --git a/gm/rebaseline_server/static/loader.js b/gm/rebaseline_server/static/loader.js
index d08fdccbf6..8b5374a323 100644
--- a/gm/rebaseline_server/static/loader.js
+++ b/gm/rebaseline_server/static/loader.js
@@ -7,16 +7,99 @@ var Loader = angular.module(
'Loader',
[]
);
+
+// TODO(epoger): Combine ALL of our filtering operations (including
+// truncation) into this one filter, so that runs most efficiently?
+// (We would have to make sure truncation still took place after
+// sorting, though.)
+Loader.filter(
+ 'removeHiddenItems',
+ function() {
+ return function(unfilteredItems, hiddenResultTypes, hiddenConfigs) {
+ var filteredItems = [];
+ for (var i = 0; i < unfilteredItems.length; i++) {
+ var item = unfilteredItems[i];
+ if ((hiddenResultTypes.indexOf(item.resultType) < 0) &&
+ (hiddenConfigs.indexOf(item.config) < 0)) {
+ filteredItems.push(item);
+ }
+ }
+ return filteredItems;
+ };
+ }
+);
+
Loader.controller(
'Loader.Controller',
- function($scope, $http) {
+ function($scope, $http, $filter) {
$http.get("/results/all").then(
function(response) {
$scope.categories = response.data.categories;
$scope.testData = response.data.testData;
$scope.sortColumn = 'test';
- $scope.showResultsOfType = 'failed';
+
+ $scope.hiddenResultTypes = [
+ 'failure-ignored', 'no-comparison', 'succeeded'];
+ $scope.hiddenConfigs = [];
+
+ $scope.updateResults();
}
);
+
+ $scope.isHiddenResultType = function(thisResultType) {
+ return ($scope.hiddenResultTypes.indexOf(thisResultType) >= 0);
+ }
+ $scope.toggleHiddenResultType = function(thisResultType) {
+ var i = $scope.hiddenResultTypes.indexOf(thisResultType);
+ if (i >= 0) {
+ $scope.hiddenResultTypes.splice(i, 1);
+ } else {
+ $scope.hiddenResultTypes.push(thisResultType);
+ }
+ $scope.areUpdatesPending = true;
+ }
+
+ // TODO(epoger): Rather than maintaining these as hard-coded
+ // variants of isHiddenResultType and toggleHiddenResultType, we
+ // should create general-purpose functions that can work with ANY
+ // category.
+ // But for now, I wanted to see this working. :-)
+ $scope.isHiddenConfig = function(thisConfig) {
+ return ($scope.hiddenConfigs.indexOf(thisConfig) >= 0);
+ }
+ $scope.toggleHiddenConfig = function(thisConfig) {
+ var i = $scope.hiddenConfigs.indexOf(thisConfig);
+ if (i >= 0) {
+ $scope.hiddenConfigs.splice(i, 1);
+ } else {
+ $scope.hiddenConfigs.push(thisConfig);
+ }
+ $scope.areUpdatesPending = true;
+ }
+
+ $scope.updateResults = function() {
+ $scope.displayLimit = $scope.displayLimitPending;
+ // TODO(epoger): Every time we apply a filter, AngularJS creates
+ // another copy of the array. Is there a way we can filter out
+ // the items as they are displayed, rather than storing multiple
+ // array copies? (For better performance.)
+ $scope.filteredTestData =
+ $filter("orderBy")(
+ $filter("removeHiddenItems")(
+ $scope.testData,
+ $scope.hiddenResultTypes,
+ $scope.hiddenConfigs
+ ),
+ $scope.sortColumn);
+ $scope.limitedTestData = $filter("limitTo")(
+ $scope.filteredTestData, $scope.displayLimit);
+ $scope.imageSize = $scope.imageSizePending;
+ $scope.areUpdatesPending = false;
+ }
+
+ $scope.sortResultsBy = function(sortColumn) {
+ $scope.sortColumn = sortColumn;
+ $scope.updateResults();
+ }
}
);
diff --git a/gm/rebaseline_server/static/view.html b/gm/rebaseline_server/static/view.html
index 5913e5c2ca..cd3ab15895 100644
--- a/gm/rebaseline_server/static/view.html
+++ b/gm/rebaseline_server/static/view.html
@@ -18,67 +18,122 @@
<!-- TODO(epoger): Add some indication of how old the
expected/actual data is -->
- Settings:
- <ul>
- <!-- TODO(epoger): Now that we get multiple result types in a single
- fetch, modify the UI: add a column showing resultType, and allow
- the user to sort/filter on that column just like all the
- others. -->
- <li>show results of type
- <select ng-model="showResultsOfType"
- ng-init="showResultsOfType='failed'">
- <option ng-repeat="(resultType, count) in categories['resultType']"
- value="{{resultType}}">
- {{resultType}} ({{count}})
- </option>
- </select>
- <!--
- TODO(epoger): See results.py: for now, I have disabled
- returning succeeded tests as part of the JSON, because it
- makes the returned JSON too big (and slows down the client).
+ <em ng-hide="categories">
+ Loading data, please wait...
+ </em>
- Also, we should use some sort of lazy-loading technique
- (e.g. http://www.appelsiini.net/projects/lazyload ), so that
- the images are only loaded as they become viewable...
- that will help with long lists like resultType='no-comparison'.
- -->
- <br>
- TODO(epoger): 'no-comparison' will probably take forever;
- see HTML source for details
- <br>
- TODO(epoger): 'succeeded' will not show any results;
- see HTML source for details
- </li>
- <li>image size
- <input type="text" ng-model="imageSizePending"
- ng-init="imageSizePending=100; imageSize=100"
- maxlength="4"/>
- <button ng:click="imageSize=imageSizePending">apply</button>
- </li>
- </ul>
+ <div ng-hide="!categories">
+ <table border="1">
+ <tr>
+ <th colspan="2">
+ Filters
+ </th>
+ <th>
+ Settings
+ </th>
+ </tr>
+ <tr valign="top">
+ <td>
+ resultType<br>
+ <label ng-repeat="(resultType, count) in categories['resultType']">
+ <input type="checkbox"
+ name="resultTypes"
+ value="{{resultType}}"
+ ng-checked="!isHiddenResultType(resultType)"
+ ng-click="toggleHiddenResultType(resultType)">
+ {{resultType}} ({{count}})<br>
+ </label>
+ </td>
+ <td>
+ config<br>
+ <label ng-repeat="(config, count) in categories['config']">
+ <input type="checkbox"
+ name="configs"
+ value="{{config}}"
+ ng-checked="!isHiddenConfig(config)"
+ ng-click="toggleHiddenConfig(config)">
+ {{config}} ({{count}})<br>
+ </label>
+ </td>
+ <td><table>
+ <tr><td>
+ Image size
+ <input type="text" ng-model="imageSizePending"
+ ng-init="imageSizePending=100"
+ ng-change="areUpdatesPending = true"
+ maxlength="4"/>
+ </td></tr>
+ <tr><td>
+ Max records to display
+ <input type="text" ng-model="displayLimitPending"
+ ng-init="displayLimitPending=50"
+ ng-change="areUpdatesPending = true"
+ maxlength="4"/>
+ </td></tr>
+ <tr><td>
+ <button style="font-size:30px"
+ ng-click="updateResults()"
+ ng-disabled="!areUpdatesPending">
+ Update Results
+ </button>
+ </td></tr>
+ </tr></table></td>
+ </tr>
+ </table>
<p>
- Click on the column header radio buttons to re-sort by that column...<br>
- <!-- TODO(epoger): Show some sort of "loading" message, instead of
- an empty table, while the data is loading. Otherwise, if there are
- a lot of failures and it takes a long time to load them, the user
- might think there are NO failures and leave the page! -->
+ TODO(epoger): Add ability to filter builder and test names
+ (using a free-form text field, with partial string match)
+ <br>
+ TODO(epoger): Add more columns, such as pixel diffs, notes/bugs,
+ ignoreFailure boolean
+ <br>
+ TODO(epoger): Improve the column sorting, as per
+ <a href="http://jsfiddle.net/vojtajina/js64b/14/">
+ http://jsfiddle.net/vojtajina/js64b/14/
+ </a>
+ <br>
+ TODO(epoger): 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}}
+ results. Can we fix the column widths to be wide enough to hold
+ any result, even the currently hidden results?
+ <p>
+ Found {{filteredTestData.length}} matches, and displaying the first
+ {{displayLimit}}: <br>
+ <!-- TODO(epoger): If (displayLimit <= filteredTestData.length),
+ modify this message to indicate that all results are shown. -->
+ (click on the column header radio buttons to re-sort by that column)
+ <br>
<table border="1">
<tr>
- <th><input ng-model="sortColumn" name="sortColumnRadio" type="radio" value="builder">Builder</input></th>
- <th><input ng-model="sortColumn" name="sortColumnRadio" type="radio" value="test">Test</input></th>
- <th><input ng-model="sortColumn" name="sortColumnRadio" type="radio" value="config">Config</input></th>
- <th><input ng-model="sortColumn" name="sortColumnRadio" type="radio" value="expectedHashDigest">Expected Image</input></th>
- <th><input ng-model="sortColumn" name="sortColumnRadio" type="radio" value="actualHashDigest">Actual Image</input></th>
- <!-- TODO(epoger): Add more columns, such as...
- pixel diff
- notes/bugs
- ignoreFailure boolean
- -->
+ <th ng-repeat="categoryName in ['resultType', 'builder', 'test', 'config']">
+ <input type="radio"
+ name="sortColumnRadio"
+ value="{{categoryName}}"
+ ng-checked="(sortColumn == categoryName)"
+ ng-click="sortResultsBy(categoryName)">
+ {{categoryName}}
+ </th>
+ <th>
+ <input type="radio"
+ name="sortColumnRadio"
+ value="expectedHashDigest"
+ ng-checked="(sortColumn == 'expectedHashDigest')"
+ ng-click="sortResultsBy('expectedHashDigest')">
+ expected image
+ </th>
+ <th>
+ <input type="radio"
+ name="sortColumnRadio"
+ value="actualHashDigest"
+ ng-checked="(sortColumn == 'actualHashDigest')"
+ ng-click="sortResultsBy('actualHashDigest')">
+ actual image
+ </th>
</tr>
- <!-- TODO(epoger): improve the column sorting, as per
- http://jsfiddle.net/vojtajina/js64b/14/ -->
- <tr ng-repeat="result in testData | filter: { resultType: showResultsOfType } | orderBy: sortColumn">
+ <tr ng-repeat="result in limitedTestData">
+ <td>{{result.resultType}}</td>
<td>{{result.builder}}</td>
<td>{{result.test}}</td>
<td>{{result.config}}</td>
@@ -95,6 +150,7 @@
</tr>
</table>
</div>
+ </div>
<!-- TODO(epoger): Can we get the base URLs (commondatastorage and
issues list) from