Index: status/res/imp/buildbot-dash-sk.html |
diff --git a/status/res/imp/buildbot-dash-sk.html b/status/res/imp/buildbot-dash-sk.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e1ba5ecea06e516e01d3e479029ad857511ea70b |
--- /dev/null |
+++ b/status/res/imp/buildbot-dash-sk.html |
@@ -0,0 +1,284 @@ |
+<!-- |
+ The common.js file must be included before this file. |
+ |
+ This in an HTML Import-able file that contains the definition |
+ of the following elements: |
+ |
+ <buildbot-dash-sk> |
+ |
+ To use this file import it: |
+ |
+ <link href="/res/imp/buildbot-dash-sk.html" rel="import" /> |
+ |
+ Usage: |
+ |
+ <buildbot-dash-sk></buildbot-dash-sk> |
+--> |
+<polymer-element name="buildbot-dash-sk"> |
+ <template> |
+ <style> |
+ paper-button { |
+ text-transform: none; |
+ } |
+ h1 { |
+ font-size: 1.7em; |
+ margin-bottom: 2px; |
+ margin-top: 5px; |
+ } |
+ #controls { |
+ width: 200px; |
+ } |
+ .control { |
+ margin: 5px; |
+ padding: 10px; |
+ border: 1px solid #eeeeee; |
+ font-size: 12px; |
+ } |
+ .control > h2 { |
+ font-size: 16px; |
+ } |
+ #maincontent { |
+ padding-top: 10px; |
+ } |
+ </style> |
+ <div id="maincontent"> |
+ <div id="spinner" horizontal layout center fit> |
+ <div vertical layout center flex> |
+ <paper-spinner active></paper-spinner> |
+ </div> |
+ </div> |
+ <div id="chart_container" horizontal layout> |
+ <div id="controls"> |
+ <div class="control"> |
+ <h2>Results from last</h2> |
+ <paper-button id="time_select_button" on-click="{{openTimeSelect}}"> |
+ <div id="time_select_label"></div> |
+ <core-icon icon="arrow-drop-down"></core-icon> |
+ </paper-button> |
+ <paper-dropdown id="time_select"> |
+ <core-menu id="time_menu" selected=0 on-core-select="{{timeSelected}}"> |
+ <paper-item value=24>24 hours</paper-item> |
+ <paper-item value=72>3 days</paper-item> |
+ <paper-item value=168>1 week</paper-item> |
+ <paper-item value=336>2 weeks</paper-item> |
+ </core-menu> |
+ </paper-dropdown> |
+ </div> |
+ <input-list-sk |
+ id="include_builders" |
+ heading="Include Patterns" |
+ values="{{include}}" |
+ on-change="{{processBuilds}}"></input-list-sk> |
+ <input-list-sk |
+ id="exclude_builders" |
+ heading="Exclude Patterns" |
+ values="{{exclude}}" |
+ on-change="{{processBuilds}}"></input-list-sk> |
+ <div class="control"> |
+ <h2>Excluded Builders</h2> |
+ <ul> |
+ <template repeat="{{bot in excludedBots}}"> |
+ <li>{{bot}}</li> |
+ </template> |
+ </ul> |
+ </div> |
+ </div> |
+ <div id="charts" flex> |
+ <bar-chart-sk heading="Build Times" id="build_times_chart"></bar-chart-sk> |
+ <bar-chart-sk heading="Step Times" id="step_times_chart"></bar-chart-sk> |
+ <bar-chart-sk heading="Build Failure Rate" id="build_failure_rate_chart"></bar-chart-sk> |
+ <bar-chart-sk heading="Step Failure Rate" id="step_failure_rate_chart"></bar-chart-sk> |
+ </div> |
+ </div> |
+ </div> |
+ </template> |
+ <script> |
+ (function() { |
+ function mean(data) { |
+ // TODO(borenet): Use a more stable algorithm. |
+ var sum = 0; |
+ for (var i = 0; i < data.length; i++) { |
+ sum += data[i]; |
+ } |
+ return sum / data.length; |
+ } |
+ |
+ Polymer({ |
+ created: function() { |
+ this.builds = []; |
+ this.buildTimes = {}; |
+ this.stepTimes = {}; |
+ this.buildResults = {}; |
+ this.stepResults = {}; |
+ |
+ this.include = []; |
+ this.exclude = []; |
+ this.excludedBots = []; |
+ |
+ var palette = [ |
+ "#03DCFB", "#00C2DD", "#008699", "#006C7C", "#00535E", // Blue |
+ "#FFAE00", "#FFAE00", "#FAAB00", "#CA8A00", "#9A6900", // Yellow |
+ "#FF1300", "#FF1300", "#FA1200", "#CA0F00", "#9A0B00", // Red |
+ ]; |
+ var paletteRowLen = 5; |
+ this.colors = [ |
+ palette[2*paletteRowLen+3], |
+ palette[1*paletteRowLen+3], |
+ palette[0*paletteRowLen+3], |
+ ]; |
+ }, |
+ |
+ reloadBuilds: function(start, end) { |
+ console.time("loadData"); |
+ url = "/json/builds"; |
+ if (!!start) { |
+ url += "?start=" + start; |
+ if (!!end) { |
+ url += "&end=" + end; |
+ } |
+ } |
+ this.$.spinner.style.display = "flex"; |
+ this.$.chart_container.style.display = "none"; |
+ var that = this; |
+ sk.get(url).then(JSON.parse).then(function(json) { |
+ console.timeEnd("loadData"); |
+ that.builds = json; |
+ that.processBuilds(); |
+ that.$.spinner.style.display = "none"; |
+ that.$.chart_container.style.display = "flex"; |
+ }); |
+ }, |
+ |
+ includeBuilder: function(builder) { |
+ for (var i = 0; i < this.exclude.length; i++) { |
+ if (builder.match(this.exclude[i])) { |
+ return false; |
+ } |
+ } |
+ for (var i = 0; i < this.include.length; i++) { |
+ if (!builder.match(this.include[i])) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ }, |
+ |
+ processBuilds: function() { |
+ console.time("processBuilds"); |
+ this.buildTimes = {}; |
+ this.stepTimes = {}; |
+ this.buildResults = {}; |
+ this.stepResults = {}; |
+ this.excludedBots = []; |
+ |
+ for (var i = 0; i < this.builds.length; i++) { |
+ var build = this.builds[i]; |
+ if (!this.includeBuilder(build.Builder)) { |
+ this.excludedBots.push(build.Builder); |
+ continue; |
+ } |
+ |
+ var duration = build.Finished - build.Started; |
+ if (!this.buildTimes[build.Builder]) { |
+ this.buildTimes[build.Builder] = []; |
+ } |
+ this.buildTimes[build.Builder].push(duration); |
+ |
+ if (!this.buildResults[build.Builder]) { |
+ this.buildResults[build.Builder] = []; |
+ } |
+ this.buildResults[build.Builder].push(build.Results == 0 ? 0 : 1); |
+ |
+ for (var j = 0; j < build.Steps.length; j++) { |
+ var step = build.Steps[j]; |
+ // Always exclude these steps. |
+ if (step.Name == "steps" || step.Name == "Uncaught Exception") { |
+ continue; |
+ } |
+ var stepDuration = step.Finished - step.Started; |
+ if (!this.stepTimes[step.Name]) { |
+ this.stepTimes[step.Name] = []; |
+ } |
+ this.stepTimes[step.Name].push(stepDuration); |
+ |
+ if (!this.stepResults[step.Name]) { |
+ this.stepResults[step.Name] = []; |
+ } |
+ this.stepResults[step.Name].push(step.Results == 0 ? 0 : 1); |
+ } |
+ } |
+ |
+ console.timeEnd("processBuilds"); |
+ this.drawCharts(); |
+ }, |
+ |
+ drawCharts: function() { |
+ console.time("drawCharts"); |
+ // Draw charts. |
+ |
+ // Build times. |
+ this.$.build_times_chart.colors = this.colors; |
+ this.$.build_times_chart.columns = [ |
+ ["string", "Builder"], |
+ ["number", "Time (s)"], |
+ ]; |
+ this.$.build_times_chart.data = this.generateStats(this.buildTimes, mean); |
+ |
+ // Step times. |
+ this.$.step_times_chart.colors = this.colors; |
+ this.$.step_times_chart.columns = [ |
+ ["string", "Step"], |
+ ["number", "Time (s)"], |
+ ]; |
+ this.$.step_times_chart.data = this.generateStats(this.stepTimes, mean); |
+ |
+ // Build failure rate. |
+ this.$.build_failure_rate_chart.colors = this.colors; |
+ this.$.build_failure_rate_chart.columns = [ |
+ ["string", "Builder"], |
+ ["number", "Failure Rate"], |
+ ]; |
+ this.$.build_failure_rate_chart.data = this.generateStats(this.buildResults, mean); |
+ |
+ // Step failure rate. |
+ this.$.step_failure_rate_chart.colors = this.colors; |
+ this.$.step_failure_rate_chart.columns = [ |
+ ["string", "Step"], |
+ ["number", "Failure Rate"], |
+ ]; |
+ this.$.step_failure_rate_chart.data = this.generateStats(this.stepResults, mean); |
+ |
+ console.timeEnd("drawCharts"); |
+ }, |
+ |
+ generateStats: function(data, aggregator) { |
+ var stats = []; |
+ for (var series in data) { |
+ stats.push([series, aggregator(data[series])]); |
+ } |
+ stats.sort(function(a, b) { |
+ return b[1] - a[1]; |
+ }); |
+ return stats; |
+ }, |
+ |
+ openTimeSelect: function() { |
+ this.$.time_select.open(); |
+ }, |
+ |
+ timeSelected: function(e) { |
+ this.$.time_select.close(); |
+ if (e.detail.isSelected) { |
+ this.$.time_select_label.innerHTML = e.detail.item.innerHTML; |
+ this.updateTimePeriod(e.detail.item.getAttribute("value")); |
+ } |
+ }, |
+ |
+ updateTimePeriod: function(timePeriod) { |
+ var now = Math.round(new Date().getTime() / 1000); |
+ this.reloadBuilds(now - timePeriod * 3600, now); |
+ }, |
+ }); |
+ })(); |
+ </script> |
+</polymer-element> |