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  }