OLD | NEW |
| (Empty) |
1 <!-- Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
2 Use of this source code is governed by a BSD-style license that can be | |
3 found in the LICENSE file. --> | |
4 | |
5 <link rel="import" href="../bower_components/core-ajax/core-ajax.html"> | |
6 | |
7 <polymer-element name="build-table" attributes="builds auto"> | |
8 <template> | |
9 <style> | |
10 :host { | |
11 font-family: 'RobotoDraft'; | |
12 font-size: 13px; | |
13 font-weight: 400; | |
14 line-height: 1.5; | |
15 color: #212121; | |
16 } | |
17 table { | |
18 border-spacing: 0; | |
19 } | |
20 th, td { | |
21 padding: 4px 16px 4px 4px; | |
22 text-align: left; | |
23 cursor: pointer; | |
24 } | |
25 .error { | |
26 color: red; | |
27 font-weight: bold; | |
28 } | |
29 .warning { | |
30 color: orange; | |
31 font-weight: bold; | |
32 } | |
33 .ok { | |
34 color: green; | |
35 font-weight: bold; | |
36 } | |
37 tr:hover { | |
38 background-color: #e9eaed; | |
39 } | |
40 th { | |
41 background-color: white; | |
42 } | |
43 th[data-sortdown] { | |
44 background-image: url('/images/sort-down.gif'); | |
45 background-repeat: no-repeat; | |
46 background-position: 95% 40%; | |
47 background-color: white; | |
48 } | |
49 th[data-sortup] { | |
50 background-image: url('/images/sort-up.gif'); | |
51 background-repeat: no-repeat; | |
52 background-position: 95% 40%; | |
53 background-color: white; | |
54 } | |
55 </style> | |
56 <table> | |
57 <tr> | |
58 <th data-type="master" on-click="{{ updateSort }}">Master</th> | |
59 <th data-type="builder" on-click="{{ updateSort }}">Builder</th> | |
60 <th data-type="buildnumber" on-click="{{ updateSort }}">Build Number</th
> | |
61 <th data-type="buildtime" on-click="{{ updateSort }}">Build Time (HH:MM:
SS)</th> | |
62 <th data-type="result" on-click="{{ updateSort }}">Result</th> | |
63 <th data-type="revision" on-click="{{ updateSort }}">Revision</th> | |
64 <th data-type="name" on-click="{{ updateSortForResponse }}">Longest Step
</th> | |
65 <th data-type="duration" on-click="{{ updateSortForResponse }}">Longest
Step Duration</th> | |
66 <th data-type="percent" on-click="{{ updateSortForResponse }}">Longest S
tep %</th> | |
67 </tr> | |
68 <template repeat="{{ build in builds }}"> | |
69 <tr data-master="{{ build.master }}" | |
70 data-builder="{{ build.builder }}" | |
71 data-buildnumber="{{ build.buildnumber }}" | |
72 on-click="{{ trClick }}"> | |
73 <td>{{ build.master }}</td> | |
74 <td>{{ build.builder }}</td> | |
75 <td>{{ build.buildnumber }}</td> | |
76 <td class="{{ howBadIsTime(build.buildtime) }}">{{ build.buildtime | p
rettifyBuildTime }}</td> | |
77 <td class="{{ howBadIsResult(build.result) }}">{{ build.result | resul
tToString }}</td> | |
78 <td>{{ build.revision }}</td> | |
79 <td class='longest-step'>{{ build.response.steps.max.name }}</td> | |
80 <td class='longest-step-duration {{ howBadIsTime(build.response.steps.
max.duration) }}'> | |
81 {{ build.response.steps.max.duration | prettifyBuildTime }}</td> | |
82 <td class='longest-step-percent'>{{ build.response.steps.max.percent |
prettifyPercent }}</td> | |
83 <core-ajax url='http://build.chromium.org/p/{{ build.master }}/json/bu
ilders/{{ build.builder }}/builds/{{ build.buildnumber }}' | |
84 handleAs='json' auto="{{ auto }}" response="{{ build.response }}" on
-core-response="{{ findLongest }}"></core-ajax> | |
85 </tr> | |
86 </template> | |
87 </table> | |
88 </template> | |
89 <script> | |
90 Polymer('build-table', { | |
91 auto: true, | |
92 trClick: function(event, detail, sender) { | |
93 var master = sender.getAttribute('data-master'); | |
94 var builder = sender.getAttribute('data-builder'); | |
95 var buildnumber = sender.getAttribute('data-buildnumber'); | |
96 var url = 'http://build.chromium.org/p/{1}/builders/{2}/builds/{3}'. | |
97 assign(master, builder, buildnumber); | |
98 window.open(url); | |
99 }, | |
100 resultToString: function(value) { | |
101 return ["success", "warning", "failure", "skipped", "exception", "retry"
][value]; | |
102 }, | |
103 prettifyBuildTime: function(value) { | |
104 var s = Math.round(value); | |
105 var m = 0; | |
106 var h = 0; | |
107 if (s >= 60) { | |
108 m = Math.floor(s / 60); | |
109 s = s % 60; | |
110 if (m >= 60) { | |
111 h = Math.floor(m / 60); | |
112 m = m % 60; | |
113 } | |
114 } | |
115 | |
116 return h + ":" + (m > 9 ? m : '0' + m) + ":" + (s > 9 ? s : '0' + s); | |
117 }, | |
118 prettifyPercent: function(percent) { | |
119 return (percent * 100).toFixed(1) + '%'; | |
120 }, | |
121 howBadIsTime: function(time) { | |
122 if (time > 60 * 60) { | |
123 return 'error'; | |
124 } else if (time > 30 * 60) { | |
125 return 'warning'; | |
126 } else { | |
127 return ''; | |
128 } | |
129 }, | |
130 howBadIsResult: function(result) { | |
131 return ['ok', 'warning', 'error', 'warning', 'error', 'warning'][result]
; | |
132 }, | |
133 _sort: function(sender, extractor) { | |
134 var th = sender; | |
135 var sortDir = 'down'; | |
136 if (th.getAttribute('data-sortdown')) { | |
137 sortDir = 'up'; | |
138 } | |
139 var headers = this.shadowRoot.querySelectorAll('th'); | |
140 for (var i = 0; i < headers.length; i++) { | |
141 headers[i].removeAttribute('data-sortdown'); | |
142 headers[i].removeAttribute('data-sortup'); | |
143 } | |
144 th.setAttribute('data-sort' + sortDir, true); | |
145 this.builds.sort(function(a, b) { | |
146 var aVal = extractor(a), bVal = extractor(b); | |
147 if (!isNaN(Number(aVal)) && !isNaN(Number(bVal))) { | |
148 if (sortDir == 'down') { | |
149 return aVal - bVal; | |
150 } else { | |
151 return bVal - aVal; | |
152 } | |
153 } else { | |
154 if (sortDir == 'down') { | |
155 return aVal.localeCompare(bVal); | |
156 } else { | |
157 return bVal.localeCompare(aVal); | |
158 } | |
159 } | |
160 }); | |
161 }, | |
162 updateSort: function(event, detail, sender) { | |
163 var type = sender.getAttribute('data-type'); | |
164 this._sort(sender, function(a) { return a[type] }); | |
165 }, | |
166 updateSortForResponse: function(event, detail, sender) { | |
167 var type = sender.getAttribute('data-type'); | |
168 this._sort(sender, function(a) { return a.response.steps.max[type] }); | |
169 }, | |
170 findLongest: function(event, data) { | |
171 var maxDuration = 0; | |
172 var maxDurationName = ''; | |
173 var buildDuration = 0; | |
174 var steps = data.response.steps; | |
175 if (steps == null) | |
176 steps.max= {name: 'waiting', duration: 0, percent: 0}; | |
177 steps.forEach(function(step) { | |
178 var name = step.name | |
179 if (name == 'steps') { | |
180 return; | |
181 } | |
182 var duration = step.times[1] - step.times[0]; | |
183 if (duration > maxDuration) { | |
184 maxDuration = duration; | |
185 maxDurationName = name; | |
186 } | |
187 buildDuration += duration; | |
188 }); | |
189 steps.max = {name: maxDurationName, duration: maxDuration, percent: maxD
uration / buildDuration}; | |
190 } | |
191 }); | |
192 </script> | |
193 </polymer-element> | |
OLD | NEW |