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.categories and $scope.testData . |
5 */ | 5 */ |
6 var Loader = angular.module( | 6 var Loader = angular.module( |
7 'Loader', | 7 'Loader', |
8 [] | 8 [] |
9 ); | 9 ); |
10 | 10 |
11 // TODO(epoger): Combine ALL of our filtering operations (including | 11 // TODO(epoger): Combine ALL of our filtering operations (including |
12 // truncation) into this one filter, so that runs most efficiently? | 12 // truncation) into this one filter, so that runs most efficiently? |
13 // (We would have to make sure truncation still took place after | 13 // (We would have to make sure truncation still took place after |
14 // sorting, though.) | 14 // sorting, though.) |
15 Loader.filter( | 15 Loader.filter( |
16 'removeHiddenItems', | 16 'removeHiddenItems', |
17 function() { | 17 function() { |
18 return function(unfilteredItems, hiddenResultTypes, hiddenConfigs) { | 18 return function(unfilteredItems, hiddenResultTypes, hiddenConfigs, |
| 19 viewingTab) { |
19 var filteredItems = []; | 20 var filteredItems = []; |
20 for (var i = 0; i < unfilteredItems.length; i++) { | 21 for (var i = 0; i < unfilteredItems.length; i++) { |
21 var item = unfilteredItems[i]; | 22 var item = unfilteredItems[i]; |
22 if (!(true == hiddenResultTypes[item.resultType]) && | 23 if (!(true == hiddenResultTypes[item.resultType]) && |
23 !(true == hiddenConfigs[item.config])) { | 24 !(true == hiddenConfigs[item.config]) && |
| 25 (viewingTab == item.tab)) { |
24 filteredItems.push(item); | 26 filteredItems.push(item); |
25 } | 27 } |
26 } | 28 } |
27 return filteredItems; | 29 return filteredItems; |
28 }; | 30 }; |
29 } | 31 } |
30 ); | 32 ); |
31 | 33 |
32 Loader.controller( | 34 Loader.controller( |
33 'Loader.Controller', | 35 'Loader.Controller', |
34 function($scope, $http, $filter, $location) { | 36 function($scope, $http, $filter, $location) { |
35 $scope.windowTitle = "Loading GM Results..."; | 37 $scope.windowTitle = "Loading GM Results..."; |
36 var resultsToLoad = $location.search().resultsToLoad; | 38 var resultsToLoad = $location.search().resultsToLoad; |
37 $scope.loadingMessage = "Loading results of type '" + resultsToLoad + | 39 $scope.loadingMessage = "Loading results of type '" + resultsToLoad + |
38 "', please wait..."; | 40 "', please wait..."; |
39 | 41 |
40 $http.get("/results/" + resultsToLoad).success( | 42 $http.get("/results/" + resultsToLoad).success( |
41 function(data, status, header, config) { | 43 function(data, status, header, config) { |
42 $scope.loadingMessage = "Processing data, please wait..."; | 44 $scope.loadingMessage = "Processing data, please wait..."; |
43 | 45 |
44 $scope.header = data.header; | 46 $scope.header = data.header; |
45 $scope.categories = data.categories; | 47 $scope.categories = data.categories; |
46 $scope.testData = data.testData; | 48 $scope.testData = data.testData; |
47 $scope.sortColumn = 'test'; | 49 $scope.sortColumn = 'test'; |
48 $scope.showTodos = true; | 50 $scope.showTodos = false; |
49 | 51 |
| 52 // Create the list of tabs (lists into which the user can file each |
| 53 // test). This may vary, depending on isEditable. |
| 54 $scope.tabs = [ |
| 55 'Unfiled', 'Hidden' |
| 56 ]; |
| 57 if (data.header.isEditable) { |
| 58 $scope.tabs = $scope.tabs.concat( |
| 59 ['Pending Approval']); |
| 60 } |
| 61 $scope.defaultTab = $scope.tabs[0]; |
| 62 $scope.viewingTab = $scope.defaultTab; |
| 63 |
| 64 // Track the number of results on each tab. |
| 65 $scope.numResultsPerTab = {}; |
| 66 for (var i = 0; i < $scope.tabs.length; i++) { |
| 67 $scope.numResultsPerTab[$scope.tabs[i]] = 0; |
| 68 } |
| 69 $scope.numResultsPerTab[$scope.defaultTab] = $scope.testData.length; |
| 70 |
| 71 // Add index and tab fields to all records. |
50 for (var i = 0; i < $scope.testData.length; i++) { | 72 for (var i = 0; i < $scope.testData.length; i++) { |
51 $scope.testData[i].index = i; | 73 $scope.testData[i].index = i; |
| 74 $scope.testData[i].tab = $scope.defaultTab; |
52 } | 75 } |
53 | 76 |
54 $scope.hiddenResultTypes = { | 77 $scope.hiddenResultTypes = { |
55 'failure-ignored': true, | 78 'failure-ignored': true, |
56 'no-comparison': true, | 79 'no-comparison': true, |
57 'succeeded': true, | 80 'succeeded': true, |
58 }; | 81 }; |
59 $scope.hiddenConfigs = {}; | 82 $scope.hiddenConfigs = {}; |
60 $scope.selectedItems = {}; | 83 $scope.selectedItems = []; |
61 | 84 |
62 $scope.updateResults(); | 85 $scope.updateResults(); |
63 $scope.loadingMessage = ""; | 86 $scope.loadingMessage = ""; |
64 $scope.windowTitle = "Current GM Results"; | 87 $scope.windowTitle = "Current GM Results"; |
65 } | 88 } |
66 ).error( | 89 ).error( |
67 function(data, status, header, config) { | 90 function(data, status, header, config) { |
68 $scope.loadingMessage = "Failed to load results of type '" | 91 $scope.loadingMessage = "Failed to load results of type '" |
69 + resultsToLoad + "'"; | 92 + resultsToLoad + "'"; |
70 $scope.windowTitle = "Failed to Load GM Results"; | 93 $scope.windowTitle = "Failed to Load GM Results"; |
71 } | 94 } |
72 ); | 95 ); |
73 | 96 |
74 $scope.isItemSelected = function(index) { | 97 $scope.isItemSelected = function(index) { |
75 return (true == $scope.selectedItems[index]); | 98 return (-1 != $scope.selectedItems.indexOf(index)); |
76 } | 99 } |
77 $scope.toggleItemSelected = function(index) { | 100 $scope.toggleItemSelected = function(index) { |
78 if (true == $scope.selectedItems[index]) { | 101 var i = $scope.selectedItems.indexOf(index); |
79 delete $scope.selectedItems[index]; | 102 if (-1 == i) { |
| 103 $scope.selectedItems.push(index); |
80 } else { | 104 } else { |
81 $scope.selectedItems[index] = true; | 105 $scope.selectedItems.splice(i, 1); |
82 } | 106 } |
83 // unlike other toggle methods below, does not set | 107 // unlike other toggle methods below, does not set |
84 // $scope.areUpdatesPending = true; | 108 // $scope.areUpdatesPending = true; |
85 } | 109 } |
86 | 110 |
87 $scope.isHiddenResultType = function(thisResultType) { | 111 $scope.isHiddenResultType = function(thisResultType) { |
88 return (true == $scope.hiddenResultTypes[thisResultType]); | 112 return (true == $scope.hiddenResultTypes[thisResultType]); |
89 } | 113 } |
90 $scope.toggleHiddenResultType = function(thisResultType) { | 114 $scope.toggleHiddenResultType = function(thisResultType) { |
91 if (true == $scope.hiddenResultTypes[thisResultType]) { | 115 if (true == $scope.hiddenResultTypes[thisResultType]) { |
(...skipping 14 matching lines...) Expand all Loading... |
106 } | 130 } |
107 $scope.toggleHiddenConfig = function(thisConfig) { | 131 $scope.toggleHiddenConfig = function(thisConfig) { |
108 if (true == $scope.hiddenConfigs[thisConfig]) { | 132 if (true == $scope.hiddenConfigs[thisConfig]) { |
109 delete $scope.hiddenConfigs[thisConfig]; | 133 delete $scope.hiddenConfigs[thisConfig]; |
110 } else { | 134 } else { |
111 $scope.hiddenConfigs[thisConfig] = true; | 135 $scope.hiddenConfigs[thisConfig] = true; |
112 } | 136 } |
113 $scope.areUpdatesPending = true; | 137 $scope.areUpdatesPending = true; |
114 } | 138 } |
115 | 139 |
| 140 $scope.setViewingTab = function(tab) { |
| 141 $scope.viewingTab = tab; |
| 142 $scope.updateResults(); |
| 143 } |
| 144 |
116 $scope.localTimeString = function(secondsPastEpoch) { | 145 $scope.localTimeString = function(secondsPastEpoch) { |
117 var d = new Date(secondsPastEpoch * 1000); | 146 var d = new Date(secondsPastEpoch * 1000); |
118 return d.toString(); | 147 return d.toString(); |
119 } | 148 } |
120 | 149 |
| 150 /** |
| 151 * Move the items in $scope.selectedItems to a different tab, |
| 152 * and then clear $scope.selectedItems. |
| 153 * |
| 154 * @param newTab (string): name of the tab to move the tests to |
| 155 */ |
| 156 $scope.moveSelectedItemsToTab = function(newTab) { |
| 157 $scope.moveItemsToTab($scope.selectedItems, newTab); |
| 158 $scope.selectedItems = []; |
| 159 $scope.updateResults(); |
| 160 } |
| 161 |
| 162 /** |
| 163 * Move a subset of $scope.testData to a different tab. |
| 164 * |
| 165 * @param itemIndices (array of ints): indices into $scope.testData |
| 166 * indicating which test results to move |
| 167 * @param newTab (string): name of the tab to move the tests to |
| 168 */ |
| 169 $scope.moveItemsToTab = function(itemIndices, newTab) { |
| 170 var itemIndex; |
| 171 var numItems = itemIndices.length; |
| 172 for (var i = 0; i < numItems; i++) { |
| 173 itemIndex = itemIndices[i]; |
| 174 $scope.numResultsPerTab[$scope.testData[itemIndex].tab]--; |
| 175 $scope.testData[itemIndex].tab = newTab; |
| 176 } |
| 177 $scope.numResultsPerTab[newTab] += numItems; |
| 178 } |
| 179 |
121 $scope.updateResults = function() { | 180 $scope.updateResults = function() { |
122 $scope.displayLimit = $scope.displayLimitPending; | 181 $scope.displayLimit = $scope.displayLimitPending; |
123 // TODO(epoger): Every time we apply a filter, AngularJS creates | 182 // TODO(epoger): Every time we apply a filter, AngularJS creates |
124 // another copy of the array. Is there a way we can filter out | 183 // another copy of the array. Is there a way we can filter out |
125 // the items as they are displayed, rather than storing multiple | 184 // the items as they are displayed, rather than storing multiple |
126 // array copies? (For better performance.) | 185 // array copies? (For better performance.) |
127 $scope.filteredTestData = | 186 |
128 $filter("orderBy")( | 187 if ($scope.viewingTab == $scope.defaultTab) { |
129 $filter("removeHiddenItems")( | 188 $scope.filteredTestData = |
130 $scope.testData, | 189 $filter("orderBy")( |
131 $scope.hiddenResultTypes, | 190 $filter("removeHiddenItems")( |
132 $scope.hiddenConfigs | 191 $scope.testData, |
133 ), | 192 $scope.hiddenResultTypes, |
134 $scope.sortColumn); | 193 $scope.hiddenConfigs, |
135 $scope.limitedTestData = $filter("limitTo")( | 194 $scope.viewingTab |
136 $scope.filteredTestData, $scope.displayLimit); | 195 ), |
| 196 $scope.sortColumn); |
| 197 $scope.limitedTestData = $filter("limitTo")( |
| 198 $scope.filteredTestData, $scope.displayLimit); |
| 199 } else { |
| 200 $scope.filteredTestData = |
| 201 $filter("orderBy")( |
| 202 $filter("filter")( |
| 203 $scope.testData, |
| 204 {tab: $scope.viewingTab}, |
| 205 true |
| 206 ), |
| 207 $scope.sortColumn); |
| 208 $scope.limitedTestData = $filter("limitTo")( |
| 209 $scope.filteredTestData, $scope.displayLimit); |
| 210 } |
137 $scope.imageSize = $scope.imageSizePending; | 211 $scope.imageSize = $scope.imageSizePending; |
138 $scope.areUpdatesPending = false; | 212 $scope.areUpdatesPending = false; |
139 } | 213 } |
140 | 214 |
141 $scope.sortResultsBy = function(sortColumn) { | 215 $scope.sortResultsBy = function(sortColumn) { |
142 $scope.sortColumn = sortColumn; | 216 $scope.sortColumn = sortColumn; |
143 $scope.updateResults(); | 217 $scope.updateResults(); |
144 } | 218 } |
| 219 |
| 220 /** |
| 221 * Tell the server that the actual results of these particular tests |
| 222 * are acceptable. |
| 223 * |
| 224 * @param testDataSubset an array of test results, most likely a subset of |
| 225 * $scope.testData (perhaps with some modifications) |
| 226 */ |
| 227 $scope.submitApprovals = function(testDataSubset) { |
| 228 $scope.submitPending = true; |
| 229 var newResults = []; |
| 230 for (var i = 0; i < testDataSubset.length; i++) { |
| 231 var actualResult = testDataSubset[i]; |
| 232 var expectedResult = { |
| 233 builder: actualResult['builder'], |
| 234 test: actualResult['test'], |
| 235 config: actualResult['config'], |
| 236 expectedHashType: actualResult['actualHashType'], |
| 237 expectedHashDigest: actualResult['actualHashDigest'], |
| 238 }; |
| 239 newResults.push(expectedResult); |
| 240 } |
| 241 $http({ |
| 242 method: "POST", |
| 243 url: "/edits", |
| 244 data: { |
| 245 oldResultsType: $scope.header.type, |
| 246 oldResultsHash: $scope.header.dataHash, |
| 247 modifications: newResults |
| 248 } |
| 249 }).success(function(data, status, headers, config) { |
| 250 var itemIndicesToMove = []; |
| 251 for (var i = 0; i < testDataSubset.length; i++) { |
| 252 itemIndicesToMove.push(testDataSubset[i].index); |
| 253 } |
| 254 $scope.moveItemsToTab(itemIndicesToMove, |
| 255 "HackToMakeSureThisItemDisappears"); |
| 256 $scope.updateResults(); |
| 257 alert("New baselines submitted successfully!\n\n" + |
| 258 "You still need to commit the updated expectations files on " + |
| 259 "the server side to the Skia repo.\n\n" + |
| 260 "Also: in order to see the complete updated data, or to submit " + |
| 261 "more baselines, you will need to reload your client."); |
| 262 $scope.submitPending = false; |
| 263 }).error(function(data, status, headers, config) { |
| 264 alert("There was an error submitting your baselines.\n\n" + |
| 265 "Please see server-side log for details."); |
| 266 $scope.submitPending = false; |
| 267 }); |
| 268 } |
145 } | 269 } |
146 ); | 270 ); |
OLD | NEW |