Merge lp:~cjohnston/qa-dashboard/1209161 into lp:qa-dashboard

Proposed by Chris Johnston
Status: Merged
Approved by: Chris Johnston
Approved revision: 582
Merged at revision: 583
Proposed branch: lp:~cjohnston/qa-dashboard/1209161
Merge into: lp:qa-dashboard
Diff against target: 340 lines (+205/-43)
5 files modified
bootspeed/templates/bootspeed/kpi_simple_average.html (+1/-1)
common/static/css/style.css (+10/-0)
eventstat/templates/eventstat/kpi_simple_average.html (+1/-1)
smokeng/dashboard.py (+161/-41)
smokeng/templates/smokeng/kpi_variant_pass_rate.html (+32/-0)
To merge this branch: bzr merge lp:~cjohnston/qa-dashboard/1209161
Reviewer Review Type Date Requested Status
Andy Doan (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+181606@code.launchpad.net

Commit message

Shows smoke kpi per variant per release

Description of the change

Due to the potential massive differences between the different variants, we should display the kpi per variant per release

Also floats kpi 'help' to the right side of the kpi's

To post a comment you must log in.
lp:~cjohnston/qa-dashboard/1209161 updated
582. By Chris Johnston

Add smoke kpi by variant

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:582
http://s-jenkins:8080/job/dashboard-ci/162/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins:8080/job/dashboard-ci/162/rebuild

review: Approve (continuous-integration)
Revision history for this message
Andy Doan (doanac) wrote :

i didn't notice how/why this is needed:

32 +.r {
33 + position: relative!important;
34 + margin: 0!important;
35 + background: transparent!important;
36 + padding: 0!important;
37 +}

Revision history for this message
Andy Doan (doanac) wrote :

dashboard.py is started to get a bit out of control. But I suppose carrying around that baggage might be handy some day

Revision history for this message
Chris Johnston (cjohnston) wrote :

Andy, the css change was required to be able to do the coloring.

Revision history for this message
Andy Doan (doanac) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bootspeed/templates/bootspeed/kpi_simple_average.html'
2--- bootspeed/templates/bootspeed/kpi_simple_average.html 2013-07-03 15:12:46 +0000
3+++ bootspeed/templates/bootspeed/kpi_simple_average.html 2013-08-22 16:13:38 +0000
4@@ -3,7 +3,7 @@
5 <strong>Build:</strong> {{ build_number }}
6 {% endif %}
7 {% if help_info %}
8- <a class="css-tooltip no-border" href="#"><div class="help">?</div><span class="classic">
9+ <a class="css-tooltip no-border" href="#"><div class="help kpihelp">?</div><span class="classic">
10 {{ help_info }}
11 </span></a>
12 {% endif %}
13
14=== modified file 'common/static/css/style.css'
15--- common/static/css/style.css 2013-08-19 15:25:18 +0000
16+++ common/static/css/style.css 2013-08-22 16:13:38 +0000
17@@ -764,6 +764,10 @@
18 width: 25px;
19 }
20
21+div.kpihelp {
22+ float: right;
23+}
24+
25 .css-tooltip {
26 border-bottom: 1px dotted #000000;
27 color: #000000; outline: none;
28@@ -873,3 +877,9 @@
29 padding-bottom: 10px!important;
30 }
31
32+.r {
33+ position: relative!important;
34+ margin: 0!important;
35+ background: transparent!important;
36+ padding: 0!important;
37+}
38
39=== modified file 'eventstat/templates/eventstat/kpi_simple_average.html'
40--- eventstat/templates/eventstat/kpi_simple_average.html 2013-07-03 15:12:46 +0000
41+++ eventstat/templates/eventstat/kpi_simple_average.html 2013-08-22 16:13:38 +0000
42@@ -3,7 +3,7 @@
43 <strong>Build:</strong> {{ build_number }}
44 {% endif %}
45 {% if help_info %}
46- <a class="css-tooltip no-border" href="#"><div class="help">?</div><span class="classic">
47+ <a class="css-tooltip no-border" href="#"><div class="help kpihelp">?</div><span class="classic">
48 {{ help_info }}
49 </span></a>
50 {% endif %}
51
52=== modified file 'smokeng/dashboard.py'
53--- smokeng/dashboard.py 2013-08-14 02:44:00 +0000
54+++ smokeng/dashboard.py 2013-08-22 16:13:38 +0000
55@@ -51,10 +51,11 @@
56 def importance(self):
57 return self.IMPORTANCE_HIGH
58
59- def _get_result_bug_count(self, build_number):
60+ def _get_result_bug_count(self, build_number, variant=None):
61 qs = SmokeResultBug.objects.filter(
62 publish=True,
63 result__image__build_number=build_number,
64+ result__image__variant=variant,
65 ).values(
66 'result__image__release',
67 'bug__bug_no',
68@@ -79,8 +80,11 @@
69 def _get_result_data(self):
70 qs = SmokeResult.objects.filter(
71 publish=True,
72+ ).exclude(
73+ image__build_number__contains='?',
74 ).values(
75 'image__arch',
76+ 'image__variant',
77 'image__release',
78 'image__build_number',
79 ).annotate(
80@@ -98,6 +102,9 @@
81 entry['image__release'],
82 {},
83 ).setdefault(
84+ entry['image__variant'],
85+ {},
86+ ).setdefault(
87 entry['image__arch'],
88 {},
89 ).setdefault(
90@@ -115,45 +122,51 @@
91 TREND_ENTRIES = 7
92
93 results = {}
94- for release, arches in entry_tree.items():
95- #print("release: {}, arches: {}".format(release, arches))
96- for arch_name, arch in arches.items():
97- res_arch = results.setdefault(
98- release,
99- {},
100- ).setdefault(
101- arch_name,
102- {},
103- )
104- keys = arch.keys()
105- item_count = min(len(keys), TREND_ENTRIES)
106- for build in sorted(keys, reverse=True)[:item_count]:
107- res_arch.setdefault(
108- 'latest_build_number',
109- build
110- )
111-
112- arch_build = arch[build]
113- arch_build['pass_rate'] = 0
114- if len(arch_build['pass_rates']) > 0:
115- pr_sum = sum(arch_build['pass_rates'])
116- pr_len = len(arch_build['pass_rates'])
117- arch_build['pass_rate'] = pr_sum / pr_len
118-
119- res_arch.setdefault('pass_rate', arch_build['pass_rate'])
120-
121- if build > res_arch['latest_build_number']:
122- res_arch['latest_build_number'] = build
123- res_arch['pass_rate'] = arch_build['pass_rate']
124-
125- res_arch.setdefault('data', []).append(
126- dict(
127- release=release,
128- arch=arch_name,
129- build_number=build,
130- pass_rate=arch_build['pass_rate'],
131- )
132- )
133+ for release, variants in entry_tree.items():
134+ for variant, arches in variants.items():
135+ for arch_name, arch in arches.items():
136+ res_arch = results.setdefault(
137+ release,
138+ {},
139+ ).setdefault(
140+ variant,
141+ {},
142+ ).setdefault(
143+ arch_name,
144+ {},
145+ )
146+ keys = arch.keys()
147+
148+ item_count = min(len(keys), TREND_ENTRIES)
149+ for build in sorted(keys, reverse=True)[:item_count]:
150+ res_arch.setdefault(
151+ 'latest_build_number',
152+ build
153+ )
154+ arch_build = arch[build]
155+ arch_build['pass_rate'] = 0
156+ if len(arch_build['pass_rates']) > 0:
157+ pr_sum = sum(arch_build['pass_rates'])
158+ pr_len = len(arch_build['pass_rates'])
159+ arch_build['pass_rate'] = pr_sum / pr_len
160+
161+ res_arch.setdefault(
162+ 'pass_rate',
163+ arch_build['pass_rate'],
164+ )
165+
166+ if build > res_arch['latest_build_number']:
167+ res_arch['latest_build_number'] = build
168+ res_arch['pass_rate'] = arch_build['pass_rate']
169+
170+ res_arch.setdefault('data', []).append(
171+ dict(
172+ release=release,
173+ arch=arch_name,
174+ build_number=build,
175+ pass_rate=arch_build['pass_rate'],
176+ )
177+ )
178
179 return results
180
181@@ -326,6 +339,113 @@
182 return retval
183
184
185+class SmokengVariantPassRateKPI(SmokengPassRateKPI):
186+ """
187+ A simple KPI entry with an average pass-rate for each variant in each
188+ release.
189+
190+ This KPI determines the latest build number for each variant and shows
191+ an average of those results for each release.
192+
193+ To identify missing images a list of architectures used to calculate
194+ the pass-rate for each release are listed.
195+
196+ A count of bugs filed by the QA Team is also included.
197+
198+ """
199+
200+ def _get_latest_build_number(self, results, release, variant):
201+
202+ build_numbers = []
203+
204+ if release in results and variant in results[release]:
205+ for arch, a_data in results[release][variant].items():
206+ build_numbers.append(a_data['latest_build_number'])
207+
208+ if len(build_numbers) == 0:
209+ return ""
210+
211+ # TODO: fix so it's sorted by float(build_number)
212+ return sorted(build_numbers, reverse=True)[0]
213+
214+ def render(self):
215+
216+ results = self._calculate_pass_rates()
217+
218+ skipped_releases = ['quantal', 'raring']
219+ entries = []
220+ for release in sorted(results.keys(), reverse=True):
221+ if release in skipped_releases:
222+ continue
223+ variants = results[release]
224+ variant_entries = []
225+ for variant in sorted(variants.keys(), reverse=True):
226+ found_arches = []
227+ arches = results[release][variant]
228+ expected_build = self._get_latest_build_number(
229+ results,
230+ release,
231+ variant,
232+ )
233+
234+ bug_counts = self._get_result_bug_count(
235+ expected_build,
236+ variant,
237+ )
238+
239+ arch_passrates = []
240+ has_expected_build = False
241+ for arch in sorted(arches.keys()):
242+ data = arches[arch]
243+ build_no = data['latest_build_number']
244+ if build_no == expected_build:
245+ arch_passrates.append(data['pass_rate'])
246+ found_arches.append(arch)
247+ has_expected_build = True
248+
249+ pass_rate = 0.0
250+
251+ if len(arch_passrates) > 0:
252+ pass_rate = sum(arch_passrates) / len(arch_passrates)
253+
254+ bug_count = bug_counts.get(release, 0)
255+
256+ if has_expected_build:
257+ build_no = expected_build
258+ else:
259+ build_no = ''
260+
261+ variant_kpi = dict(
262+ variant=variant,
263+ results=dict(
264+ build_no=build_no,
265+ pass_rate=pass_rate,
266+ found_arches=found_arches,
267+ bug_count=bug_count,
268+ ),
269+ )
270+ variant_entries.append(variant_kpi)
271+ release_kpi = dict(
272+ release=release,
273+ kpi=variant_entries,
274+ )
275+ entries.append(release_kpi)
276+
277+ retval = render_to_string(
278+ 'smokeng/kpi_variant_pass_rate.html',
279+ dict(
280+ entries=entries,
281+ help_info="""The average pass_rate for each variant is
282+ calculated by accounting for each architecture (i.e. "amd64",
283+ "i386", "armhf") in the variant. These averages are averaged
284+ for each release being tracked. Hover over the pass rate
285+ for each release for the number of bugs reported.""",
286+ ),
287+ )
288+
289+ return retval
290+
291+
292 class SmokengExtension(Extension):
293 URL_PATTERNS = patterns(
294 '',
295@@ -343,7 +463,7 @@
296
297 def contribute_to_kpis(self, kpi_mgr):
298 return [
299- SmokengPassRateV3KPI,
300+ SmokengVariantPassRateKPI,
301 ]
302
303 def pull_data(self):
304
305=== added file 'smokeng/templates/smokeng/kpi_variant_pass_rate.html'
306--- smokeng/templates/smokeng/kpi_variant_pass_rate.html 1970-01-01 00:00:00 +0000
307+++ smokeng/templates/smokeng/kpi_variant_pass_rate.html 2013-08-22 16:13:38 +0000
308@@ -0,0 +1,32 @@
309+{% load dashboard_extras %}
310+ {% if help_info %}
311+ <a class="css-tooltip no-border" href="#"><div class="help kpihelp">?</div><span class="classic">
312+ {{ help_info }}
313+ </span></a>
314+ {% endif %}
315+ {% for release in entries %}
316+ <strong>{{ release.release|capfirst }}</strong>
317+ <table class="kpi">
318+ {% for kpi in release.kpi %}
319+ <tr>
320+ <td><a href="{% url smokeng_overview release.release kpi.variant %}">{{ kpi.variant }}</a></td>
321+ <td>
322+ {{ kpi.results.build_no }}
323+ </td>
324+ <td class="num pad-seven {{ kpi.results.pass_rate|pass_rate_color }}">
325+ <a class="css-tooltip no-border" href="#">
326+ <div class="r" style="color: {% if kpi.results.pass_rate|pass_rate_color == "yellow" %}#000;{% else %}#fff;{% endif %}">
327+ {{ kpi.results.pass_rate|decimal_to_percent }}</div>
328+ <span class="classic">
329+ {{ kpi.results.bug_count }} Bug{% if kpi.results.bug_count > 1 %}s{% endif %}
330+ </span>
331+ </a>
332+ </td>
333+ <td class="pad-seven">
334+ {% if kpi.results.found_arches %}{% for arch in kpi.results.found_arches %}{{ arch|product_name }}{% if not forloop.last %}, {% endif %}{% endfor %}
335+ {% endif %}
336+ </td>
337+ </tr>
338+{% endfor %}
339+</table>
340+{% endfor %}

Subscribers

People subscribed via source and target branches