github.com/abayer/test-infra@v0.0.5/mungegithub/submit-queue/www/script.js (about) 1 "use strict"; 2 var app = angular.module('SubmitQueueModule', ['ngMaterial', 'md.data.table', 'angular-toArrayFilter', 'angularMoment']); 3 4 app.config(['$compileProvider', function ($compileProvider) { 5 $compileProvider.debugInfoEnabled(false); 6 }]); 7 8 app.controller('SQCntl', ['DataService', '$interval', '$location', SQCntl]); 9 10 function SQCntl(dataService, $interval, $location) { 11 var self = this; 12 self.prs = {}; 13 self.prsCount = 0; 14 self.health = {}; 15 self.metadata = {}; 16 self.ciStatus = {}; 17 self.lastMergeTime = Date(); 18 self.prQuerySearch = prQuerySearch; 19 self.historyQuerySearch = historyQuerySearch; 20 self.goToPerson = goToPerson; 21 self.selectTab = selectTab; 22 self.tabLoaded = {}; 23 self.functionLoading = {}; 24 self.queryNum = 0; 25 self.selected = 1; 26 self.OverallHealth = ""; 27 self.StatOrder = "-Count"; 28 self.batch = {}; 29 self.location = $location; 30 31 // http://submit-queue.k8s.io/#?prDisplay=eparis&historyDisplay=15999 32 // will show all PRs opened by eparis and all historic decisions for PR #15999 33 var vars = $location.search(); 34 self.prDisplayValue = vars.prDisplay; 35 self.historyDisplayValue = vars.historyDisplay; 36 37 var path = $location.path(); 38 if (path.length > 0) { 39 switch (path) { 40 case "/prs": 41 self.selected=0; 42 break; 43 case "/queue": 44 self.selected=1; 45 break; 46 case "/history": 47 self.selected=2; 48 break; 49 // NOTE: /e2e was moved to /ci 50 // TODO: display a toast noting this 51 case "/e2e": 52 self.selected=3; 53 self.location.path("/ci"); 54 break; 55 case "/ci": 56 self.selected=3; 57 break; 58 case "/info": 59 self.selected=4; 60 break; 61 default: 62 console.log("unknown path: " + path); 63 break; 64 } 65 } 66 67 // Populate data about the submit-queue instance. 68 refreshMetadata(); 69 70 loadTab(self.selected); 71 72 // Defer loading of data for a tab until it's actually needed. 73 // This speeds up the first-boot experience a LOT. 74 function loadTab() { 75 // reloadFunctions is a map from functions to how often they should run (in minutes) 76 var reloadFunctions = {} 77 reloadFunctions[refreshPRs] = 10; 78 reloadFunctions[refreshGithubE2E] = 0.5; 79 reloadFunctions[refreshSQStats] = 30; 80 reloadFunctions[refreshHistoryPRs] = 1; 81 reloadFunctions[refreshE2EHealth] = 10; 82 reloadFunctions[refreshBotStats] = 10; 83 84 // tabFunctionReloads is a map of which tabs need which functions to refresh 85 var tabFunctionReloads = { 86 0: [refreshPRs], 87 1: [refreshGithubE2E, refreshSQStats], 88 2: [refreshHistoryPRs], 89 3: [refreshE2EHealth, refreshSQStats], 90 4: [refreshSQStats, refreshBotStats], 91 } 92 if (self.tabLoaded[self.selected]) { 93 return; 94 } 95 self.tabLoaded[self.selected] = true; 96 97 var reloadFuncs = tabFunctionReloads[self.selected]; 98 if (reloadFuncs === undefined) 99 return; 100 101 for (var i = 0; i < reloadFuncs.length; i++) { 102 var func = reloadFuncs[i]; 103 if (self.functionLoading[func] == true) { 104 continue; 105 } 106 self.functionLoading[func] = true; 107 108 var updateIntervalMinutes = reloadFunctions[func]; 109 func(); 110 $interval(func, updateIntervalMinutes * 60 * 1000); 111 } 112 } 113 114 // This data is shown in a top banner (when the Queue is blocked), 115 // so it's always loaded. 116 refreshContinuousTests(); 117 $interval(refreshContinuousTests, 60000); // Refresh every minute 118 119 // Request Avatars that are only as large necessary (CSS limits them to 40px) 120 function fixPRAvatars(prs) { 121 angular.forEach(prs, function(pr) { 122 if (/^https:\/\/avatars.githubusercontent.com\/u\/\d+\?v=3$/.test(pr.AvatarURL)) { 123 pr.AvatarURL += '&size=40'; 124 } 125 }); 126 } 127 128 function refreshPRs() { 129 dataService.getData('prs').then(function successCallback(response) { 130 var prs = response.data.PRStatus; 131 fixPRAvatars(prs); 132 self.prs = prs; 133 self.prsCount = Object.keys(prs).length; 134 self.prSearchTerms = getPRSearchTerms(); 135 }, function errorCallback(response) { 136 console.log("Error: Getting SubmitQueue Status"); 137 }); 138 } 139 140 function refreshMetadata() { 141 dataService.getData('metadata').then(function successCallback(response) { 142 var metadata = response.data; 143 self.metadata = metadata; 144 }, function errorCallback(response) { 145 console.log("Error: Getting MetaData for SubmitQueue"); 146 }); 147 } 148 149 function refreshGithubE2E() { 150 dataService.getData('github-e2e-queue').then(function successCallback(response) { 151 if (response.data.E2ERunning.Number == 0) { 152 self.e2erunning = []; 153 } else { 154 self.e2erunning = [response.data.E2ERunning]; 155 } 156 self.e2equeue = response.data.E2EQueue; 157 self.batch = response.data.BatchStatus; 158 fixPRAvatars(self.e2equeue); 159 }); 160 } 161 162 function refreshHistoryPRs() { 163 dataService.getData('history').then(function successCallback(response) { 164 var prs = response.data; 165 fixPRAvatars(prs); 166 self.historyPRs = prs; 167 self.historySearchTerms = getHistorySearchTerms(); 168 }); 169 } 170 171 function refreshSQStats() { 172 dataService.getData('sq-stats').then(function successCallback(response) { 173 self.sqStats = response.data; 174 self.lastMergeTime = new Date(response.data.LastMergeTime) 175 }); 176 } 177 178 function refreshBotStats() { 179 dataService.getData('stats').then(function successCallback(response) { 180 self.botStats = response.data; 181 for (var key in self.botStats.Analytics) { 182 var analytic = self.botStats.Analytics[key]; 183 analytic.UncachedCount = analytic.Count - analytic.CachedCount; 184 } 185 self.botStats.UncachedAPICount = self.botStats.APICount - self.botStats.CachedAPICount; 186 }); 187 } 188 189 function refreshE2EHealth() { 190 dataService.getData("health").then(function successCallback(response) { 191 self.health = response.data; 192 if (self.health.TotalLoops !== 0) { 193 var percentStable = self.health.NumStable * 100.0 / self.health.TotalLoops; 194 self.OverallHealth = Math.round(percentStable) + "%"; 195 } 196 }); 197 } 198 199 function refreshContinuousTests() { 200 dataService.getData('ci-status').then(function successCallback(response) { 201 self.ciStatus = processTests(response.data); 202 }); 203 } 204 205 function processTests(data) { 206 var results = {}; 207 angular.forEach(data, function(jobs, key) { 208 // job results are keyed by job.Type/category, 209 // but in javascript we can't use / in an identifier so use $ instead 210 key = key.replace('/', '$'); 211 results[key] = []; 212 var parts = key.split('$'); 213 var type = parts[0]; 214 var category = parts[1]; 215 if (category != "") { 216 if (!(category in results)) { 217 results[category] = []; 218 } 219 } 220 angular.forEach(jobs, function(job, jobName) { 221 var obj = { 222 'name': jobName, 223 'id': job.build_id, 224 'url': job.url, 225 'msg': '', 226 }; 227 if (job.state == 'success') { 228 // green heavy check mark 229 obj.state = '\u2714'; 230 obj.color = 'green'; 231 } else { 232 //red x mark 233 obj.state = '\u2716'; 234 obj.color = 'red'; 235 } 236 results[key].push(obj); 237 if (category != "") { 238 results[category].push(obj); 239 } 240 }); 241 }); 242 angular.forEach(results, function(r, key) { 243 results[key].sort(function(a, b) { 244 if (a.name < b.name) { 245 return -1; 246 } else if (a.name > b.name) { 247 return 1; 248 } 249 return 0; 250 }); 251 }); 252 return results; 253 } 254 255 function searchTermsContain(terms, value) { 256 var found = false; 257 angular.forEach(terms, function(term) { 258 if (term.value === value) { 259 found = true; 260 } 261 }); 262 return found; 263 } 264 265 function getPRSearchTerms() { 266 return getSearchTerms(self.prs); 267 } 268 269 function getHistorySearchTerms() { 270 return getSearchTerms(self.historyPRs); 271 } 272 273 function getSearchTerms(prs) { 274 var result = []; 275 angular.forEach(prs, function(pr) { 276 var llogin = pr.Login.toLowerCase(); 277 if (!searchTermsContain(result, llogin)) { 278 var loginobj = { 279 value: llogin, 280 display: pr.Login, 281 }; 282 result.push(loginobj); 283 } 284 if (!searchTermsContain(result, pr.Num)) { 285 var numobj = { 286 value: pr.Number.toString(), 287 display: pr.Number, 288 }; 289 result.push(numobj); 290 } 291 }); 292 result.sort(compareSearchTerms); 293 return result; 294 } 295 296 /* We need to compare the 'value' to get a sane sort */ 297 function compareSearchTerms(a, b) { 298 if (a.value > b.value) { 299 return 1; 300 } 301 if (a.value < b.value) { 302 return -1; 303 } 304 return 0; 305 } 306 307 function prQuerySearch(query) { 308 return querySearch(query, self.prSearchTerms); 309 } 310 311 function historyQuerySearch(query) { 312 return querySearch(query, self.historySearchTerms); 313 } 314 315 function querySearch(query, terms) { 316 var results = query ? terms.filter(createFilterFor(query)) : terms; 317 return results; 318 } 319 320 /** 321 * Create filter function for a query string 322 */ 323 function createFilterFor(query) { 324 var lowercaseQuery = angular.lowercase(query); 325 return function filterFn(state) { 326 return (state.value.indexOf(lowercaseQuery) === 0); 327 }; 328 } 329 330 function goToPerson(person) { 331 window.location.href = 'https://github.com/' + person; 332 } 333 334 function selectTab(tabName) { 335 self.location.path('/' + tabName); 336 loadTab(); 337 } 338 339 getPriorityInfo() 340 341 function getPriorityInfo() { 342 dataService.getData('priority-info').then(function successCallback(response) { 343 document.getElementById("priority-info").innerHTML = response.data; 344 }); 345 } 346 347 getMergeInfo() 348 349 function getMergeInfo() { 350 dataService.getData('merge-info').then(function successCallback(response) { 351 document.getElementById("merge-info").innerHTML = response.data; 352 }); 353 } 354 } 355 356 357 app.filter('loginOrPR', function() { 358 return function(prs, searchVal) { 359 searchVal = searchVal || ""; 360 prs = prs || []; 361 searchVal = angular.lowercase(searchVal); 362 var out = []; 363 364 angular.forEach(prs, function(pr) { 365 var shouldPush = false; 366 var llogin = pr.Login.toLowerCase(); 367 if (llogin.indexOf(searchVal) === 0) { 368 shouldPush = true; 369 } 370 if (pr.Number.toString().indexOf(searchVal) === 0) { 371 shouldPush = true; 372 } 373 if (shouldPush) { 374 out.push(pr); 375 } 376 }); 377 return out; 378 }; 379 }); 380 381 app.filter('refToPRs', function() { 382 return function(ref) { 383 return ref.replace(/(?:(\d+)|[^:]+):[^,]*,?/g, '$1 ').trim().split(' '); 384 } 385 }) 386 387 app.service('DataService', ['$http', dataService]); 388 389 function dataService($http) { 390 return ({ 391 getData: getData, 392 }); 393 394 function getData(file) { 395 return $http.get(file); 396 } 397 }