| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Loader: | 2 * Loader: |
| 3 * Reads GM result reports written out by results.py, and imports | 3 * Reads GM result reports written out by results.py, and imports |
| 4 * them into $scope.categories and $scope.testData . | 4 * them into $scope.extraColumnHeaders and $scope.imagePairs . |
| 5 */ | 5 */ |
| 6 var Loader = angular.module( | 6 var Loader = angular.module( |
| 7 'Loader', | 7 'Loader', |
| 8 ['diff_viewer'] | 8 ['ConstantsModule', 'diff_viewer'] |
| 9 ); | 9 ); |
| 10 | 10 |
| 11 | |
| 12 // TODO(epoger): Combine ALL of our filtering operations (including | 11 // TODO(epoger): Combine ALL of our filtering operations (including |
| 13 // truncation) into this one filter, so that runs most efficiently? | 12 // truncation) into this one filter, so that runs most efficiently? |
| 14 // (We would have to make sure truncation still took place after | 13 // (We would have to make sure truncation still took place after |
| 15 // sorting, though.) | 14 // sorting, though.) |
| 16 Loader.filter( | 15 Loader.filter( |
| 17 'removeHiddenItems', | 16 'removeHiddenImagePairs', |
| 18 function() { | 17 function(constants) { |
| 19 return function(unfilteredItems, hiddenResultTypes, hiddenConfigs, | 18 return function(unfilteredImagePairs, hiddenResultTypes, hiddenConfigs, |
| 20 builderSubstring, testSubstring, viewingTab) { | 19 builderSubstring, testSubstring, viewingTab) { |
| 21 var filteredItems = []; | 20 var filteredImagePairs = []; |
| 22 for (var i = 0; i < unfilteredItems.length; i++) { | 21 for (var i = 0; i < unfilteredImagePairs.length; i++) { |
| 23 var item = unfilteredItems[i]; | 22 var imagePair = unfilteredImagePairs[i]; |
| 23 var extraColumnValues = imagePair[constants.KEY__EXTRA_COLUMN_VALUES]; |
| 24 // For performance, we examine the "set" objects directly rather | 24 // For performance, we examine the "set" objects directly rather |
| 25 // than calling $scope.isValueInSet(). | 25 // than calling $scope.isValueInSet(). |
| 26 // Besides, I don't think we have access to $scope in here... | 26 // Besides, I don't think we have access to $scope in here... |
| 27 if (!(true == hiddenResultTypes[item.resultType]) && | 27 if (!(true == hiddenResultTypes[extraColumnValues[ |
| 28 !(true == hiddenConfigs[item.config]) && | 28 constants.KEY__EXTRACOLUMN__RESULT_TYPE]]) && |
| 29 !(-1 == item.builder.indexOf(builderSubstring)) && | 29 !(true == hiddenConfigs[extraColumnValues[ |
| 30 !(-1 == item.test.indexOf(testSubstring)) && | 30 constants.KEY__EXTRACOLUMN__CONFIG]]) && |
| 31 (viewingTab == item.tab)) { | 31 !(-1 == extraColumnValues[constants.KEY__EXTRACOLUMN__BUILDER] |
| 32 filteredItems.push(item); | 32 .indexOf(builderSubstring)) && |
| 33 !(-1 == extraColumnValues[constants.KEY__EXTRACOLUMN__TEST] |
| 34 .indexOf(testSubstring)) && |
| 35 (viewingTab == imagePair.tab)) { |
| 36 filteredImagePairs.push(imagePair); |
| 33 } | 37 } |
| 34 } | 38 } |
| 35 return filteredItems; | 39 return filteredImagePairs; |
| 36 }; | 40 }; |
| 37 } | 41 } |
| 38 ); | 42 ); |
| 39 | 43 |
| 40 | 44 |
| 41 Loader.controller( | 45 Loader.controller( |
| 42 'Loader.Controller', | 46 'Loader.Controller', |
| 43 function($scope, $http, $filter, $location, $timeout) { | 47 function($scope, $http, $filter, $location, $timeout, constants) { |
| 48 $scope.constants = constants; |
| 44 $scope.windowTitle = "Loading GM Results..."; | 49 $scope.windowTitle = "Loading GM Results..."; |
| 45 $scope.resultsToLoad = $location.search().resultsToLoad; | 50 $scope.resultsToLoad = $location.search().resultsToLoad; |
| 46 $scope.loadingMessage = "Loading results of type '" + $scope.resultsToLoad + | 51 $scope.loadingMessage = "Loading results of type '" + $scope.resultsToLoad + |
| 47 "', please wait..."; | 52 "', please wait..."; |
| 48 | 53 |
| 49 /** | 54 /** |
| 50 * On initial page load, load a full dictionary of results. | 55 * On initial page load, load a full dictionary of results. |
| 51 * Once the dictionary is loaded, unhide the page elements so they can | 56 * Once the dictionary is loaded, unhide the page elements so they can |
| 52 * render the data. | 57 * render the data. |
| 53 */ | 58 */ |
| 54 $http.get("/results/" + $scope.resultsToLoad).success( | 59 $http.get("/results/" + $scope.resultsToLoad).success( |
| 55 function(data, status, header, config) { | 60 function(data, status, header, config) { |
| 56 if (data.header.resultsStillLoading) { | 61 var dataHeader = data[constants.KEY__HEADER]; |
| 62 if (dataHeader[constants.KEY__HEADER__IS_STILL_LOADING]) { |
| 57 $scope.loadingMessage = | 63 $scope.loadingMessage = |
| 58 "Server is still loading results; will retry at " + | 64 "Server is still loading results; will retry at " + |
| 59 $scope.localTimeString(data.header.timeNextUpdateAvailable); | 65 $scope.localTimeString(dataHeader[ |
| 66 constants.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE]); |
| 60 $timeout( | 67 $timeout( |
| 61 function(){location.reload();}, | 68 function(){location.reload();}, |
| 62 (data.header.timeNextUpdateAvailable * 1000) - new Date().getTime(
)); | 69 (dataHeader[constants.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE] |
| 70 * 1000) - new Date().getTime()); |
| 63 } else { | 71 } else { |
| 64 $scope.loadingMessage = "Processing data, please wait..."; | 72 $scope.loadingMessage = "Processing data, please wait..."; |
| 65 | 73 |
| 66 $scope.header = data.header; | 74 $scope.header = dataHeader; |
| 67 $scope.categories = data.categories; | 75 $scope.extraColumnHeaders = data[constants.KEY__EXTRACOLUMNHEADERS]; |
| 68 $scope.testData = data.testData; | 76 $scope.imagePairs = data[constants.KEY__IMAGEPAIRS]; |
| 69 $scope.sortColumn = 'weightedDiffMeasure'; | 77 $scope.imageSets = data[constants.KEY__IMAGESETS]; |
| 78 $scope.sortColumnSubdict = constants.KEY__DIFFERENCE_DATA; |
| 79 $scope.sortColumnKey = constants.KEY__DIFFERENCE_DATA__WEIGHTED_DIFF; |
| 70 $scope.showTodos = false; | 80 $scope.showTodos = false; |
| 71 | 81 |
| 72 $scope.showSubmitAdvancedSettings = false; | 82 $scope.showSubmitAdvancedSettings = false; |
| 73 $scope.submitAdvancedSettings = {}; | 83 $scope.submitAdvancedSettings = {}; |
| 74 $scope.submitAdvancedSettings['reviewed-by-human'] = true; | 84 $scope.submitAdvancedSettings[ |
| 75 $scope.submitAdvancedSettings['ignore-failure'] = false; | 85 constants.KEY__EXPECTATIONS__REVIEWED] = true; |
| 86 $scope.submitAdvancedSettings[ |
| 87 constants.KEY__EXPECTATIONS__IGNOREFAILURE] = false; |
| 76 $scope.submitAdvancedSettings['bug'] = ''; | 88 $scope.submitAdvancedSettings['bug'] = ''; |
| 77 | 89 |
| 78 // Create the list of tabs (lists into which the user can file each | 90 // Create the list of tabs (lists into which the user can file each |
| 79 // test). This may vary, depending on isEditable. | 91 // test). This may vary, depending on isEditable. |
| 80 $scope.tabs = [ | 92 $scope.tabs = [ |
| 81 'Unfiled', 'Hidden' | 93 'Unfiled', 'Hidden' |
| 82 ]; | 94 ]; |
| 83 if (data.header.isEditable) { | 95 if (dataHeader[constants.KEY__HEADER__IS_EDITABLE]) { |
| 84 $scope.tabs = $scope.tabs.concat( | 96 $scope.tabs = $scope.tabs.concat( |
| 85 ['Pending Approval']); | 97 ['Pending Approval']); |
| 86 } | 98 } |
| 87 $scope.defaultTab = $scope.tabs[0]; | 99 $scope.defaultTab = $scope.tabs[0]; |
| 88 $scope.viewingTab = $scope.defaultTab; | 100 $scope.viewingTab = $scope.defaultTab; |
| 89 | 101 |
| 90 // Track the number of results on each tab. | 102 // Track the number of results on each tab. |
| 91 $scope.numResultsPerTab = {}; | 103 $scope.numResultsPerTab = {}; |
| 92 for (var i = 0; i < $scope.tabs.length; i++) { | 104 for (var i = 0; i < $scope.tabs.length; i++) { |
| 93 $scope.numResultsPerTab[$scope.tabs[i]] = 0; | 105 $scope.numResultsPerTab[$scope.tabs[i]] = 0; |
| 94 } | 106 } |
| 95 $scope.numResultsPerTab[$scope.defaultTab] = $scope.testData.length; | 107 $scope.numResultsPerTab[$scope.defaultTab] = $scope.imagePairs.length; |
| 96 | 108 |
| 97 // Add index and tab fields to all records. | 109 // Add index and tab fields to all records. |
| 98 for (var i = 0; i < $scope.testData.length; i++) { | 110 for (var i = 0; i < $scope.imagePairs.length; i++) { |
| 99 $scope.testData[i].index = i; | 111 $scope.imagePairs[i].index = i; |
| 100 $scope.testData[i].tab = $scope.defaultTab; | 112 $scope.imagePairs[i].tab = $scope.defaultTab; |
| 101 } | 113 } |
| 102 | 114 |
| 103 // Arrays within which the user can toggle individual elements. | 115 // Arrays within which the user can toggle individual elements. |
| 104 $scope.selectedItems = []; | 116 $scope.selectedImagePairs = []; |
| 105 | 117 |
| 106 // Sets within which the user can toggle individual elements. | 118 // Sets within which the user can toggle individual elements. |
| 107 $scope.hiddenResultTypes = { | 119 $scope.hiddenResultTypes = {}; |
| 108 'failure-ignored': true, | 120 $scope.hiddenResultTypes[ |
| 109 'no-comparison': true, | 121 constants.KEY__RESULT_TYPE__FAILUREIGNORED] = true; |
| 110 'succeeded': true, | 122 $scope.hiddenResultTypes[ |
| 111 }; | 123 constants.KEY__RESULT_TYPE__NOCOMPARISON] = true; |
| 112 $scope.allResultTypes = Object.keys(data.categories['resultType']); | 124 $scope.hiddenResultTypes[ |
| 125 constants.KEY__RESULT_TYPE__SUCCEEDED] = true; |
| 126 $scope.allResultTypes = Object.keys( |
| 127 $scope.extraColumnHeaders[constants.KEY__EXTRACOLUMN__RESULT_TYPE] |
| 128 [constants.KEY__VALUES_AND_COUNTS]); |
| 113 $scope.hiddenConfigs = {}; | 129 $scope.hiddenConfigs = {}; |
| 114 $scope.allConfigs = Object.keys(data.categories['config']); | 130 $scope.allConfigs = Object.keys( |
| 131 $scope.extraColumnHeaders[constants.KEY__EXTRACOLUMN__CONFIG] |
| 132 [constants.KEY__VALUES_AND_COUNTS]); |
| 115 | 133 |
| 116 // Associative array of partial string matches per category. | 134 // Associative array of partial string matches per category. |
| 117 $scope.categoryValueMatch = {}; | 135 $scope.categoryValueMatch = {}; |
| 118 $scope.categoryValueMatch.builder = ""; | 136 $scope.categoryValueMatch.builder = ""; |
| 119 $scope.categoryValueMatch.test = ""; | 137 $scope.categoryValueMatch.test = ""; |
| 120 | 138 |
| 121 // If any defaults were overridden in the URL, get them now. | 139 // If any defaults were overridden in the URL, get them now. |
| 122 $scope.queryParameters.load(); | 140 $scope.queryParameters.load(); |
| 123 | 141 |
| 124 $scope.updateResults(); | 142 $scope.updateResults(); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 135 ); | 153 ); |
| 136 | 154 |
| 137 | 155 |
| 138 // | 156 // |
| 139 // Select/Clear/Toggle all tests. | 157 // Select/Clear/Toggle all tests. |
| 140 // | 158 // |
| 141 | 159 |
| 142 /** | 160 /** |
| 143 * Select all currently showing tests. | 161 * Select all currently showing tests. |
| 144 */ | 162 */ |
| 145 $scope.selectAllItems = function() { | 163 $scope.selectAllImagePairs = function() { |
| 146 var numItemsShowing = $scope.limitedTestData.length; | 164 var numImagePairsShowing = $scope.limitedImagePairs.length; |
| 147 for (var i = 0; i < numItemsShowing; i++) { | 165 for (var i = 0; i < numImagePairsShowing; i++) { |
| 148 var index = $scope.limitedTestData[i].index; | 166 var index = $scope.limitedImagePairs[i].index; |
| 149 if (!$scope.isValueInArray(index, $scope.selectedItems)) { | 167 if (!$scope.isValueInArray(index, $scope.selectedImagePairs)) { |
| 150 $scope.toggleValueInArray(index, $scope.selectedItems); | 168 $scope.toggleValueInArray(index, $scope.selectedImagePairs); |
| 151 } | 169 } |
| 152 } | 170 } |
| 153 } | 171 } |
| 154 | 172 |
| 155 /** | 173 /** |
| 156 * Deselect all currently showing tests. | 174 * Deselect all currently showing tests. |
| 157 */ | 175 */ |
| 158 $scope.clearAllItems = function() { | 176 $scope.clearAllImagePairs = function() { |
| 159 var numItemsShowing = $scope.limitedTestData.length; | 177 var numImagePairsShowing = $scope.limitedImagePairs.length; |
| 160 for (var i = 0; i < numItemsShowing; i++) { | 178 for (var i = 0; i < numImagePairsShowing; i++) { |
| 161 var index = $scope.limitedTestData[i].index; | 179 var index = $scope.limitedImagePairs[i].index; |
| 162 if ($scope.isValueInArray(index, $scope.selectedItems)) { | 180 if ($scope.isValueInArray(index, $scope.selectedImagePairs)) { |
| 163 $scope.toggleValueInArray(index, $scope.selectedItems); | 181 $scope.toggleValueInArray(index, $scope.selectedImagePairs); |
| 164 } | 182 } |
| 165 } | 183 } |
| 166 } | 184 } |
| 167 | 185 |
| 168 /** | 186 /** |
| 169 * Toggle selection of all currently showing tests. | 187 * Toggle selection of all currently showing tests. |
| 170 */ | 188 */ |
| 171 $scope.toggleAllItems = function() { | 189 $scope.toggleAllImagePairs = function() { |
| 172 var numItemsShowing = $scope.limitedTestData.length; | 190 var numImagePairsShowing = $scope.limitedImagePairs.length; |
| 173 for (var i = 0; i < numItemsShowing; i++) { | 191 for (var i = 0; i < numImagePairsShowing; i++) { |
| 174 var index = $scope.limitedTestData[i].index; | 192 var index = $scope.limitedImagePairs[i].index; |
| 175 $scope.toggleValueInArray(index, $scope.selectedItems); | 193 $scope.toggleValueInArray(index, $scope.selectedImagePairs); |
| 176 } | 194 } |
| 177 } | 195 } |
| 178 | 196 |
| 179 | 197 |
| 180 // | 198 // |
| 181 // Tab operations. | 199 // Tab operations. |
| 182 // | 200 // |
| 183 | 201 |
| 184 /** | 202 /** |
| 185 * Change the selected tab. | 203 * Change the selected tab. |
| 186 * | 204 * |
| 187 * @param tab (string): name of the tab to select | 205 * @param tab (string): name of the tab to select |
| 188 */ | 206 */ |
| 189 $scope.setViewingTab = function(tab) { | 207 $scope.setViewingTab = function(tab) { |
| 190 $scope.viewingTab = tab; | 208 $scope.viewingTab = tab; |
| 191 $scope.updateResults(); | 209 $scope.updateResults(); |
| 192 } | 210 } |
| 193 | 211 |
| 194 /** | 212 /** |
| 195 * Move the items in $scope.selectedItems to a different tab, | 213 * Move the imagePairs in $scope.selectedImagePairs to a different tab, |
| 196 * and then clear $scope.selectedItems. | 214 * and then clear $scope.selectedImagePairs. |
| 197 * | 215 * |
| 198 * @param newTab (string): name of the tab to move the tests to | 216 * @param newTab (string): name of the tab to move the tests to |
| 199 */ | 217 */ |
| 200 $scope.moveSelectedItemsToTab = function(newTab) { | 218 $scope.moveSelectedImagePairsToTab = function(newTab) { |
| 201 $scope.moveItemsToTab($scope.selectedItems, newTab); | 219 $scope.moveImagePairsToTab($scope.selectedImagePairs, newTab); |
| 202 $scope.selectedItems = []; | 220 $scope.selectedImagePairs = []; |
| 203 $scope.updateResults(); | 221 $scope.updateResults(); |
| 204 } | 222 } |
| 205 | 223 |
| 206 /** | 224 /** |
| 207 * Move a subset of $scope.testData to a different tab. | 225 * Move a subset of $scope.imagePairs to a different tab. |
| 208 * | 226 * |
| 209 * @param itemIndices (array of ints): indices into $scope.testData | 227 * @param imagePairIndices (array of ints): indices into $scope.imagePairs |
| 210 * indicating which test results to move | 228 * indicating which test results to move |
| 211 * @param newTab (string): name of the tab to move the tests to | 229 * @param newTab (string): name of the tab to move the tests to |
| 212 */ | 230 */ |
| 213 $scope.moveItemsToTab = function(itemIndices, newTab) { | 231 $scope.moveImagePairsToTab = function(imagePairIndices, newTab) { |
| 214 var itemIndex; | 232 var imagePairIndex; |
| 215 var numItems = itemIndices.length; | 233 var numImagePairs = imagePairIndices.length; |
| 216 for (var i = 0; i < numItems; i++) { | 234 for (var i = 0; i < numImagePairs; i++) { |
| 217 itemIndex = itemIndices[i]; | 235 imagePairIndex = imagePairIndices[i]; |
| 218 $scope.numResultsPerTab[$scope.testData[itemIndex].tab]--; | 236 $scope.numResultsPerTab[$scope.imagePairs[imagePairIndex].tab]--; |
| 219 $scope.testData[itemIndex].tab = newTab; | 237 $scope.imagePairs[imagePairIndex].tab = newTab; |
| 220 } | 238 } |
| 221 $scope.numResultsPerTab[newTab] += numItems; | 239 $scope.numResultsPerTab[newTab] += numImagePairs; |
| 222 } | 240 } |
| 223 | 241 |
| 224 | 242 |
| 225 // | 243 // |
| 226 // $scope.queryParameters: | 244 // $scope.queryParameters: |
| 227 // Transfer parameter values between $scope and the URL query string. | 245 // Transfer parameter values between $scope and the URL query string. |
| 228 // | 246 // |
| 229 $scope.queryParameters = {}; | 247 $scope.queryParameters = {}; |
| 230 | 248 |
| 231 // load and save functions for parameters of each type | 249 // load and save functions for parameters of each type |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 270 } | 288 } |
| 271 }, | 289 }, |
| 272 | 290 |
| 273 }; | 291 }; |
| 274 | 292 |
| 275 // parameter name -> copier objects to load/save parameter value | 293 // parameter name -> copier objects to load/save parameter value |
| 276 $scope.queryParameters.map = { | 294 $scope.queryParameters.map = { |
| 277 'resultsToLoad': $scope.queryParameters.copiers.simple, | 295 'resultsToLoad': $scope.queryParameters.copiers.simple, |
| 278 'displayLimitPending': $scope.queryParameters.copiers.simple, | 296 'displayLimitPending': $scope.queryParameters.copiers.simple, |
| 279 'imageSizePending': $scope.queryParameters.copiers.simple, | 297 'imageSizePending': $scope.queryParameters.copiers.simple, |
| 280 'sortColumn': $scope.queryParameters.copiers.simple, | 298 'sortColumnSubdict': $scope.queryParameters.copiers.simple, |
| 281 | 299 'sortColumnKey': $scope.queryParameters.copiers.simple, |
| 282 'builder': $scope.queryParameters.copiers.categoryValueMatch, | |
| 283 'test': $scope.queryParameters.copiers.categoryValueMatch, | |
| 284 | 300 |
| 285 'hiddenResultTypes': $scope.queryParameters.copiers.set, | 301 'hiddenResultTypes': $scope.queryParameters.copiers.set, |
| 286 'hiddenConfigs': $scope.queryParameters.copiers.set, | 302 'hiddenConfigs': $scope.queryParameters.copiers.set, |
| 287 }; | 303 }; |
| 304 $scope.queryParameters.map[constants.KEY__EXTRACOLUMN__BUILDER] = |
| 305 $scope.queryParameters.copiers.categoryValueMatch; |
| 306 $scope.queryParameters.map[constants.KEY__EXTRACOLUMN__TEST] = |
| 307 $scope.queryParameters.copiers.categoryValueMatch; |
| 288 | 308 |
| 289 // Loads all parameters into $scope from the URL query string; | 309 // Loads all parameters into $scope from the URL query string; |
| 290 // any which are not found within the URL will keep their current value. | 310 // any which are not found within the URL will keep their current value. |
| 291 $scope.queryParameters.load = function() { | 311 $scope.queryParameters.load = function() { |
| 292 var nameValuePairs = $location.search(); | 312 var nameValuePairs = $location.search(); |
| 293 angular.forEach($scope.queryParameters.map, | 313 angular.forEach($scope.queryParameters.map, |
| 294 function(copier, paramName) { | 314 function(copier, paramName) { |
| 295 copier.load(nameValuePairs, paramName); | 315 copier.load(nameValuePairs, paramName); |
| 296 } | 316 } |
| 297 ); | 317 ); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 | 352 |
| 333 /** | 353 /** |
| 334 * Update the displayed results, based on filters/settings, | 354 * Update the displayed results, based on filters/settings, |
| 335 * and call $scope.queryParameters.save() so that the new filter results | 355 * and call $scope.queryParameters.save() so that the new filter results |
| 336 * can be bookmarked. | 356 * can be bookmarked. |
| 337 */ | 357 */ |
| 338 $scope.updateResults = function() { | 358 $scope.updateResults = function() { |
| 339 $scope.displayLimit = $scope.displayLimitPending; | 359 $scope.displayLimit = $scope.displayLimitPending; |
| 340 // TODO(epoger): Every time we apply a filter, AngularJS creates | 360 // TODO(epoger): Every time we apply a filter, AngularJS creates |
| 341 // another copy of the array. Is there a way we can filter out | 361 // another copy of the array. Is there a way we can filter out |
| 342 // the items as they are displayed, rather than storing multiple | 362 // the imagePairs as they are displayed, rather than storing multiple |
| 343 // array copies? (For better performance.) | 363 // array copies? (For better performance.) |
| 344 | 364 |
| 345 if ($scope.viewingTab == $scope.defaultTab) { | 365 if ($scope.viewingTab == $scope.defaultTab) { |
| 346 | 366 |
| 347 // TODO(epoger): Until we allow the user to reverse sort order, | 367 // TODO(epoger): Until we allow the user to reverse sort order, |
| 348 // there are certain columns we want to sort in a different order. | 368 // there are certain columns we want to sort in a different order. |
| 349 var doReverse = ( | 369 var doReverse = ( |
| 350 ($scope.sortColumn == 'percentDifferingPixels') || | 370 ($scope.sortColumnKey == |
| 351 ($scope.sortColumn == 'weightedDiffMeasure')); | 371 constants.KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS) || |
| 372 ($scope.sortColumnKey == |
| 373 constants.KEY__DIFFERENCE_DATA__WEIGHTED_DIFF)); |
| 352 | 374 |
| 353 $scope.filteredTestData = | 375 $scope.filteredImagePairs = |
| 354 $filter("orderBy")( | 376 $filter("orderBy")( |
| 355 $filter("removeHiddenItems")( | 377 $filter("removeHiddenImagePairs")( |
| 356 $scope.testData, | 378 $scope.imagePairs, |
| 357 $scope.hiddenResultTypes, | 379 $scope.hiddenResultTypes, |
| 358 $scope.hiddenConfigs, | 380 $scope.hiddenConfigs, |
| 359 $scope.categoryValueMatch.builder, | 381 $scope.categoryValueMatch.builder, |
| 360 $scope.categoryValueMatch.test, | 382 $scope.categoryValueMatch.test, |
| 361 $scope.viewingTab | 383 $scope.viewingTab |
| 362 ), | 384 ), |
| 363 $scope.sortColumn, doReverse); | 385 $scope.getSortColumnValue, doReverse); |
| 364 $scope.limitedTestData = $filter("limitTo")( | 386 $scope.limitedImagePairs = $filter("limitTo")( |
| 365 $scope.filteredTestData, $scope.displayLimit); | 387 $scope.filteredImagePairs, $scope.displayLimit); |
| 366 } else { | 388 } else { |
| 367 $scope.filteredTestData = | 389 $scope.filteredImagePairs = |
| 368 $filter("orderBy")( | 390 $filter("orderBy")( |
| 369 $filter("filter")( | 391 $filter("filter")( |
| 370 $scope.testData, | 392 $scope.imagePairs, |
| 371 {tab: $scope.viewingTab}, | 393 {tab: $scope.viewingTab}, |
| 372 true | 394 true |
| 373 ), | 395 ), |
| 374 $scope.sortColumn); | 396 $scope.getSortColumnValue); |
| 375 $scope.limitedTestData = $scope.filteredTestData; | 397 $scope.limitedImagePairs = $scope.filteredImagePairs; |
| 376 } | 398 } |
| 377 $scope.imageSize = $scope.imageSizePending; | 399 $scope.imageSize = $scope.imageSizePending; |
| 378 $scope.setUpdatesPending(false); | 400 $scope.setUpdatesPending(false); |
| 379 $scope.queryParameters.save(); | 401 $scope.queryParameters.save(); |
| 380 } | 402 } |
| 381 | 403 |
| 382 /** | 404 /** |
| 383 * Re-sort the displayed results. | 405 * Re-sort the displayed results. |
| 384 * | 406 * |
| 385 * @param sortColumn (string): name of the column to sort on | 407 * @param subdict (string): which subdictionary |
| 408 * (constants.KEY__DIFFERENCE_DATA, constants.KEY__EXPECTATIONS_DATA, |
| 409 * constants.KEY__EXTRA_COLUMN_VALUES) the sort column key is within |
| 410 * @param key (string): sort by value associated with this key in subdict |
| 386 */ | 411 */ |
| 387 $scope.sortResultsBy = function(sortColumn) { | 412 $scope.sortResultsBy = function(subdict, key) { |
| 388 $scope.sortColumn = sortColumn; | 413 $scope.sortColumnSubdict = subdict; |
| 414 $scope.sortColumnKey = key; |
| 389 $scope.updateResults(); | 415 $scope.updateResults(); |
| 390 } | 416 } |
| 391 | 417 |
| 392 /** | 418 /** |
| 419 * For a particular ImagePair, return the value of the column we are |
| 420 * sorting on (according to $scope.sortColumnSubdict and |
| 421 * $scope.sortColumnKey). |
| 422 * |
| 423 * @param imagePair: imagePair to get a column value out of. |
| 424 */ |
| 425 $scope.getSortColumnValue = function(imagePair) { |
| 426 if ($scope.sortColumnSubdict in imagePair) { |
| 427 return imagePair[$scope.sortColumnSubdict][$scope.sortColumnKey]; |
| 428 } else { |
| 429 return undefined; |
| 430 } |
| 431 } |
| 432 |
| 433 /** |
| 393 * Set $scope.categoryValueMatch[name] = value, and update results. | 434 * Set $scope.categoryValueMatch[name] = value, and update results. |
| 394 * | 435 * |
| 395 * @param name | 436 * @param name |
| 396 * @param value | 437 * @param value |
| 397 */ | 438 */ |
| 398 $scope.setCategoryValueMatch = function(name, value) { | 439 $scope.setCategoryValueMatch = function(name, value) { |
| 399 $scope.categoryValueMatch[name] = value; | 440 $scope.categoryValueMatch[name] = value; |
| 400 $scope.updateResults(); | 441 $scope.updateResults(); |
| 401 } | 442 } |
| 402 | 443 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 449 | 490 |
| 450 | 491 |
| 451 // | 492 // |
| 452 // Operations for sending info back to the server. | 493 // Operations for sending info back to the server. |
| 453 // | 494 // |
| 454 | 495 |
| 455 /** | 496 /** |
| 456 * Tell the server that the actual results of these particular tests | 497 * Tell the server that the actual results of these particular tests |
| 457 * are acceptable. | 498 * are acceptable. |
| 458 * | 499 * |
| 459 * @param testDataSubset an array of test results, most likely a subset of | 500 * TODO(epoger): This assumes that the original expectations are in |
| 460 * $scope.testData (perhaps with some modifications) | 501 * imageSetA, and the actuals are in imageSetB. |
| 502 * |
| 503 * @param imagePairsSubset an array of test results, most likely a subset of |
| 504 * $scope.imagePairs (perhaps with some modifications) |
| 461 */ | 505 */ |
| 462 $scope.submitApprovals = function(testDataSubset) { | 506 $scope.submitApprovals = function(imagePairsSubset) { |
| 463 $scope.submitPending = true; | 507 $scope.submitPending = true; |
| 464 | 508 |
| 465 // Convert bug text field to null or 1-item array. | 509 // Convert bug text field to null or 1-item array. |
| 466 var bugs = null; | 510 var bugs = null; |
| 467 var bugNumber = parseInt($scope.submitAdvancedSettings['bug']); | 511 var bugNumber = parseInt($scope.submitAdvancedSettings['bug']); |
| 468 if (!isNaN(bugNumber)) { | 512 if (!isNaN(bugNumber)) { |
| 469 bugs = [bugNumber]; | 513 bugs = [bugNumber]; |
| 470 } | 514 } |
| 471 | 515 |
| 472 // TODO(epoger): This is a suboptimal way to prevent users from | 516 // TODO(epoger): This is a suboptimal way to prevent users from |
| 473 // rebaselining failures in alternative renderModes, but it does work. | 517 // rebaselining failures in alternative renderModes, but it does work. |
| 474 // For a better solution, see | 518 // For a better solution, see |
| 475 // https://code.google.com/p/skia/issues/detail?id=1748 ('gm: add new | 519 // https://code.google.com/p/skia/issues/detail?id=1748 ('gm: add new |
| 476 // result type, RenderModeMismatch') | 520 // result type, RenderModeMismatch') |
| 477 var encounteredComparisonConfig = false; | 521 var encounteredComparisonConfig = false; |
| 478 | 522 |
| 479 var newResults = []; | 523 var updatedExpectations = []; |
| 480 for (var i = 0; i < testDataSubset.length; i++) { | 524 for (var i = 0; i < imagePairsSubset.length; i++) { |
| 481 var actualResult = testDataSubset[i]; | 525 var imagePair = imagePairsSubset[i]; |
| 482 var expectedResult = { | 526 var updatedExpectation = {}; |
| 483 builder: actualResult['builder'], | 527 updatedExpectation[constants.KEY__EXPECTATIONS_DATA] = |
| 484 test: actualResult['test'], | 528 imagePair[constants.KEY__EXPECTATIONS_DATA]; |
| 485 config: actualResult['config'], | 529 updatedExpectation[constants.KEY__EXTRA_COLUMN_VALUES] = |
| 486 expectedHashType: actualResult['actualHashType'], | 530 imagePair[constants.KEY__EXTRA_COLUMN_VALUES]; |
| 487 expectedHashDigest: actualResult['actualHashDigest'], | 531 updatedExpectation[constants.KEY__NEW_IMAGE_URL] = |
| 488 }; | 532 imagePair[constants.KEY__IMAGE_B_URL]; |
| 489 if (0 == expectedResult.config.indexOf('comparison-')) { | 533 if (0 == updatedExpectation[constants.KEY__EXTRA_COLUMN_VALUES] |
| 534 [constants.KEY__EXTRACOLUMN__CONFIG] |
| 535 .indexOf('comparison-')) { |
| 490 encounteredComparisonConfig = true; | 536 encounteredComparisonConfig = true; |
| 491 } | 537 } |
| 492 | 538 |
| 493 // Advanced settings... | 539 // Advanced settings... |
| 494 expectedResult['reviewed-by-human'] = | 540 if (null == updatedExpectation[constants.KEY__EXPECTATIONS_DATA]) { |
| 495 $scope.submitAdvancedSettings['reviewed-by-human']; | 541 updatedExpectation[constants.KEY__EXPECTATIONS_DATA] = {}; |
| 496 if (true == $scope.submitAdvancedSettings['ignore-failure']) { | 542 } |
| 543 updatedExpectation[constants.KEY__EXPECTATIONS_DATA] |
| 544 [constants.KEY__EXPECTATIONS__REVIEWED] = |
| 545 $scope.submitAdvancedSettings[ |
| 546 constants.KEY__EXPECTATIONS__REVIEWED]; |
| 547 if (true == $scope.submitAdvancedSettings[ |
| 548 constants.KEY__EXPECTATIONS__IGNOREFAILURE]) { |
| 497 // if it's false, don't send it at all (just keep the default) | 549 // if it's false, don't send it at all (just keep the default) |
| 498 expectedResult['ignore-failure'] = true; | 550 updatedExpectation[constants.KEY__EXPECTATIONS_DATA] |
| 551 [constants.KEY__EXPECTATIONS__IGNOREFAILURE] = true; |
| 499 } | 552 } |
| 500 expectedResult['bugs'] = bugs; | 553 updatedExpectation[constants.KEY__EXPECTATIONS_DATA] |
| 554 [constants.KEY__EXPECTATIONS__BUGS] = bugs; |
| 501 | 555 |
| 502 newResults.push(expectedResult); | 556 updatedExpectations.push(updatedExpectation); |
| 503 } | 557 } |
| 504 if (encounteredComparisonConfig) { | 558 if (encounteredComparisonConfig) { |
| 505 alert("Approval failed -- you cannot approve results with config " + | 559 alert("Approval failed -- you cannot approve results with config " + |
| 506 "type comparison-*"); | 560 "type comparison-*"); |
| 507 $scope.submitPending = false; | 561 $scope.submitPending = false; |
| 508 return; | 562 return; |
| 509 } | 563 } |
| 564 var modificationData = {}; |
| 565 modificationData[constants.KEY__EDITS__MODIFICATIONS] = |
| 566 updatedExpectations; |
| 567 modificationData[constants.KEY__EDITS__OLD_RESULTS_HASH] = |
| 568 $scope.header[constants.KEY__HEADER__DATAHASH]; |
| 569 modificationData[constants.KEY__EDITS__OLD_RESULTS_TYPE] = |
| 570 $scope.header[constants.KEY__HEADER__TYPE]; |
| 510 $http({ | 571 $http({ |
| 511 method: "POST", | 572 method: "POST", |
| 512 url: "/edits", | 573 url: "/edits", |
| 513 data: { | 574 data: modificationData |
| 514 oldResultsType: $scope.header.type, | 575 }).success(function(data, status, headers, config) { |
| 515 oldResultsHash: $scope.header.dataHash, | 576 var imagePairIndicesToMove = []; |
| 516 modifications: newResults | 577 for (var i = 0; i < imagePairsSubset.length; i++) { |
| 578 imagePairIndicesToMove.push(imagePairsSubset[i].index); |
| 517 } | 579 } |
| 518 }).success(function(data, status, headers, config) { | 580 $scope.moveImagePairsToTab(imagePairIndicesToMove, |
| 519 var itemIndicesToMove = []; | 581 "HackToMakeSureThisImagePairDisappears"); |
| 520 for (var i = 0; i < testDataSubset.length; i++) { | |
| 521 itemIndicesToMove.push(testDataSubset[i].index); | |
| 522 } | |
| 523 $scope.moveItemsToTab(itemIndicesToMove, | |
| 524 "HackToMakeSureThisItemDisappears"); | |
| 525 $scope.updateResults(); | 582 $scope.updateResults(); |
| 526 alert("New baselines submitted successfully!\n\n" + | 583 alert("New baselines submitted successfully!\n\n" + |
| 527 "You still need to commit the updated expectations files on " + | 584 "You still need to commit the updated expectations files on " + |
| 528 "the server side to the Skia repo.\n\n" + | 585 "the server side to the Skia repo.\n\n" + |
| 529 "When you click OK, your web UI will reload; after that " + | 586 "When you click OK, your web UI will reload; after that " + |
| 530 "completes, you will see the updated data (once the server has " + | 587 "completes, you will see the updated data (once the server has " + |
| 531 "finished loading the update results into memory!) and you can " + | 588 "finished loading the update results into memory!) and you can " + |
| 532 "submit more baselines if you want."); | 589 "submit more baselines if you want."); |
| 533 // I don't know why, but if I just call reload() here it doesn't work. | 590 // I don't know why, but if I just call reload() here it doesn't work. |
| 534 // Making a timer call it fixes the problem. | 591 // Making a timer call it fixes the problem. |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 675 * @param brightnessString (string): 0-255, 0 is completely black | 732 * @param brightnessString (string): 0-255, 0 is completely black |
| 676 * | 733 * |
| 677 * TODO(epoger): It might be nice to tint the color when it's not completely | 734 * TODO(epoger): It might be nice to tint the color when it's not completely |
| 678 * black or completely white. | 735 * black or completely white. |
| 679 */ | 736 */ |
| 680 $scope.brightnessStringToHexColor = function(brightnessString) { | 737 $scope.brightnessStringToHexColor = function(brightnessString) { |
| 681 var v = parseInt(brightnessString); | 738 var v = parseInt(brightnessString); |
| 682 return $scope.hexColorString(v, v, v); | 739 return $scope.hexColorString(v, v, v); |
| 683 } | 740 } |
| 684 | 741 |
| 742 /** |
| 743 * Returns the last path component of image diff URL for a given ImagePair. |
| 744 * |
| 745 * Depending on which diff this is (whitediffs, pixeldiffs, etc.) this |
| 746 * will be relative to different base URLs. |
| 747 * |
| 748 * We must keep this function in sync with _get_difference_locator() in |
| 749 * ../imagediffdb.py |
| 750 * |
| 751 * @param imagePair: ImagePair to generate image diff URL for |
| 752 */ |
| 753 $scope.getImageDiffRelativeUrl = function(imagePair) { |
| 754 var before = |
| 755 imagePair[constants.KEY__IMAGE_A_URL] + "-vs-" + |
| 756 imagePair[constants.KEY__IMAGE_B_URL]; |
| 757 return before.replace(/[^\w\-]/g, "_") + ".png"; |
| 758 } |
| 759 |
| 685 } | 760 } |
| 686 ); | 761 ); |
| OLD | NEW |