From eb832599b631a09b180ea3615347adba6bd5e363 Mon Sep 17 00:00:00 2001 From: "epoger@google.com" Date: Wed, 23 Oct 2013 15:07:26 +0000 Subject: rebaseline_server: add tabs, and ability to submit new baselines to the server Tabs allow the user to divide the tests into groups: hide these for now, approve these, etc. (SkipBuildbotRuns) R=borenet@google.com Review URL: https://codereview.chromium.org/28903008 git-svn-id: http://skia.googlecode.com/svn/trunk@11915 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gm/rebaseline_server/static/loader.js | 160 ++++++++++++++++++++++++++++++---- gm/rebaseline_server/static/view.css | 6 ++ gm/rebaseline_server/static/view.html | 148 +++++++++++++++++++++---------- 3 files changed, 251 insertions(+), 63 deletions(-) create mode 100644 gm/rebaseline_server/static/view.css (limited to 'gm/rebaseline_server/static') diff --git a/gm/rebaseline_server/static/loader.js b/gm/rebaseline_server/static/loader.js index 80f84c6a3a..09b66d5546 100644 --- a/gm/rebaseline_server/static/loader.js +++ b/gm/rebaseline_server/static/loader.js @@ -15,12 +15,14 @@ var Loader = angular.module( Loader.filter( 'removeHiddenItems', function() { - return function(unfilteredItems, hiddenResultTypes, hiddenConfigs) { + return function(unfilteredItems, hiddenResultTypes, hiddenConfigs, + viewingTab) { var filteredItems = []; for (var i = 0; i < unfilteredItems.length; i++) { var item = unfilteredItems[i]; if (!(true == hiddenResultTypes[item.resultType]) && - !(true == hiddenConfigs[item.config])) { + !(true == hiddenConfigs[item.config]) && + (viewingTab == item.tab)) { filteredItems.push(item); } } @@ -45,10 +47,31 @@ Loader.controller( $scope.categories = data.categories; $scope.testData = data.testData; $scope.sortColumn = 'test'; - $scope.showTodos = true; + $scope.showTodos = false; + // Create the list of tabs (lists into which the user can file each + // test). This may vary, depending on isEditable. + $scope.tabs = [ + 'Unfiled', 'Hidden' + ]; + if (data.header.isEditable) { + $scope.tabs = $scope.tabs.concat( + ['Pending Approval']); + } + $scope.defaultTab = $scope.tabs[0]; + $scope.viewingTab = $scope.defaultTab; + + // Track the number of results on each tab. + $scope.numResultsPerTab = {}; + for (var i = 0; i < $scope.tabs.length; i++) { + $scope.numResultsPerTab[$scope.tabs[i]] = 0; + } + $scope.numResultsPerTab[$scope.defaultTab] = $scope.testData.length; + + // Add index and tab fields to all records. for (var i = 0; i < $scope.testData.length; i++) { $scope.testData[i].index = i; + $scope.testData[i].tab = $scope.defaultTab; } $scope.hiddenResultTypes = { @@ -57,7 +80,7 @@ Loader.controller( 'succeeded': true, }; $scope.hiddenConfigs = {}; - $scope.selectedItems = {}; + $scope.selectedItems = []; $scope.updateResults(); $scope.loadingMessage = ""; @@ -72,13 +95,14 @@ Loader.controller( ); $scope.isItemSelected = function(index) { - return (true == $scope.selectedItems[index]); + return (-1 != $scope.selectedItems.indexOf(index)); } $scope.toggleItemSelected = function(index) { - if (true == $scope.selectedItems[index]) { - delete $scope.selectedItems[index]; + var i = $scope.selectedItems.indexOf(index); + if (-1 == i) { + $scope.selectedItems.push(index); } else { - $scope.selectedItems[index] = true; + $scope.selectedItems.splice(i, 1); } // unlike other toggle methods below, does not set // $scope.areUpdatesPending = true; @@ -113,27 +137,77 @@ Loader.controller( $scope.areUpdatesPending = true; } + $scope.setViewingTab = function(tab) { + $scope.viewingTab = tab; + $scope.updateResults(); + } + $scope.localTimeString = function(secondsPastEpoch) { var d = new Date(secondsPastEpoch * 1000); return d.toString(); } + /** + * Move the items in $scope.selectedItems to a different tab, + * and then clear $scope.selectedItems. + * + * @param newTab (string): name of the tab to move the tests to + */ + $scope.moveSelectedItemsToTab = function(newTab) { + $scope.moveItemsToTab($scope.selectedItems, newTab); + $scope.selectedItems = []; + $scope.updateResults(); + } + + /** + * Move a subset of $scope.testData to a different tab. + * + * @param itemIndices (array of ints): indices into $scope.testData + * indicating which test results to move + * @param newTab (string): name of the tab to move the tests to + */ + $scope.moveItemsToTab = function(itemIndices, newTab) { + var itemIndex; + var numItems = itemIndices.length; + for (var i = 0; i < numItems; i++) { + itemIndex = itemIndices[i]; + $scope.numResultsPerTab[$scope.testData[itemIndex].tab]--; + $scope.testData[itemIndex].tab = newTab; + } + $scope.numResultsPerTab[newTab] += numItems; + } + $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); + + if ($scope.viewingTab == $scope.defaultTab) { + $scope.filteredTestData = + $filter("orderBy")( + $filter("removeHiddenItems")( + $scope.testData, + $scope.hiddenResultTypes, + $scope.hiddenConfigs, + $scope.viewingTab + ), + $scope.sortColumn); + $scope.limitedTestData = $filter("limitTo")( + $scope.filteredTestData, $scope.displayLimit); + } else { + $scope.filteredTestData = + $filter("orderBy")( + $filter("filter")( + $scope.testData, + {tab: $scope.viewingTab}, + true + ), + $scope.sortColumn); + $scope.limitedTestData = $filter("limitTo")( + $scope.filteredTestData, $scope.displayLimit); + } $scope.imageSize = $scope.imageSizePending; $scope.areUpdatesPending = false; } @@ -142,5 +216,55 @@ Loader.controller( $scope.sortColumn = sortColumn; $scope.updateResults(); } + + /** + * Tell the server that the actual results of these particular tests + * are acceptable. + * + * @param testDataSubset an array of test results, most likely a subset of + * $scope.testData (perhaps with some modifications) + */ + $scope.submitApprovals = function(testDataSubset) { + $scope.submitPending = true; + var newResults = []; + for (var i = 0; i < testDataSubset.length; i++) { + var actualResult = testDataSubset[i]; + var expectedResult = { + builder: actualResult['builder'], + test: actualResult['test'], + config: actualResult['config'], + expectedHashType: actualResult['actualHashType'], + expectedHashDigest: actualResult['actualHashDigest'], + }; + newResults.push(expectedResult); + } + $http({ + method: "POST", + url: "/edits", + data: { + oldResultsType: $scope.header.type, + oldResultsHash: $scope.header.dataHash, + modifications: newResults + } + }).success(function(data, status, headers, config) { + var itemIndicesToMove = []; + for (var i = 0; i < testDataSubset.length; i++) { + itemIndicesToMove.push(testDataSubset[i].index); + } + $scope.moveItemsToTab(itemIndicesToMove, + "HackToMakeSureThisItemDisappears"); + $scope.updateResults(); + alert("New baselines submitted successfully!\n\n" + + "You still need to commit the updated expectations files on " + + "the server side to the Skia repo.\n\n" + + "Also: in order to see the complete updated data, or to submit " + + "more baselines, you will need to reload your client."); + $scope.submitPending = false; + }).error(function(data, status, headers, config) { + alert("There was an error submitting your baselines.\n\n" + + "Please see server-side log for details."); + $scope.submitPending = false; + }); + } } ); diff --git a/gm/rebaseline_server/static/view.css b/gm/rebaseline_server/static/view.css new file mode 100644 index 0000000000..e4eeb0f5a3 --- /dev/null +++ b/gm/rebaseline_server/static/view.css @@ -0,0 +1,6 @@ +.tab-true { + background-color: #ccccff; +} +.tab-false { + background-color: #8888ff; +} diff --git a/gm/rebaseline_server/static/view.html b/gm/rebaseline_server/static/view.html index 9f0158d579..9bb01fc0c5 100644 --- a/gm/rebaseline_server/static/view.html +++ b/gm/rebaseline_server/static/view.html @@ -6,6 +6,7 @@ + @@ -13,16 +14,69 @@ {{loadingMessage}} -
+
+
WARNING! These results are editable and exported, so any user who can connect to this server over the network can modify them.
+ +
+

+ TODO(epoger): + + show +

    +
  • + If server was run with --reload flag, automatically check for + new results and tell the user when new results are available + (the user can reload the page if he wants to see them). +
  • + Add ability to filter builder and test names + (using a free-form text field, with partial string match) +
  • + Add more columns, such as pixel diffs, notes/bugs, + ignoreFailure boolean +
  • + Improve the column sorting, as per + + http://jsfiddle.net/vojtajina/js64b/14/ + +
  • + Right now, if you change which column is used to + sort the data, the column widths may fluctuate based on the + longest string currently visible within the top {{displayLimit}} + results. Can we fix the column widths to be wide enough to hold + any result, even the currently hidden results? +
  • +
+
+
Results current as of {{localTimeString(header.timeUpdated)}}
- + +
+
+
+  {{tab}} ({{numResultsPerTab[tab]}})  +
+
+   +
+
+
+ +
+ +
+
Filters @@ -80,49 +134,52 @@
-

- TODO(epoger): - - show -

    -
  • - Implement editing of results (we have added the --editable - flag to the server, but it's not fully implemented yet). -
    - Currently selected items are: {{selectedItems}} +

    + +

    +
    + +
    +
    +
    + Submitting, please wait...
    -
  • - If server was run with --reload flag, automatically check for - new results and tell the user when new results are available - (the user can reload the page if he wants to see them). -
  • - Add ability to filter builder and test names - (using a free-form text field, with partial string match) -
  • - Add more columns, such as pixel diffs, notes/bugs, - ignoreFailure boolean -
  • - Improve the column sorting, as per - - http://jsfiddle.net/vojtajina/js64b/14/ - -
  • - Right now, if you change which column is used to - sort the data, the column widths may fluctuate based on the - longest string currently visible within the top {{displayLimit}} - results. Can we fix the column widths to be wide enough to hold - any result, even the currently hidden results? -
  • -
+
+
+

- Found {{filteredTestData.length}} matches, and displaying the first - {{displayLimit}}:
- - (click on the column header radio buttons to re-sort by that column) + +

+
+ Found {{filteredTestData.length}} matches; + + displaying the first {{limitedTestData.length}} + + + displaying them all + +
+ (click on the column header radio buttons to re-sort by that column) +
+
+
+ +
+
+
+
+

+ - @@ -168,7 +225,7 @@ -
@@ -149,7 +206,7 @@ ng-click="sortResultsBy('actualHashDigest')"> actual image +
+
- + +