diff options
author | 2013-10-02 18:57:48 +0000 | |
---|---|---|
committer | 2013-10-02 18:57:48 +0000 | |
commit | 5f2bb002bd343e0c6e54455e9203883d7769ff67 (patch) | |
tree | 11fc7f5d01aa8b36c69954d888238fd96be3d0f8 /gm | |
parent | 55f5682523f66c7a0d9696ff9f7c50cc8a6d6b45 (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-x | gm/rebaseline_server/results.py | 36 | ||||
-rw-r--r-- | gm/rebaseline_server/static/loader.js | 87 | ||||
-rw-r--r-- | gm/rebaseline_server/static/view.html | 166 |
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 |