Merge lp:~allanlesage/qa-coverage-dashboard/initial-mega-merge into lp:qa-coverage-dashboard

Proposed by Allan LeSage
Status: Merged
Merged at revision: 758
Proposed branch: lp:~allanlesage/qa-coverage-dashboard/initial-mega-merge
Merge into: lp:qa-coverage-dashboard
Diff against target: 1233 lines (+362/-419)
24 files modified
common/static/css/style.css (+5/-0)
common/templates/layout.html (+5/-21)
gaps/api.py (+87/-46)
gaps/dashboard.py (+2/-2)
gaps/management/commands/c2dconfigutils/cu2dOutputCi.py (+3/-0)
gaps/models.py (+43/-7)
gaps/templates/branch_list.html (+0/-2)
gaps/templates/build.html (+1/-1)
gaps/templates/gaps_layout.html (+0/-4)
gaps/templates/integration_list.html (+1/-1)
gaps/templates/job.html (+1/-1)
gaps/templates/job_list.html (+1/-1)
gaps/templates/main.html (+0/-2)
gaps/templates/project_detail.html (+49/-38)
gaps/templates/project_list.html (+8/-10)
gaps/templates/stack_detail.html (+43/-37)
gaps/templates/stack_list.html (+49/-16)
gaps/templatetags/gaps_extras.py (+7/-8)
gaps/urls.py (+3/-8)
gaps/urls_api.py (+9/-82)
gaps/util/add.py (+2/-0)
gaps/views.py (+33/-128)
qa_dashboard/settings.py (+5/-3)
qa_dashboard/urls.py (+5/-1)
To merge this branch: bzr merge lp:~allanlesage/qa-coverage-dashboard/initial-mega-merge
Reviewer Review Type Date Requested Status
Chris Gagnon Pending
Review via email: mp+210458@code.launchpad.net

Description of the change

Initial mega merge of what we're demo-ing as the QA Coverage Dashboard, we will now have a normal MP-review process :) .

To post a comment you must log in.
Revision history for this message
Chris Gagnon (chris.gagnon) wrote :

move this out of the html, it's using a lot of the same code

467 +<script type="text/javascript">
468 + d3.json('{% url "gaps_api_project" project.name %}', function(data) {
469 + nv.addGraph(function() {
470 + var chart = nv.models.lineChart()
471 + .width(1100).height(400);
472 + chart.forceY([0, 1]);
473 + chart.xAxis
474 + .axisLabel('Date')
475 + .tickFormat(function(d){return d3.time.format('%Y%m%d')(new Date(d * 1000));})
476 +
477 + chart.yAxis
478 + .axisLabel('%')
479 + .tickFormat(d3.format('%'))
480 + d3.select('#line-chart svg').datum(data).transition().duration(500).call(chart);
481 +
482 + nv.utils.windowResize(chart.update);
483 +
484 + return chart;
485 + });
486 + });
487 +</script>

640 +<script type="text/javascript">
641 + d3.json('{% url 'gaps_api_stack' stack.name %}', function(data) {
642 + nv.addGraph(function() {
643 + var chart = nv.models.lineChart()
644 + .width(900).height(400);
645 + chart.forceY([0, 1]);
646 + chart.xAxis
647 + .axisLabel('Date')
648 + .tickFormat(function(d){return d3.time.format('%Y%m%d')(new Date(d * 1000));})
649 + chart.yAxis
650 + .axisLabel('%')
651 + .tickFormat(d3.format('%'))
652 + d3.select('#line-chart svg').datum(data).transition().duration(500).call(chart);
653 + nv.utils.windowResize(chart.update);
654 + return chart;
655 + });
656 + });
657 +</script>

758 +<script type="text/javascript">
759 + d3.json('{% url 'gaps_api_stack_list' %}', function(data) {
760 + nv.addGraph(function() {
761 + var chart = nv.models.lineChart()
762 + .width(900).height(400);
763 + chart.forceY([0, 1]);
764 + chart.xAxis
765 + .axisLabel('Date')
766 + .tickFormat(function(d){return d3.time.format('%Y%m%d')(new Date(d * 1000));})
767 + chart.yAxis
768 + .axisLabel('%')
769 + .tickFormat(d3.format('%'))
770 + d3.select('#line-chart svg').datum(data).transition().duration(500).call(chart);
771 + nv.utils.windowResize(chart.update);
772 + return chart;
773 + });
774 + });
775 +</script>

Can we move the properties in the model to the api?

Revision history for this message
Allan LeSage (allanlesage) wrote :

Agreed with Chris to push this to trunk and file bugs for his two requests: bug 1302788 and bug 1302786 .

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'common/static/css/style.css'
2--- common/static/css/style.css 2013-12-02 13:35:26 +0000
3+++ common/static/css/style.css 2014-03-11 17:00:08 +0000
4@@ -12,6 +12,11 @@
5 color: white !important;
6 }
7
8+.black {
9+ background-color: #000000;
10+ color: white !important;
11+}
12+
13 .error {
14 background-color: #ffabab;
15 }
16
17=== modified file 'common/templates/layout.html'
18--- common/templates/layout.html 2014-03-05 23:18:33 +0000
19+++ common/templates/layout.html 2014-03-11 17:00:08 +0000
20@@ -11,7 +11,7 @@
21 <link href='{% static "css/jquery.dataTables.css" %}' rel='stylesheet' type='text/css' />
22 <link href='{% static "css/style.css" %}' rel='stylesheet' type='text/css' />
23 <link href='{% static "css/new-style.css" %}' rel='stylesheet' type='text/css' />
24- <title>{% block page_name %}{% endblock %} | Ubuntu CI Dashboard</title>
25+ <title>{% block page_name %}{% endblock %}Ubuntu QA Test Coverage Dashboard</title>
26 <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' type="text/javascript"></script>
27 <script src='{% static "js/jquery.dataTables.min.js" %}' type="text/javascript"></script>
28 {% block extra_headers %}
29@@ -19,32 +19,16 @@
30 {% analytics %}
31 </head>
32 <body>
33- <div class="header-login">
34- <div class="header-login-container">
35- <ul>
36- {% if user.is_authenticated %}
37- <li>Logged in as: <strong><a href="{% url edit_profile %}">{{ user.username }}</a></strong></li>
38- {% if user.is_staff %}
39- <li><a href="/admin" title="Admin">Admin</a></li>
40- {% endif %}
41- <li><a href="{% url 'logout' %}" title="Log Out: {{ user.username }}">Log out</a></li>
42- {% else %}
43- <li><a href="/openid/login/?next={{ login_next }}" title="Log In">Log in</a></li>
44- {% endif %}
45- </ul>
46- </div>
47- </div>
48- <header class="banner global" role="banner">
49- <nav role="navigation" class="nav-primary nav-right">
50+ <header class="banner global" role="banner">
51+ <nav role="navigation" class="nav-primary nav-right">
52 <div class="logo">
53 <a class="logo-ubuntu" href='{% url 'index' %}'>
54 <img width="118" height="27" src="//assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-orange.png" alt="Ubuntu" />
55- <span>ci</span>
56+ <span></span>
57 </a>
58 </div>
59 <ul>
60 {% plugin_links url %}
61- <li><a href="https://jenkins.qa.ubuntu.com/view/Trusty/view/AutoPkgTest/">Proposed</a></li>
62 </ul>
63 </nav>
64 </header>
65@@ -82,7 +66,7 @@
66 <p>&copy; 2012 - {% now "Y" %} Canonical Ltd. Ubuntu and Canonical are registered trademarks of Canonical Ltd.
67 <a href="https://bugs.launchpad.net/qa-dashboard/+filebug" target="_blank">Report a bug on this site</a></p>
68 <ul class="inline clear">
69- <li class='info'>CI Dashboard</li>
70+ <li class='info'>QA Coverage Dashboard</li>
71 <li class='info'>version: {{ dash_version }}</li>
72 <li class='info'>revno: {{ dash_revno }}</li>
73 </ul>
74
75=== modified file 'gaps/api.py'
76--- gaps/api.py 2014-02-20 19:19:28 +0000
77+++ gaps/api.py 2014-03-11 17:00:08 +0000
78@@ -1,5 +1,5 @@
79-# QA Dashboard
80-# Copyright 2012-2013 Canonical Ltd.
81+# QA Coverage Dashboard
82+# Copyright 2012-2014 Canonical Ltd.
83
84 # This program is free software: you can redistribute it and/or modify it
85 # under the terms of the GNU Affero General Public License version 3, as
86@@ -13,59 +13,100 @@
87 # You should have received a copy of the GNU Affero General Public License
88 # along with this program. If not, see <http://www.gnu.org/licenses/>.
89
90+import json
91 import logging
92-import json
93+
94 from django.shortcuts import get_object_or_404
95 from django.utils.dateformat import format
96
97 from common.utils import JSONResponse
98-from gaps.models import GapsProject, JenkinsBuild
99+from gaps.models import (CoverageBuild,
100+ CoverageData,
101+ CoverageProject,
102+ CoverageStack,
103+ JenkinsBuild,
104+ StackCoverageObservation)
105
106 logger = logging.getLogger('qa_dashboard')
107
108
109 def project(request, name):
110 project = get_object_or_404(
111- GapsProject,
112- launchpad_project__name=name,
113- release__name='head',
114- )
115-
116- coverage_builds = JenkinsBuild.objects.filter(
117- jenkins_job=project.autolanding_primary_job,
118- coverage_report__isnull=False,
119- ).order_by('timestamp')
120-
121- data = []
122- line_data = {
123- 'key': 'Line coverage',
124- 'color': '#DD4814'
125- }
126- branch_data = {
127- 'key': 'Branch coverage',
128- 'color': '#772953'
129- }
130-
131- line_values = []
132- branch_values = []
133-
134- for build in coverage_builds:
135- ld = dict(
136- y=build.line_coverage,
137- x=format(build.timestamp, 'U'),
138- )
139- bd = dict(
140- y=build.branch_coverage,
141- x=format(build.timestamp, 'U'),
142- )
143- line_values.append(ld)
144- branch_values.append(bd)
145- line_data['values'] = line_values
146- branch_data['values'] = branch_values
147-
148- data.append(line_data)
149- data.append(branch_data)
150-
151- data = json.dumps(data)
152-
153+ CoverageProject,
154+ name=name)
155+ coverage_datas = CoverageData.objects.filter(
156+ coverage_build__project=project).order_by(
157+ '-coverage_build__ran_at')
158+ data = []
159+ line_coverage_data = {
160+ 'key': 'Line coverage',
161+ 'color': '#DD4814'
162+ }
163+ branch_coverage_data = {
164+ 'key': 'Branch coverage',
165+ 'color': '#772953'
166+ }
167+ line_coverage_values = []
168+ branch_coverage_values = []
169+ for coverage_data in coverage_datas:
170+ line_coverage_values.append(
171+ {'y': float(coverage_data.line_coverage),
172+ 'x': format(coverage_data.coverage_build.ran_at, 'U'),})
173+ branch_coverage_values.append(
174+ {'y': float(coverage_data.branch_coverage),
175+ 'x': format(coverage_data.coverage_build.ran_at, 'U'),})
176+ line_coverage_data['values'] = line_coverage_values
177+ branch_coverage_data['values'] = branch_coverage_values
178+ data.append(line_coverage_data)
179+ data.append(branch_coverage_data)
180+ data = json.dumps(data)
181+ return JSONResponse(data)
182+
183+def stack(request, name):
184+ stack = get_object_or_404(
185+ CoverageStack,
186+ name=name)
187+ line_coverage_data = {
188+ 'key': 'Line coverage',
189+ 'color': '#DD4814'
190+ }
191+ branch_coverage_data = {
192+ 'key': 'Branch coverage',
193+ 'color': '#772953'
194+ }
195+ observations = StackCoverageObservation.objects.filter(
196+ stack=stack).order_by('-timestamp')
197+ line_coverage_values = []
198+ branch_coverage_values = []
199+ # TODO: more Pythonic pls
200+ for observation in observations:
201+ line_coverage_values.append(
202+ {'y': float(observation.line_coverage),
203+ 'x': format(observation.timestamp, 'U'),})
204+ branch_coverage_values.append(
205+ {'y': float(observation.branch_coverage),
206+ 'x': format(observation.timestamp, 'U'),})
207+ line_coverage_data['values'] = line_coverage_values
208+ branch_coverage_data['values'] = branch_coverage_values
209+ data = []
210+ data.append(line_coverage_data)
211+ data.append(branch_coverage_data)
212+ data = json.dumps(data)
213+ return JSONResponse(data)
214+
215+def stack_list(request):
216+ stacks = CoverageStack.objects.all()
217+ data = []
218+ for stack in stacks:
219+ line_coverage_data = {'key': stack.name}
220+ observations = StackCoverageObservation.objects.filter(
221+ stack=stack).order_by('-timestamp')
222+ line_coverage_values = []
223+ for observation in observations:
224+ line_coverage_values.append(
225+ {'y': float(observation.line_coverage),
226+ 'x': format(observation.timestamp, 'U'),})
227+ line_coverage_data['values'] = line_coverage_values
228+ data.append(line_coverage_data)
229+ data = json.dumps(data)
230 return JSONResponse(data)
231
232=== modified file 'gaps/dashboard.py'
233--- gaps/dashboard.py 2014-02-20 20:06:08 +0000
234+++ gaps/dashboard.py 2014-03-11 17:00:08 +0000
235@@ -25,14 +25,14 @@
236 URL_PATTERNS = patterns(
237 '',
238 url(r'^gaps/', include('gaps.urls')),
239- #url(r'^api/gaps/idle/', include('gaps.urls_api')),
240+ url(r'^api/gaps/', include('gaps.urls_api')),
241 )
242
243 LINKS = [
244 {
245 'name': 'gaps',
246 'url': reverse_lazy('gaps_overview'),
247- 'label': 'Gaps',
248+ 'label': 'Coverage',
249 'index': 100,
250 },
251 ]
252
253=== modified file 'gaps/management/commands/c2dconfigutils/cu2dOutputCi.py'
254--- gaps/management/commands/c2dconfigutils/cu2dOutputCi.py 2014-03-05 15:53:44 +0000
255+++ gaps/management/commands/c2dconfigutils/cu2dOutputCi.py 2014-03-11 17:00:08 +0000
256@@ -126,6 +126,9 @@
257 stacks = os.listdir('stacks/{}'.format(pocket))
258 for stack in stacks:
259 stack_name = str(stack).split('.')[0]
260+ if stack_name not in ['qa']:
261+ print "skipping stack {}".format(stack_name)
262+ continue
263 f_path = os.path.join(
264 local_path, '../stacks/{}/{}'.format(pocket, stack))
265 stack_list.append({'file': f_path,
266
267=== modified file 'gaps/models.py'
268--- gaps/models.py 2014-03-06 22:13:30 +0000
269+++ gaps/models.py 2014-03-11 17:00:08 +0000
270@@ -14,6 +14,43 @@
271 """
272 name = models.CharField(max_length=64)
273
274+ @property
275+ def last_coverage_observation(self):
276+ try:
277+ return StackCoverageObservation.objects.filter(
278+ stack=self).order_by('-timestamp')[0]
279+ except IndexError:
280+ return None
281+
282+ def refresh_coverage_observations(self):
283+ StackCoverageObservation.objects.filter(stack=self).delete()
284+ # for each coveragedata relevant to this stack, make a new observation (and compute coverages)
285+ coverage_projects = CoverageProject.objects.filter(stack=self)
286+ coverage_datas = CoverageData.objects.filter(
287+ coverage_build__project__in=coverage_projects)
288+ for coverage_data in coverage_datas:
289+ observation = StackCoverageObservation(
290+ stack=self,
291+ timestamp=coverage_data.coverage_build.ran_at)
292+ observation.compute_coverage()
293+
294+ @property
295+ def percent_reporting(self):
296+ coverage_projects = CoverageProject.objects.filter(stack=self)
297+ coverage_data_exists_count = 0
298+ for coverage_project in coverage_projects:
299+ if coverage_project.last_coverage_data is not None:
300+ coverage_data_exists_count = coverage_data_exists_count + 1
301+ # TODO: this is arguably better but sqlite3 doesn't support distinct
302+ #coverage_datas = CoverageData.objects.filter(
303+ # coverage_build__project__in=coverage_projects).distinct(
304+ # 'coverage_build__project')
305+ try:
306+ return float(coverage_data_exists_count) / float(len(coverage_projects))
307+ except ZeroDivisionError:
308+ # I suppose this is possible?
309+ return 0.0
310+
311 def __unicode__(self):
312 return self.name
313
314@@ -24,22 +61,21 @@
315 stack = models.ManyToManyField(CoverageStack)
316
317 @property
318- def line_coverage(self):
319- coverage_builds = CoverageBuild.objects.filter(
320- project=self)
321+ def last_build(self):
322 try:
323- return CoverageData.objects.filter(
324- coverage_build__in=coverage_builds)[0].line_coverage
325+ return CoverageBuild.objects.filter(
326+ project=self).order_by('-ran_at')[0]
327 except IndexError:
328 return None
329
330 @property
331- def branch_coverage(self):
332+ def last_coverage_data(self):
333 coverage_builds = CoverageBuild.objects.filter(
334 project=self)
335 try:
336 return CoverageData.objects.filter(
337- coverage_build__in=coverage_builds)[0].branch_coverage
338+ coverage_build__in=coverage_builds).order_by(
339+ '-coverage_build__ran_at')[0]
340 except IndexError:
341 return None
342
343
344=== modified file 'gaps/templates/branch_list.html'
345--- gaps/templates/branch_list.html 2014-02-20 19:19:28 +0000
346+++ gaps/templates/branch_list.html 2014-03-11 17:00:08 +0000
347@@ -1,8 +1,6 @@
348 {% extends "layout.html" %}
349 {% load dashboard_extras %}
350
351-{% block page_name %}PS-QA Dashboard{% endblock %}
352-
353 {% block content %}
354 <script type='text/javascript'>
355 //<![CDATA[
356
357=== modified file 'gaps/templates/build.html'
358--- gaps/templates/build.html 2014-02-20 19:19:28 +0000
359+++ gaps/templates/build.html 2014-03-11 17:00:08 +0000
360@@ -1,7 +1,7 @@
361 {% extends "layout.html" %}
362 {% load dashboard_extras %}
363
364-{% block page_name %}PS-QA Dashboard{% endblock %}
365+{% block page_name %}QA Coverage Dashboard{% endblock %}
366
367 {% block content %}
368 {{ build.timestamp }}
369
370=== modified file 'gaps/templates/gaps_layout.html'
371--- gaps/templates/gaps_layout.html 2014-03-05 23:18:33 +0000
372+++ gaps/templates/gaps_layout.html 2014-03-11 17:00:08 +0000
373@@ -1,5 +1,1 @@
374 {% extends "layout.html" %}
375-{% block sub_nav_links %}
376-<li {% ifequal url.1 'stacks' %}class="active"{% endifequal %}><a class="sub-nav-item" href='{% url 'gaps_overview' %}'>Integration</a></li>
377-<li {% ifequal url.1 'project' %}class="active"{% endifequal %}><a class="sub-nav-item" href='{% url 'projects' %}'>Projects</a></li>
378-{% endblock %}
379
380=== modified file 'gaps/templates/integration_list.html'
381--- gaps/templates/integration_list.html 2014-02-20 20:20:57 +0000
382+++ gaps/templates/integration_list.html 2014-03-11 17:00:08 +0000
383@@ -1,7 +1,7 @@
384 {% extends "gaps_layout.html" %}
385 {% load dashboard_extras %}
386
387-{% block page_name %}PS-QA Dashboard{% endblock %}
388+{% block page_name %}QA Coverage Dashboard{% endblock %}
389
390 {% block content %}
391 <script type='text/javascript'>
392
393=== modified file 'gaps/templates/job.html'
394--- gaps/templates/job.html 2014-02-20 19:19:28 +0000
395+++ gaps/templates/job.html 2014-03-11 17:00:08 +0000
396@@ -1,7 +1,7 @@
397 {% extends "layout.html" %}
398 {% load dashboard_extras %}
399
400-{% block page_name %}PS-QA Dashboard{% endblock %}
401+{% block page_name %}QA Coverage Dashboard{% endblock %}
402
403 {% block content %}
404 <script type='text/javascript'>
405
406=== modified file 'gaps/templates/job_list.html'
407--- gaps/templates/job_list.html 2014-02-20 19:19:28 +0000
408+++ gaps/templates/job_list.html 2014-03-11 17:00:08 +0000
409@@ -1,7 +1,7 @@
410 {% extends "layout.html" %}
411 {% load dashboard_extras %}
412
413-{% block page_name %}PS-QA Dashboard{% endblock %}
414+{% block page_name %}QA Coverage Dashboard{% endblock %}
415
416 {% block content %}
417 <script type='text/javascript'>
418
419=== modified file 'gaps/templates/main.html'
420--- gaps/templates/main.html 2014-02-20 19:19:28 +0000
421+++ gaps/templates/main.html 2014-03-11 17:00:08 +0000
422@@ -1,8 +1,6 @@
423 {% extends "layout.html" %}
424 {% load dashboard_extras %}
425
426-{% block page_name %}PS-QA Dashboard{% endblock %}
427-
428 {% block content %}
429 <script type='text/javascript'>
430 //<![CDATA[
431
432=== modified file 'gaps/templates/project_detail.html'
433--- gaps/templates/project_detail.html 2014-02-21 17:55:09 +0000
434+++ gaps/templates/project_detail.html 2014-03-11 17:00:08 +0000
435@@ -1,30 +1,63 @@
436 {% extends "layout.html" %}
437 {% load dashboard_extras %}
438 {% load percentage %}
439-
440-{% block page_name %}PS-QA Dashboard{% endblock %}
441+{% block extra_headers %}
442+<link type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/nvd3/0.9/nv.d3.css" rel="stylesheet" />
443+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.2.2/d3.min.js"></script>
444+<!-- need to switch back to cdnjs when bugs are fixed
445+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/0.9/nv.d3.js"></script>-->
446+<script type="text/javascript" src="//6df403e3d98e2ac67ac2-180150c581869d2c4c18db9c9e3179c4.r40.cf1.rackcdn.com/nv.d3.js"></script>
447+{% endblock extra_headers %}
448
449 {% block content %}
450+<style>
451+ #line-chart svg {
452+ height: 400px;
453+ width: 1100px;
454+ }
455+</style>
456 <script type='text/javascript'>
457 //<![CDATA[
458 $(document).ready(function() {
459 $(".data-table").dataTable({
460 "bPaginate": false,
461- "aaSorting": [[0, 'desc']]
462+ "aaSorting": [[2, 'desc']]
463 });
464 });
465 //]]>
466 </script>
467+<script type="text/javascript">
468+ d3.json('{% url "gaps_api_project" project.name %}', function(data) {
469+ nv.addGraph(function() {
470+ var chart = nv.models.lineChart()
471+ .width(1100).height(400);
472+ chart.forceY([0, 1]);
473+ chart.xAxis
474+ .axisLabel('Date')
475+ .tickFormat(function(d){return d3.time.format('%Y%m%d')(new Date(d * 1000));})
476+
477+ chart.yAxis
478+ .axisLabel('%')
479+ .tickFormat(d3.format('%'))
480+ d3.select('#line-chart svg').datum(data).transition().duration(500).call(chart);
481+
482+ nv.utils.windowResize(chart.update);
483+
484+ return chart;
485+ });
486+ });
487+</script>
488 <div class='grid_15'>
489 <h2>
490 Project: {{ project.name }}
491 </h2>
492 </div>
493-<div class='grid_2'>
494- <ul class='left_nav'>
495- </ul>
496-</div>
497 <div class='grid_13'>
498+ <div class="grid_13">
499+ <div id="line-chart">
500+ <svg></svg>
501+ </div>
502+ </div>
503 {% if project.report_url %}
504 <table>
505 <tr>
506@@ -45,48 +78,26 @@
507 <table class='data-table basic wide'>
508 <thead>
509 <tr>
510- <th>Name</th>
511+ <th>Jenkins Build</th>
512 <th>Line Coverage</th>
513 <th>Branch Coverage</th>
514 </tr>
515 </thead>
516 <tbody>
517- {% for job in job_list %}
518+ {% for coverage_data in coverage_datas %}
519 <tr>
520 <td>
521- <a href='{% url jobs %}{{ job.name }}'>{{ job.name }}</a>
522- </td>
523- <td>
524- {{ job.line_coverage|percentage }}
525- </td>
526- <td>
527- {{ job.branch_coverage|percentage }}
528+ <a href='{{ coverage_data.coverage_build.url }}'>{{ coverage_data.coverage_build.ran_at }}</a>
529+ </td>
530+ <td>
531+ {{ coverage_data.line_coverage|percentage }}
532+ </td>
533+ <td>
534+ {{ coverage_data.branch_coverage|percentage }}
535 </td>
536 </tr>
537 {% endfor %}
538 </tbody>
539 </table>
540- <div class='grid_13'>
541- <div id='historical_coverage_chart' style='width: 800px; height: 320px'></div>
542- </div>
543 </div>
544-<script type="text/javascript" src="http://www.google.com/jsapi"></script>
545-<script type="text/javascript">
546-    google.load('visualization', '1', {packages: ['corechart', 'annotatedtimeline']});
547- function draw_historical_coverage_chart() {
548- var historical_coverage_data = new google.visualization.DataTable({{ json_data|safe }});
549- var historical_coverage_options = {
550- title: 'Test Coverage Trend',
551- displayRangeSelector: false,
552- thickness: 2,
553- max: 1.0,
554- min: 0.0,
555- numberFormats: "##.#%",
556- colors: ["DD4814", "77216F", "5E2750", "2C001E", "AEA79F", "333333", "000000"]
557- };
558- var historical_coverage_chart = new google.visualization.AnnotatedTimeLine(document.getElementById('historical_coverage_chart'));
559- historical_coverage_chart.draw(historical_coverage_data, historical_coverage_options);
560- }
561- google.setOnLoadCallback(draw_historical_coverage_chart);
562-</script>
563 {% endblock %}
564
565=== modified file 'gaps/templates/project_list.html'
566--- gaps/templates/project_list.html 2014-03-05 23:18:33 +0000
567+++ gaps/templates/project_list.html 2014-03-11 17:00:08 +0000
568@@ -2,15 +2,13 @@
569 {% load dashboard_extras %}
570 {% load percentage %}
571
572-{% block page_name %}PS-QA Dashboard{% endblock %}
573-
574 {% block content %}
575 <script type='text/javascript'>
576 //<![CDATA[
577 $(document).ready(function() {
578 $(".data-table").dataTable({
579 "bPaginate": false,
580- "aaSorting": [[0, 'desc']]
581+ "aaSorting": [[2, 'desc']]
582 });
583 });
584 //]]>
585@@ -67,13 +65,13 @@
586 <a href='{% url 'projects' %}{{ project.name }}'>{{ project.name }}</a>
587 </td>
588 <td>
589- {{ project.name }}
590- </td>
591- <td>
592- {{ project.line_coverage|percentage }}
593- </td>
594- <td>
595- {{ project.branch_coverage|percentage }}
596+ {{ project.last_build.ran_at }}
597+ </td>
598+ <td>
599+ {{ project.last_coverage_data.line_coverage|percentage }}
600+ </td>
601+ <td>
602+ {{ project.last_coverage_data.branch_coverage|percentage }}
603 </td>
604 </tr>
605 {% endfor %}
606
607=== modified file 'gaps/templates/stack_detail.html'
608--- gaps/templates/stack_detail.html 2014-03-06 22:13:30 +0000
609+++ gaps/templates/stack_detail.html 2014-03-11 17:00:08 +0000
610@@ -1,11 +1,22 @@
611 {% extends "gaps_layout.html" %}
612 {% load dashboard_extras %}
613-{% load ci_extras %}
614+{% load gaps_extras %}
615 {% load percentage %}
616-
617-{% block page_name %}PS-QA Dashboard{% endblock %}
618+{% block extra_headers %}
619+<link type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/nvd3/0.9/nv.d3.css" rel="stylesheet" />
620+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.2.2/d3.min.js"></script>
621+<!-- need to switch back to cdnjs when bugs are fixed
622+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/0.9/nv.d3.js"></script>-->
623+<script type="text/javascript" src="//6df403e3d98e2ac67ac2-180150c581869d2c4c18db9c9e3179c4.r40.cf1.rackcdn.com/nv.d3.js"></script>
624+{% endblock extra_headers %}
625
626 {% block content %}
627+<style>
628+ #line-chart svg {
629+ height: 400px;
630+ width: 1100px;
631+ }
632+</style>
633 <script type='text/javascript'>
634 //<![CDATA[
635 $(document).ready(function() {
636@@ -16,6 +27,24 @@
637 });
638 //]]>
639 </script>
640+<script type="text/javascript">
641+ d3.json('{% url 'gaps_api_stack' stack.name %}', function(data) {
642+ nv.addGraph(function() {
643+ var chart = nv.models.lineChart()
644+ .width(900).height(400);
645+ chart.forceY([0, 1]);
646+ chart.xAxis
647+ .axisLabel('Date')
648+ .tickFormat(function(d){return d3.time.format('%Y%m%d')(new Date(d * 1000));})
649+ chart.yAxis
650+ .axisLabel('%')
651+ .tickFormat(d3.format('%'))
652+ d3.select('#line-chart svg').datum(data).transition().duration(500).call(chart);
653+ nv.utils.windowResize(chart.update);
654+ return chart;
655+ });
656+ });
657+</script>
658 <div class='grid_15'>
659 <h2>
660 Stack: {{ stack.name }}
661@@ -33,7 +62,10 @@
662 {% endfor %}
663 </ul>
664 </div>
665-<div class='grid_13'>
666+ <div class="grid_13">
667+ <div id="line-chart">
668+ <svg></svg>
669+ </div>
670 <table class='data-table basic wide'>
671 <thead>
672 <tr>
673@@ -50,43 +82,17 @@
674 <a href='{% url 'projects' %}{{ project.name }}/'>{{ project.name }}</a>
675 </td>
676 <td>
677- {{ project.timestamp }}
678- </td>
679- <td>
680- {{ project.line_coverage|percentage }}
681- </td>
682- <td>
683- {{ project.branch_coverage|percentage }}
684+ {{ project.last_build.ran_at }}
685+ </td>
686+ <td>
687+ {{ project.last_coverage_data.line_coverage|percentage }}
688+ </td>
689+ <td>
690+ {{ project.last_coverage_data.branch_coverage|percentage }}
691 </td>
692 </tr>
693 {% endfor %}
694 </tbody>
695 </table>
696- <div class='grid_13'>
697- {% if project_list %}
698- <div id='historical_coverage_chart' style='width: 800px; height: 320px'></div>
699- {% endif %}
700- </div>
701 </div>
702-<script type="text/javascript" src="http://www.google.com/jsapi"></script>
703-<script type="text/javascript">
704-    google.load('visualization', '1', {packages: ['corechart', 'annotatedtimeline']});
705- function draw_historical_coverage_chart() {
706- var historical_coverage_data = new google.visualization.DataTable({{ json_data|safe }});
707- var historical_coverage_options = {
708- title: 'Test Coverage Trend',
709- displayRangeSelector: false,
710- thickness: 2,
711- max: 1.0,
712- min: 0.0,
713- numberFormats: "##.#%",
714- colors: ["DD4814", "77216F", "5E2750", "2C001E", "AEA79F", "333333", "000000"]
715- };
716- var historical_coverage_chart = new google.visualization.AnnotatedTimeLine(document.getElementById('historical_coverage_chart'));
717- historical_coverage_chart.draw(historical_coverage_data, historical_coverage_options);
718- }
719-
720- google.setOnLoadCallback(draw_historical_coverage_chart);
721-
722- </script>
723 {% endblock %}
724
725=== modified file 'gaps/templates/stack_list.html'
726--- gaps/templates/stack_list.html 2014-03-06 22:13:30 +0000
727+++ gaps/templates/stack_list.html 2014-03-11 17:00:08 +0000
728@@ -1,10 +1,22 @@
729 {% extends "gaps_layout.html" %}
730 {% load dashboard_extras %}
731-{% load ci_extras %}
732-
733-{% block page_name %}PS-QA Dashboard{% endblock %}
734+{% load gaps_extras %}
735+{% load percentage %}
736+{% block extra_headers %}
737+<link type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/nvd3/0.9/nv.d3.css" rel="stylesheet" />
738+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.2.2/d3.min.js"></script>
739+<!-- need to switch back to cdnjs when bugs are fixed
740+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/0.9/nv.d3.js"></script>-->
741+<script type="text/javascript" src="//6df403e3d98e2ac67ac2-180150c581869d2c4c18db9c9e3179c4.r40.cf1.rackcdn.com/nv.d3.js"></script>
742+{% endblock extra_headers %}
743
744 {% block content %}
745+<style>
746+ #line-chart svg {
747+ height: 400px;
748+ width: 1100px;
749+ }
750+</style>
751 <script type='text/javascript'>
752 //<![CDATA[
753 $(document).ready(function() {
754@@ -15,6 +27,24 @@
755 });
756 //]]>
757 </script>
758+<script type="text/javascript">
759+ d3.json('{% url 'gaps_api_stack_list' %}', function(data) {
760+ nv.addGraph(function() {
761+ var chart = nv.models.lineChart()
762+ .width(900).height(400);
763+ chart.forceY([0, 1]);
764+ chart.xAxis
765+ .axisLabel('Date')
766+ .tickFormat(function(d){return d3.time.format('%Y%m%d')(new Date(d * 1000));})
767+ chart.yAxis
768+ .axisLabel('%')
769+ .tickFormat(d3.format('%'))
770+ d3.select('#line-chart svg').datum(data).transition().duration(500).call(chart);
771+ nv.utils.windowResize(chart.update);
772+ return chart;
773+ });
774+ });
775+</script>
776 <div class='grid_15'>
777 <h2>
778 {% if show_filter %}
779@@ -26,7 +56,7 @@
780 </div>
781 <div class='grid_2'>
782 <h3 class='nav-title'>
783- RELEASE FILTER
784+
785 </h3>
786 <ul class='left_nav'>
787 <li>
788@@ -39,14 +69,17 @@
789 {% endfor %}
790 </ul>
791 </div>
792-<div class='grid_13'>
793+<div class="grid_13">
794+<div id="line-chart">
795+ <svg></svg>
796+</div>
797 <table class='data-table basic wide'>
798 <thead>
799 <tr>
800 <th>Name</th>
801- <th>Publish Status</th>
802- <th>Last Build</th>
803- <th>Last Publish</th>
804+ <th>Projects Reporting</th>
805+ <th>Line Coverage</th>
806+ <th>Branch Coverage</th>
807 </tr>
808 </thead>
809 <tbody>
810@@ -55,14 +88,14 @@
811 <td>
812 <a href='{% url 'stacks' %}{{ stack.name }}/'>{{ stack.name }}</a>
813 </td>
814- <td class='{{ stack.status|stack_status_color }}'>
815- {{ stack.status }}
816- </td>
817- <td>
818- {{ stack.last_build.timestamp }}
819- </td>
820- <td>
821- {{ stack.last_publish }}
822+ <td class='{{ stack.percent_reporting|percent_reporting_color }}'>
823+ {{ stack.percent_reporting|percentage }}
824+ </td>
825+ <td>
826+ {{ stack.last_coverage_observation.line_coverage|percentage }}
827+ </td>
828+ <td>
829+ {{ stack.last_coverage_observation.branch_coverage|percentage }}
830 </td>
831 </tr>
832 {% endfor %}
833
834=== renamed file 'gaps/templatetags/ci_extras.py' => 'gaps/templatetags/gaps_extras.py'
835--- gaps/templatetags/ci_extras.py 2014-02-20 19:19:28 +0000
836+++ gaps/templatetags/gaps_extras.py 2014-03-11 17:00:08 +0000
837@@ -1,16 +1,15 @@
838+import logging
839+
840 from django import template
841-import logging
842
843-logger = logging.getLogger('qa_dashboard')
844 register = template.Library()
845
846-
847 @register.filter
848-def stack_status_color(value):
849- if value is None:
850- return ""
851- if value == "Published":
852+def percent_reporting_color(value):
853+ if value == 0.0:
854+ return "black"
855+ if value == 1.0:
856 return "green"
857- if value.startswith("Manual"):
858+ if value >= 0.9:
859 return "yellow"
860 return "red"
861
862=== modified file 'gaps/urls.py'
863--- gaps/urls.py 2014-03-05 23:18:33 +0000
864+++ gaps/urls.py 2014-03-11 17:00:08 +0000
865@@ -23,15 +23,10 @@
866
867 urlpatterns = patterns(
868 'gaps.views',
869- #url(r'^$', redirect_to, {'url': 'project', 'permanent': False},
870- # name='gaps_overview'),
871- url(r'^$', RedirectView.as_view(url='project', permanent=False), name='gaps_overview'),
872+ url(r'^$', RedirectView.as_view(url='project', permanent=False),
873+ name='gaps_overview'),
874 url(r'^stack/$', 'stack_list', name='stacks'),
875- url(r'^stack/(?P<stack_name>[^/]+)/$', 'stack'),
876+ url(r'^stack/(?P<name>[^/]+)/$', 'stack'),
877 url(r'^project/$', 'project_list', name='projects'),
878 url(r'^project/(?P<name>[^/]+)/$', 'project'),
879- url(r'^job/$', 'job_list', name='jobs'),
880- url(r'^job/(?P<job_name>[^/]+)/$', 'job'),
881- url(r'^job/(?P<job_name>[^/]+)/(?P<build_number>\d+)/$',
882- 'build'),
883 )
884
885=== modified file 'gaps/urls_api.py'
886--- gaps/urls_api.py 2014-02-20 19:19:28 +0000
887+++ gaps/urls_api.py 2014-03-11 17:00:08 +0000
888@@ -17,86 +17,13 @@
889
890 urlpatterns = patterns(
891 'gaps.api',
892- url(r'^images/$', 'images', name='gaps_api_images'),
893- url(
894- r'^image/(?P<image_id>\d+)/$',
895- 'image_detail',
896- name='gaps_api_image_detail',
897- ),
898- url(r'^upgrades/$', 'upgrades', name='gaps_api_upgrades'),
899- url(
900- r'^upgrade/(?P<upgrade_id>\d+)/$',
901- 'upgrade_detail',
902- name='gaps_api_upgrade_detail',
903- ),
904- url(r'^machines/$', 'machines', name='gaps_api_machines'),
905- url(
906- r'^machine/(?P<machine_id>\d+)/$',
907- 'machine_detail',
908- name='gaps_api_machine_detail',
909- ),
910- url(
911- r'^result/(?P<result_id>\d+)/$',
912- 'result_detail',
913- name='gaps_api_result_detail',
914- ),
915- url(
916- r'^image/(?P<image_id>\d+)/machine/(?P<machine_id>\d+)/results/$',
917- 'image_machine_results',
918- name='gaps_api_image_machine_results',
919- ),
920- url(
921- r'^image/(?P<image_id>\d+)/machine/(?P<machine_id>\d+)/metrics/$',
922- 'image_machine_metrics',
923- name='gaps_api_image_machine_metrics',
924- ),
925- url(
926- r'^upgrade/(?P<upgrade_id>\d+)/machine/(?P<machine_id>\d+)/results/$',
927- 'upgrade_machine_results',
928- name='gaps_api_upgrade_machine_results',
929- ),
930- url(
931- r'^upgrade/(?P<upgrade_id>\d+)/machine/(?P<machine_id>\d+)/metrics/$',
932- 'upgrade_machine_metrics',
933- name='gaps_api_upgrade_machine_metrics',
934- ),
935- url(
936- r'^upgrade/machine/(?P<machine_id>\d+)/(?P<arch>\w+)/' +
937- '(?P<variant>\w+)/metric/(?P<metric>\w+)/$',
938- 'upgrade_machine_arch_metrics',
939- name='gaps_api_upgrade_machine_arch_metrics',
940- ),
941- url(
942- r'^image/machine/(?P<machine_id>\d+)/(?P<arch>\w+)/' +
943- '(?P<variant>\w+)/metric/(?P<metric>\w+)/$',
944- 'image_machine_arch_metrics',
945- name='gaps_api_image_machine_arch_metrics',
946- ),
947- url(
948- r'^image/machine/(?P<machine_id>\d+)/arch/(?P<arch>\w+)/process/$',
949- 'image_machine_arch_processes',
950- name='gaps_api_image_machine_arch_processes',
951- ),
952- url(
953- r'^image/machine/(?P<machine_id>\d+)/arch/(?P<arch>\w+)/process/' +
954- r'(?P<process>.+)/$',
955- 'image_machine_arch_processes',
956- name='gaps_api_image_machine_arch_processes',
957- ),
958- url(
959- r'^upgrade/machine/(?P<machine_id>\d+)/arch/(?P<arch>\w+)/process/$',
960- 'upgrade_machine_arch_processes',
961- name='gaps_api_upgrade_machine_arch_processes',
962- ),
963- url(
964- r'^upgrade/machine/(?P<machine_id>\d+)/arch/(?P<arch>\w+)/process/' +
965- r'(?P<process>.+)/$',
966- 'upgrade_machine_arch_processes',
967- name='gaps_api_upgrade_machine_arch_processes',
968- ),
969- url(
970- r'result/(?P<result_id>\d+)/processes/$',
971- 'result_processes',
972- name='gaps_api_result_processes',
973- ),
974+ url(r'^project/(?P<name>[^/]+)/$',
975+ 'project',
976+ name='gaps_api_project'),
977+ url(r'^stack/$',
978+ 'stack_list',
979+ name='gaps_api_stack_list'),
980+ url(r'^stack/(?P<name>[^/]+)/$',
981+ 'stack',
982+ name='gaps_api_stack'),
983 )
984
985=== modified file 'gaps/util/add.py'
986--- gaps/util/add.py 2014-03-06 17:01:11 +0000
987+++ gaps/util/add.py 2014-03-11 17:00:08 +0000
988@@ -7,6 +7,7 @@
989 CoverageData,
990 CoverageProject,
991 CoverageStack,
992+ StackCoverageObservation
993 )
994 from gaps.util import extractor, jenkins_pull
995
996@@ -79,3 +80,4 @@
997 total_count=coverage_info.total,
998 coverage_build=build,
999 )
1000+ stack.refresh_coverage_observations()
1001
1002=== modified file 'gaps/views.py'
1003--- gaps/views.py 2014-03-06 22:13:30 +0000
1004+++ gaps/views.py 2014-03-11 17:00:08 +0000
1005@@ -5,6 +5,8 @@
1006 from django.shortcuts import get_object_or_404
1007 import gviz_api
1008
1009+from common.bread_crumbs import (BreadCrumb,
1010+ BreadCrumbTrail)
1011 from gaps.models import (CoverageProject,
1012 CoverageBuild,
1013 CoverageData,
1014@@ -14,146 +16,49 @@
1015 logger = logging.getLogger('qa_dashboard')
1016
1017
1018-def build(request, job_name, build_number):
1019- jenkins_build = CoverageBuild.objects.get(jenkins_job__name=job_name,
1020- number=build_number)
1021- runs = JenkinsRun.objects.filter(jenkins_build=jenkins_build)
1022- t = loader.get_template('build.html')
1023- c = Context({'build': jenkins_build,
1024- 'runs': runs, })
1025- return HttpResponse(t.render(c))
1026-
1027-
1028-def job_list(request):
1029- show_filter = request.GET.get('show')
1030- logger.debug("Filter: %s" % show_filter)
1031- if show_filter is None or show_filter not in BRANCH_FILTER_TYPES:
1032- show_filter = 'all'
1033-
1034- job_list = JenkinsJob.objects.all().order_by('-name')
1035- if show_filter == 'all':
1036- out_list = job_list
1037- else:
1038- out_list = []
1039- for j in job_list:
1040- if j.name.endswith(show_filter):
1041- out_list.append(j)
1042-
1043- # TODO Remove fake data
1044- for j in out_list:
1045- j.series = 'raring'
1046- j.arch = 'amd64, armhf, i386'
1047- j.last_run_data = ''
1048- j.build_count = 40
1049- j.duration = 1200
1050- j.test_count = 120
1051- j.coverage_rate = 0.55
1052- j.coverage_rate_pct = 55
1053- j.bug_count = 0
1054-
1055- t = loader.get_template('job_list.html')
1056- c = Context({
1057- 'job_list': out_list,
1058- })
1059- return HttpResponse(t.render(c))
1060-
1061-def job(request, job_name):
1062- result_filter = request.GET.get('show')
1063- logger.debug("Filter: %s" % result_filter)
1064- job = JenkinsJob.objects.get(name=job_name)
1065- result_list = JenkinsResult.objects.all()
1066- if result_filter is None or result_filter not in [
1067- result.name for result in result_list]:
1068- logger.debug("we believe the filter isn't in the list")
1069- result_filter = 'all'
1070- if result_filter == 'all':
1071- build_list = CoverageBuild.objects.filter(
1072- jenkins_job=job)
1073- else:
1074- build_list = CoverageBuild.objects.filter(
1075- jenkins_job=job,
1076- result__name=result_filter)
1077- t = loader.get_template('job.html')
1078- c = Context({
1079- 'job': job,
1080- 'build_list': build_list,
1081- 'result_list': result_list,
1082- })
1083- return HttpResponse(t.render(c))
1084-
1085+@BreadCrumb("Stacks")
1086 def stack_list(request):
1087- stack_list = CoverageStack.objects.all()
1088+ stack_list_ = CoverageStack.objects.all()
1089 t = loader.get_template('stack_list.html')
1090- c = Context({'stack_list': stack_list})
1091+ c = Context({'stack_list': stack_list_,
1092+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1093+ stack_list)})
1094 return HttpResponse(t.render(c))
1095
1096-def stack(request, stack_name):
1097- stack = CoverageStack.objects.get(name=stack_name)
1098+@BreadCrumb("Stack", parent=stack_list, needs=['name'])
1099+def stack(request, name):
1100+ stack_ = CoverageStack.objects.get(name=name)
1101 stack_list = CoverageStack.objects.all()
1102 project_list = CoverageProject.objects.filter(
1103- stack__name=stack_name)
1104- coverage_builds = CoverageBuild.objects.filter(
1105- project__in=project_list)
1106- data = []
1107- for build in coverage_builds:
1108- # for every time point, compute and cache stack coverage percentages
1109- observation, observation_created = StackCoverageObservation.objects.get_or_create(
1110- stack=stack,
1111- timestamp=build.ran_at)
1112- if observation_created:
1113- observation.compute_coverage()
1114- data.append({'timestamp': observation.timestamp,
1115- 'line coverage': float(observation.line_coverage),
1116- 'branch coverage': float(observation.branch_coverage)})
1117- columns_order = ['timestamp',
1118- "line coverage",
1119- "branch coverage"]
1120- description = {"timestamp": ("Date", "timestamp"),
1121- "line coverage": ("number", "line coverage"),
1122- "branch coverage": ("number", "branch coverage")}
1123- data_table = gviz_api.DataTable(description)
1124- data_table.LoadData(data)
1125- json_data = data_table.ToJSon(columns_order=columns_order,
1126- order_by="timestamp")
1127+ stack__name=name)
1128 t = loader.get_template('stack_detail.html')
1129- c = Context({'stack': stack,
1130+ c = Context({'stack': stack_,
1131 'stack_list': stack_list,
1132 'project_list': project_list,
1133- 'coverage_builds': coverage_builds,
1134- 'json_data': json_data})
1135- return HttpResponse(t.render(c))
1136-
1137+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1138+ stack, name=stack_.name)})
1139+ return HttpResponse(t.render(c))
1140+
1141+@BreadCrumb("Projects")
1142+def project_list(request):
1143+ project_list_ = CoverageProject.objects.all()
1144+ t = loader.get_template('project_list.html')
1145+ c = Context({'project_list': project_list_,
1146+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1147+ project_list)})
1148+ return HttpResponse(t.render(c))
1149+
1150+@BreadCrumb('Project', parent=stack, needs=['name'])
1151 def project(request, name):
1152- project = get_object_or_404(
1153+ project_ = get_object_or_404(
1154 CoverageProject,
1155 name=name)
1156- data = []
1157 coverage_datas = CoverageData.objects.filter(
1158- coverage_build__project=project).order_by(
1159- '-coverage_build__ran_at')
1160- for coverage_data in coverage_datas:
1161- data.append({'timestamp': coverage_data.coverage_build.ran_at,
1162- 'line coverage': float(coverage_data.line_coverage),
1163- 'branch coverage': float(coverage_data.branch_coverage)})
1164- columns_order = ['timestamp',
1165- "line coverage",
1166- "branch coverage"]
1167- description = {"timestamp": ("Date", "timestamp"),
1168- "line coverage": ("number", "line coverage"),
1169- "branch coverage": ("number", "branch coverage")}
1170- data_table = gviz_api.DataTable(description)
1171- data_table.LoadData(data)
1172- json_data = data_table.ToJSon(columns_order=columns_order,
1173- order_by="ran_at")
1174+ coverage_build__project=project_).order_by(
1175+ '-coverage_build__ran_at')[:10]
1176 t = loader.get_template('project_detail.html')
1177- c = Context({'stack': stack,
1178- 'project': project,
1179- 'job_list': job_list,
1180- 'json_data': json_data})
1181- return HttpResponse(t.render(c))
1182-
1183-def project_list(request):
1184- project_list = CoverageProject.objects.all()
1185- t = loader.get_template('project_list.html')
1186- c = Context({'project_list': project_list})
1187+ c = Context({'project': project_,
1188+ 'coverage_datas': coverage_datas,
1189+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1190+ project, name=project_.name)})
1191 return HttpResponse(t.render(c))
1192
1193=== modified file 'qa_dashboard/settings.py'
1194--- qa_dashboard/settings.py 2014-02-20 19:19:28 +0000
1195+++ qa_dashboard/settings.py 2014-03-11 17:00:08 +0000
1196@@ -24,9 +24,11 @@
1197
1198
1199 ALLOWED_HOSTS = [
1200- 'reports.qa.ubuntu.com',
1201- 'ci.ubuntu.com',
1202- 'rootstock.canonical.com',
1203+ # temporary!
1204+ '*',
1205+# 'reports.qa.ubuntu.com',
1206+# 'ci.ubuntu.com',
1207+# 'rootstock.canonical.com',
1208 ]
1209
1210 ADMINS = (
1211
1212=== modified file 'qa_dashboard/urls.py'
1213--- qa_dashboard/urls.py 2014-02-20 19:19:28 +0000
1214+++ qa_dashboard/urls.py 2014-03-11 17:00:08 +0000
1215@@ -16,13 +16,17 @@
1216 import sys
1217
1218 from django.conf.urls import include, patterns, url
1219+from django.views.generic import RedirectView
1220
1221 from django.contrib import admin
1222 admin.autodiscover()
1223
1224 urlpatterns = patterns(
1225 '',
1226- url(r'^$', 'common.views.index', name='index'),
1227+ # Restore this when we've merged with qa_dashboard of course :) .
1228+ #url(r'^$', 'common.views.index', name='index'),
1229+ url(r'^$', RedirectView.as_view(url='gaps/stack', permanent=False),
1230+ name='index'),
1231 url(r'^edit/$', 'common.views.edit_profile', name='edit_profile'),
1232 url(r'^admin/', include(admin.site.urls)),
1233 url(r'^\+oops$', 'common.views.cause_oops', name='cause-oops'),

Subscribers

People subscribed via source and target branches

to all changes: