Merge lp:~allanlesage/qa-coverage-dashboard/initial-mega-merge into lp:qa-coverage-dashboard
- initial-mega-merge
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chris Gagnon | Pending | ||
Review via email: mp+210458@code.launchpad.net |
Commit message
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 : | # |
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>© 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'), |
move this out of the html, it's using a lot of the same code
467 +<script type="text/ javascript" > function( ) { lineChart( ) 1100).height( 400); function( d){return d3.time. format( '%Y%m%d' )(new Date(d * 1000));}) d3.format( '%')) '#line- chart svg').datum( data).transitio n().duration( 500).call( chart); windowResize( chart.update) ;
468 + d3.json('{% url "gaps_api_project" project.name %}', function(data) {
469 + nv.addGraph(
470 + var chart = nv.models.
471 + .width(
472 + chart.forceY([0, 1]);
473 + chart.xAxis
474 + .axisLabel('Date')
475 + .tickFormat(
476 +
477 + chart.yAxis
478 + .axisLabel('%')
479 + .tickFormat(
480 + d3.select(
481 +
482 + nv.utils.
483 +
484 + return chart;
485 + });
486 + });
487 +</script>
640 +<script type="text/ javascript" > function( ) { lineChart( ) 900).height( 400); function( d){return d3.time. format( '%Y%m%d' )(new Date(d * 1000));}) d3.format( '%')) '#line- chart svg').datum( data).transitio n().duration( 500).call( chart); windowResize( chart.update) ;
641 + d3.json('{% url 'gaps_api_stack' stack.name %}', function(data) {
642 + nv.addGraph(
643 + var chart = nv.models.
644 + .width(
645 + chart.forceY([0, 1]);
646 + chart.xAxis
647 + .axisLabel('Date')
648 + .tickFormat(
649 + chart.yAxis
650 + .axisLabel('%')
651 + .tickFormat(
652 + d3.select(
653 + nv.utils.
654 + return chart;
655 + });
656 + });
657 +</script>
758 +<script type="text/ javascript" > stack_list' %}', function(data) { function( ) { lineChart( ) 900).height( 400); function( d){return d3.time. format( '%Y%m%d' )(new Date(d * 1000));}) d3.format( '%')) '#line- chart svg').datum( data).transitio n().duration( 500).call( chart); windowResize( chart.update) ;
759 + d3.json('{% url 'gaps_api_
760 + nv.addGraph(
761 + var chart = nv.models.
762 + .width(
763 + chart.forceY([0, 1]);
764 + chart.xAxis
765 + .axisLabel('Date')
766 + .tickFormat(
767 + chart.yAxis
768 + .axisLabel('%')
769 + .tickFormat(
770 + d3.select(
771 + nv.utils.
772 + return chart;
773 + });
774 + });
775 +</script>
Can we move the properties in the model to the api?