blob: 09b66d554696e339b723750a02f5dafbf627adb7 [file] [log] [blame]
epoger@google.comf9d134d2013-09-27 15:02:44 +00001/*
2 * Loader:
epoger@google.comafaad3d2013-09-30 15:06:25 +00003 * Reads GM result reports written out by results.py, and imports
4 * them into $scope.categories and $scope.testData .
epoger@google.comf9d134d2013-09-27 15:02:44 +00005 */
6var Loader = angular.module(
7 'Loader',
8 []
9);
epoger@google.com5f2bb002013-10-02 18:57:48 +000010
11// TODO(epoger): Combine ALL of our filtering operations (including
12// truncation) into this one filter, so that runs most efficiently?
13// (We would have to make sure truncation still took place after
14// sorting, though.)
15Loader.filter(
16 'removeHiddenItems',
17 function() {
epoger@google.comeb832592013-10-23 15:07:26 +000018 return function(unfilteredItems, hiddenResultTypes, hiddenConfigs,
19 viewingTab) {
epoger@google.com5f2bb002013-10-02 18:57:48 +000020 var filteredItems = [];
21 for (var i = 0; i < unfilteredItems.length; i++) {
epoger@google.com9fb6c8a2013-10-09 18:05:58 +000022 var item = unfilteredItems[i];
23 if (!(true == hiddenResultTypes[item.resultType]) &&
epoger@google.comeb832592013-10-23 15:07:26 +000024 !(true == hiddenConfigs[item.config]) &&
25 (viewingTab == item.tab)) {
epoger@google.com9fb6c8a2013-10-09 18:05:58 +000026 filteredItems.push(item);
27 }
epoger@google.com5f2bb002013-10-02 18:57:48 +000028 }
29 return filteredItems;
30 };
31 }
32);
33
epoger@google.comf9d134d2013-09-27 15:02:44 +000034Loader.controller(
35 'Loader.Controller',
epoger@google.com542b65f2013-10-15 20:10:33 +000036 function($scope, $http, $filter, $location) {
37 $scope.windowTitle = "Loading GM Results...";
epoger@google.comdcb4e652013-10-11 18:45:33 +000038 var resultsToLoad = $location.search().resultsToLoad;
39 $scope.loadingMessage = "Loading results of type '" + resultsToLoad +
40 "', please wait...";
41
42 $http.get("/results/" + resultsToLoad).success(
43 function(data, status, header, config) {
44 $scope.loadingMessage = "Processing data, please wait...";
45
46 $scope.header = data.header;
47 $scope.categories = data.categories;
48 $scope.testData = data.testData;
epoger@google.comf9d134d2013-09-27 15:02:44 +000049 $scope.sortColumn = 'test';
epoger@google.comeb832592013-10-23 15:07:26 +000050 $scope.showTodos = false;
epoger@google.com5f2bb002013-10-02 18:57:48 +000051
epoger@google.comeb832592013-10-23 15:07:26 +000052 // 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.
epoger@google.comdcb4e652013-10-11 18:45:33 +000072 for (var i = 0; i < $scope.testData.length; i++) {
73 $scope.testData[i].index = i;
epoger@google.comeb832592013-10-23 15:07:26 +000074 $scope.testData[i].tab = $scope.defaultTab;
epoger@google.comdcb4e652013-10-11 18:45:33 +000075 }
76
epoger@google.com9fb6c8a2013-10-09 18:05:58 +000077 $scope.hiddenResultTypes = {
78 'failure-ignored': true,
79 'no-comparison': true,
80 'succeeded': true,
81 };
82 $scope.hiddenConfigs = {};
epoger@google.comeb832592013-10-23 15:07:26 +000083 $scope.selectedItems = [];
epoger@google.com5f2bb002013-10-02 18:57:48 +000084
85 $scope.updateResults();
epoger@google.comdcb4e652013-10-11 18:45:33 +000086 $scope.loadingMessage = "";
epoger@google.com542b65f2013-10-15 20:10:33 +000087 $scope.windowTitle = "Current GM Results";
epoger@google.comdcb4e652013-10-11 18:45:33 +000088 }
89 ).error(
90 function(data, status, header, config) {
91 $scope.loadingMessage = "Failed to load results of type '"
92 + resultsToLoad + "'";
epoger@google.com542b65f2013-10-15 20:10:33 +000093 $scope.windowTitle = "Failed to Load GM Results";
epoger@google.comf9d134d2013-09-27 15:02:44 +000094 }
95 );
epoger@google.com5f2bb002013-10-02 18:57:48 +000096
epoger@google.com9fb6c8a2013-10-09 18:05:58 +000097 $scope.isItemSelected = function(index) {
epoger@google.comeb832592013-10-23 15:07:26 +000098 return (-1 != $scope.selectedItems.indexOf(index));
epoger@google.com9fb6c8a2013-10-09 18:05:58 +000099 }
100 $scope.toggleItemSelected = function(index) {
epoger@google.comeb832592013-10-23 15:07:26 +0000101 var i = $scope.selectedItems.indexOf(index);
102 if (-1 == i) {
103 $scope.selectedItems.push(index);
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000104 } else {
epoger@google.comeb832592013-10-23 15:07:26 +0000105 $scope.selectedItems.splice(i, 1);
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000106 }
107 // unlike other toggle methods below, does not set
108 // $scope.areUpdatesPending = true;
109 }
110
epoger@google.com5f2bb002013-10-02 18:57:48 +0000111 $scope.isHiddenResultType = function(thisResultType) {
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000112 return (true == $scope.hiddenResultTypes[thisResultType]);
epoger@google.com5f2bb002013-10-02 18:57:48 +0000113 }
114 $scope.toggleHiddenResultType = function(thisResultType) {
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000115 if (true == $scope.hiddenResultTypes[thisResultType]) {
116 delete $scope.hiddenResultTypes[thisResultType];
epoger@google.com5f2bb002013-10-02 18:57:48 +0000117 } else {
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000118 $scope.hiddenResultTypes[thisResultType] = true;
epoger@google.com5f2bb002013-10-02 18:57:48 +0000119 }
120 $scope.areUpdatesPending = true;
121 }
122
123 // TODO(epoger): Rather than maintaining these as hard-coded
124 // variants of isHiddenResultType and toggleHiddenResultType, we
125 // should create general-purpose functions that can work with ANY
126 // category.
127 // But for now, I wanted to see this working. :-)
128 $scope.isHiddenConfig = function(thisConfig) {
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000129 return (true == $scope.hiddenConfigs[thisConfig]);
epoger@google.com5f2bb002013-10-02 18:57:48 +0000130 }
131 $scope.toggleHiddenConfig = function(thisConfig) {
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000132 if (true == $scope.hiddenConfigs[thisConfig]) {
133 delete $scope.hiddenConfigs[thisConfig];
epoger@google.com5f2bb002013-10-02 18:57:48 +0000134 } else {
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000135 $scope.hiddenConfigs[thisConfig] = true;
epoger@google.com5f2bb002013-10-02 18:57:48 +0000136 }
137 $scope.areUpdatesPending = true;
138 }
139
epoger@google.comeb832592013-10-23 15:07:26 +0000140 $scope.setViewingTab = function(tab) {
141 $scope.viewingTab = tab;
142 $scope.updateResults();
143 }
144
epoger@google.com542b65f2013-10-15 20:10:33 +0000145 $scope.localTimeString = function(secondsPastEpoch) {
146 var d = new Date(secondsPastEpoch * 1000);
147 return d.toString();
148 }
149
epoger@google.comeb832592013-10-23 15:07:26 +0000150 /**
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
epoger@google.com5f2bb002013-10-02 18:57:48 +0000180 $scope.updateResults = function() {
181 $scope.displayLimit = $scope.displayLimitPending;
182 // TODO(epoger): Every time we apply a filter, AngularJS creates
183 // another copy of the array. Is there a way we can filter out
184 // the items as they are displayed, rather than storing multiple
185 // array copies? (For better performance.)
epoger@google.comeb832592013-10-23 15:07:26 +0000186
187 if ($scope.viewingTab == $scope.defaultTab) {
188 $scope.filteredTestData =
189 $filter("orderBy")(
190 $filter("removeHiddenItems")(
191 $scope.testData,
192 $scope.hiddenResultTypes,
193 $scope.hiddenConfigs,
194 $scope.viewingTab
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 }
epoger@google.com5f2bb002013-10-02 18:57:48 +0000211 $scope.imageSize = $scope.imageSizePending;
212 $scope.areUpdatesPending = false;
213 }
214
215 $scope.sortResultsBy = function(sortColumn) {
216 $scope.sortColumn = sortColumn;
217 $scope.updateResults();
218 }
epoger@google.comeb832592013-10-23 15:07:26 +0000219
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 }
epoger@google.comf9d134d2013-09-27 15:02:44 +0000269 }
270);