Merge lp:~qa-dashboard/qa-dashboard/adding-view-based-breadcrumbs into lp:qa-dashboard

Proposed by Chris Johnston
Status: Merged
Approved by: Chris Johnston
Approved revision: 364
Merged at revision: 389
Proposed branch: lp:~qa-dashboard/qa-dashboard/adding-view-based-breadcrumbs
Merge into: lp:qa-dashboard
Diff against target: 1771 lines (+781/-274)
24 files modified
bootspeed/templates/bootspeed/nav.html (+1/-1)
bootspeed/views.py (+72/-0)
common/bread_crumbs.py (+183/-0)
common/static/css/style.css (+67/-0)
common/templates/layout.html (+16/-1)
eventstat/templates/power/eventstat/arch_breakdown.html (+0/-6)
eventstat/templates/power/eventstat/image_details.html (+0/-6)
eventstat/templates/power/eventstat/task_details.html (+0/-16)
eventstat/templates/power/eventstat/task_overview.html (+0/-6)
eventstat/templates/power/eventstat/upgrade_details.html (+0/-6)
eventstat/views.py (+158/-69)
idle_power/views.py (+30/-0)
memory/templates/memory/machine_details.html (+0/-20)
memory/templates/memory/machine_results.html (+0/-5)
memory/views.py (+102/-10)
power/urls.py (+1/-1)
power/views.py (+59/-42)
qa_dashboard/urls.py (+3/-3)
smoke/templates/smoke/build_overview.html (+0/-14)
smoke/templates/smoke/result_logs.html (+0/-25)
smoke/templates/smoke/results.html (+0/-22)
smoke/views.py (+69/-6)
sru/templates/sru/overview_kernel.html (+0/-13)
sru/views.py (+20/-2)
To merge this branch: bzr merge lp:~qa-dashboard/qa-dashboard/adding-view-based-breadcrumbs
Reviewer Review Type Date Requested Status
Andy Doan (community) Approve
Chris Johnston Needs Resubmitting
Review via email: mp+162376@code.launchpad.net

Commit message

Add breadcrumbs across the dashboard

To post a comment you must log in.
Revision history for this message
Andy Doan (doanac) wrote :

issue 1: http://91.189.93.71:8000/smoke/precise/run/2427/image/2427/ - use build number instead of date in the crumb. while you are in there you can also change the page title to include variant, arch, and type

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

I think we missed some stuff earlier: capitalize the first letter of:
827 + "upgrade details"
884 + "task details",
909 + "task details",
993 + "detailed view",
1201 + "machine upgrade results",
1256 + "machine upgrade details",
1591 + "image {build_number}"

Revision history for this message
Chris Johnston (cjohnston) :
review: Needs Resubmitting
Revision history for this message
Andy Doan (doanac) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bootspeed/templates/bootspeed/nav.html'
2--- bootspeed/templates/bootspeed/nav.html 2012-12-14 15:16:40 +0000
3+++ bootspeed/templates/bootspeed/nav.html 2013-05-03 22:59:28 +0000
4@@ -5,7 +5,7 @@
5 <ul class='left_nav'>
6 {% for arch in arches %}
7 <li{% if active = arch %} class="active"{% endif %}>
8- <a href='{% url bootspeed_arch_overview arch %}'>
9+ <a href='{% url bootspeed_arch arch %}'>
10 {{ arch }}
11 </a>
12 </li>
13
14=== modified file 'bootspeed/views.py'
15--- bootspeed/views.py 2013-05-01 16:20:50 +0000
16+++ bootspeed/views.py 2013-05-03 22:59:28 +0000
17@@ -25,6 +25,13 @@
18
19 from django_tables2 import RequestConfig
20
21+from common.bread_crumbs import (
22+ BreadCrumb,
23+ BreadCrumbTrail,
24+)
25+
26+from smoke.views import index
27+
28 from common.models import Bug
29
30 from bootspeed.models import (
31@@ -71,6 +78,7 @@
32 data = {
33 'images': images,
34 'releases': releases,
35+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(overview),
36 }
37
38 return render_to_response(
39@@ -80,6 +88,7 @@
40 )
41
42
43+@BreadCrumb("Bootspeed testing", parent=index, needs=['arch', ])
44 @require_GET
45 def arch_overview(request, arch=None):
46 if arch is None:
47@@ -94,6 +103,10 @@
48 'arch': arch,
49 'arches': arches,
50 'active': arch,
51+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
52+ arch_overview,
53+ arch=arch,
54+ ),
55 }
56 return render_to_response(
57 'bootspeed/arch_overview.html',
58@@ -102,6 +115,11 @@
59 )
60
61
62+@BreadCrumb(
63+ "Aggregated data",
64+ parent=arch_overview,
65+ needs=['machine_id', 'arch'],
66+)
67 @require_GET
68 def machine_data(request, machine_id, arch):
69 machine = get_object_or_404(Machine, id=machine_id)
70@@ -110,6 +128,12 @@
71 'name': machine.name,
72 'id': machine.id,
73 'arch': arch,
74+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
75+ machine_data,
76+ machine_id=machine_id,
77+ machine=machine.name,
78+ arch=arch,
79+ ),
80 }
81 return render_to_response(
82 "bootspeed/machine_data.html",
83@@ -118,6 +142,11 @@
84 )
85
86
87+@BreadCrumb(
88+ "Raw data",
89+ parent=machine_data,
90+ needs=['machine_id', 'arch'],
91+)
92 @require_GET
93 def machine_raw_data(request, machine_id, arch):
94 machine = get_object_or_404(Machine, id=machine_id)
95@@ -150,11 +179,21 @@
96 'id': machine.id,
97 'arch': arch,
98 'table': table,
99+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
100+ machine_raw_data,
101+ machine_id=machine_id,
102+ machine=machine.name,
103+ arch=arch,
104+ ),
105 }
106 return render_to_response("bootspeed/machine_raw_data.html", data,
107 RequestContext(request))
108
109
110+@BreadCrumb(
111+ "Bugs by image",
112+ parent=arch_overview,
113+)
114 @require_GET
115 def image_bugs_overview(request):
116 images = JobImage.objects.annotate(
117@@ -177,6 +216,10 @@
118 data = {
119 'images': images,
120 'unknown_images': unknown_images,
121+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
122+ image_bugs_overview,
123+ arch='amd64',
124+ ),
125 }
126
127 return render_to_response(
128@@ -186,6 +229,11 @@
129 )
130
131
132+@BreadCrumb(
133+ "{image}",
134+ parent=image_bugs_overview,
135+ needs=['image_id']
136+)
137 @require_GET
138 def image_bugs_detail(request, image_id):
139 image_builds = ImageBuild.objects.filter(job_image=image_id)
140@@ -194,6 +242,12 @@
141 data = {
142 'image_builds': image_builds,
143 'image': image,
144+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
145+ image_bugs_detail,
146+ image_id=image_id,
147+ arch='amd64',
148+ image=image.build_number,
149+ ),
150 }
151
152 return render_to_response(
153@@ -203,6 +257,10 @@
154 )
155
156
157+@BreadCrumb(
158+ "Bugs",
159+ parent=arch_overview,
160+)
161 @require_GET
162 def bug_overview(request):
163 bugs = Bug.objects.annotate(
164@@ -216,6 +274,10 @@
165
166 data = {
167 'bugs': bugs,
168+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
169+ bug_overview,
170+ arch='amd64',
171+ ),
172 }
173
174 return render_to_response(
175@@ -225,6 +287,11 @@
176 )
177
178
179+@BreadCrumb(
180+ "Builds affected",
181+ parent=bug_overview,
182+ needs=['bug_id'],
183+)
184 @require_GET
185 def bug_detail(request, bug_id):
186 bug = get_object_or_404(Bug, id=bug_id)
187@@ -232,6 +299,11 @@
188 data = {
189 'builds': bug.build_bugs.all(),
190 'bug': bug,
191+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
192+ bug_detail,
193+ arch='amd64',
194+ bug_id=bug_id,
195+ ),
196 }
197
198 return render_to_response(
199
200=== added file 'common/bread_crumbs.py'
201--- common/bread_crumbs.py 1970-01-01 00:00:00 +0000
202+++ common/bread_crumbs.py 2013-05-03 22:59:28 +0000
203@@ -0,0 +1,183 @@
204+# Copyright (C) 2010, 2011 Linaro Limited
205+#
206+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
207+#
208+# This file is part of LAVA Server.
209+#
210+# LAVA Server is free software: you can redistribute it and/or modify
211+# it under the terms of the GNU Affero General Public License version 3
212+# as published by the Free Software Foundation
213+#
214+# LAVA Server is distributed in the hope that it will be useful,
215+# but WITHOUT ANY WARRANTY; without even the implied warranty of
216+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
217+# GNU General Public License for more details.
218+#
219+# You should have received a copy of the GNU Affero General Public License
220+# along with LAVA Server. If not, see <http://www.gnu.org/licenses/>.
221+
222+"""
223+lava_server.bread_crumbs
224+========================
225+
226+Bread crumb management for LAVA server.
227+
228+This system allows one to construct static trees of views or even site maps,
229+where each view has at most one parent. In this model any view could be
230+followed back through the parent link to create a bread crumb trail of named
231+URLs.
232+
233+It is important to emphasize that this system is STATIC, that is, it is not
234+based on browsing history. Regardless on how the user got to a particular view
235+the bread crumb system will report the same set of pages. The idea is not to
236+let users go back (that's the what the browser allows them to do) but to put
237+the current page into context of where it "belongs".
238+
239+To use this system apply the @BreadCrumb(name, parent=parent_view,
240+needs=['required', 'keywords']) decorator to your view function. To render
241+breadcrumbs you can use the default template that is a part of
242+"layout/content.html" template. Your context must include the bread_crumb_trail
243+variable. To construct it call BreadCrumbTrail.leading_to(your_view_name, ...)
244+passing any of the keyword arguments specified in needs of your and any parent
245+views (yes this is annoying).
246+
247+A mistake in pairing 'needs' to keywords passed to BreadCrumbTrail.leading_to()
248+will result in logged warnings (either a name of the URL being not
249+constructible). To fix that simply add the missing keyword argument and reload.
250+"""
251+
252+from django.core.urlresolvers import reverse
253+import logging
254+
255+
256+class BreadCrumb(object):
257+ """
258+ A crumb of bread left in the forest of pages to let you go back to (no, not
259+ to where you came from) where the developer desired you to go.
260+ """
261+
262+ def __init__(self, name, parent=None, needs=None):
263+ """
264+ Construct a bread crumb object.
265+
266+ The name is the essential property creating the actual text visible on
267+ web pages. It may be a static string or a new-style python string
268+ template. Parent allows one to construct a static brad crumb tree where
269+ each crumb may have at most one parent. Needs, if specified, must be
270+ an array of strings that denote identifiers required to resolve the URL
271+ of this bread crumb. The identifiers are obtained from the call
272+ BreadCrumbTrail.leading_to().
273+ """
274+ self.name = name
275+ self.view = None
276+ self.parent = parent
277+ self.needs = needs or []
278+
279+ def __repr__(self):
280+ return "<BreadCrumb name=%r view=%r parent=%r>" % (
281+ self.name, self.view, self.parent)
282+
283+ def __call__(self, view):
284+ """
285+ Call method, used when decorating function-based views
286+
287+ It does not redefine the function (so is not a real decorator) but
288+ instead stores the bread crumb object in the _bread_crumb attribute of
289+ the function.
290+ """
291+ self.view = view
292+ view._bread_crumb = self
293+ return view
294+
295+ def get_name(self, kwargs):
296+ """
297+ Get the name of this crumb.
298+
299+ The name is formatted with the specified keyword arguments.
300+ """
301+ try:
302+ return self.name.format(**kwargs)
303+ except:
304+ logging.exception(
305+ "Unable to construct breadcrumb name for view %r", self.view)
306+ raise
307+
308+ def get_absolute_url(self, kwargs):
309+ """
310+ Get the URL of this crumb.
311+
312+ The URL is constructed with a call to Dajngo's reverse() function. It
313+ is supplemented with the same variables that were listed in needs array
314+ in the bread crumb constructor. The arguments are passed in order, from
315+ the kwargs dictionary.
316+ """
317+ try:
318+ return reverse(
319+ self.view,
320+ args=[kwargs[name] for name in self.needs])
321+ except:
322+ logging.exception(
323+ "Unable to construct breadcrumb URL for view %r", self.view)
324+ raise
325+
326+
327+class LiveBreadCrumb(object):
328+ """
329+ Bread crumb instance as observed by a particular request.
330+
331+ It is a binding between the global view-specific bread crumb object and
332+ dynamic request-specific keyword arguments.
333+
334+ For convenience it provides two bread crumb functions (get_name() and
335+ get_absolute_url()) that automatically provide the correct keyword
336+ arguments.
337+ """
338+
339+ def __init__(self, bread_crumb, kwargs):
340+ self.bread_crumb = bread_crumb
341+ self.kwargs = kwargs
342+
343+ def __unicode__(self):
344+ return self.get_name()
345+
346+ def get_name(self):
347+ return self.bread_crumb.get_name(self.kwargs)
348+
349+ def get_absolute_url(self):
350+ return self.bread_crumb.get_absolute_url(self.kwargs)
351+
352+
353+class BreadCrumbTrail(object):
354+ """
355+ A list of live bread crumbs that lead from a particular view, along the
356+ parent chain, all the way to the root view (that is without any parent
357+ view).
358+ """
359+
360+ def __init__(self, bread_crumb_list, kwargs):
361+ self.bread_crumb_list = bread_crumb_list
362+ self.kwargs = kwargs
363+
364+ def __iter__(self):
365+ for bread_crumb in self.bread_crumb_list:
366+ yield LiveBreadCrumb(bread_crumb, self.kwargs)
367+
368+ @classmethod
369+ def leading_to(cls, view, **kwargs):
370+ """
371+ Create an instance of BreadCrumbTrail that starts at the specified
372+ view.
373+
374+ Additional keyword arguments, if provided, will be available to
375+ get_name() and get_absolute_url() of each LiveBreadCrumb that makes up
376+ this trail. In practice they should contain a set of arguments that are
377+ needed by any parent bread crumb URL or name.
378+
379+ TODO: could we check this statically somehow?
380+ """
381+ lst = []
382+ while view is not None:
383+ lst.append(view._bread_crumb)
384+ view = view._bread_crumb.parent
385+ lst.reverse()
386+ return cls(lst, kwargs or {})
387
388=== modified file 'common/static/css/style.css'
389--- common/static/css/style.css 2013-04-26 17:31:17 +0000
390+++ common/static/css/style.css 2013-05-03 22:59:28 +0000
391@@ -761,3 +761,70 @@
392 overflow : hidden;
393 padding : 8px 8px 2px;
394 }
395+
396+/* Breadcrumbs */
397+#lava-breadcrumbs {
398+ /* Clear floats */
399+ overflow: hidden;
400+ font-size: 80%
401+}
402+#lava-breadcrumbs li{
403+ float: left;
404+ margin: 0 .5em 0 1em;
405+}
406+
407+#lava-breadcrumbs a{
408+ background: #ddd;
409+ padding: .3em .6em;
410+ float: left;
411+ text-decoration: none;
412+ text-shadow: 0 1px 0 rgba(255,255,255,.5);
413+ position: relative;
414+}
415+
416+#lava-breadcrumbs a:hover{
417+ background: #DD4814;
418+ color: white;
419+}
420+
421+#lava-breadcrumbs a::before{
422+ content: "";
423+ position: absolute;
424+ top: 21px;
425+ margin-top: -1.8em;
426+ border-width: 1em 0 1em 1.3em;
427+ border-style: solid;
428+ border-color: #ddd #ddd #ddd transparent;
429+ left: -14px;
430+}
431+
432+#lava-breadcrumbs a:hover::before{
433+ border-color: #DD4814 #DD4814 #DD4814 transparent;
434+}
435+
436+#lava-breadcrumbs a::after{
437+ content: "";
438+ position: absolute;
439+ top: 21px;
440+ margin-top: -1.8em;
441+ border-top: 1em solid transparent;
442+ border-bottom: 1em solid transparent;
443+ border-left: 1.3em solid #ddd;
444+ right: -14px;
445+}
446+
447+
448+#lava-breadcrumbs a:hover::after{
449+ border-left-color: #DD4814;
450+}
451+
452+#lava-breadcrumbs .current,
453+#lava-breadcrumbs .current:hover{
454+ font-weight: bold;
455+ background: none;
456+}
457+
458+#lava-breadcrumbs .current::after,
459+#lava-breadcrumbs .current::before{
460+ content: normal;
461+}
462
463=== modified file 'common/templates/layout.html'
464--- common/templates/layout.html 2013-05-01 18:08:38 +0000
465+++ common/templates/layout.html 2013-05-03 22:59:28 +0000
466@@ -26,7 +26,7 @@
467 <li {% ifequal url.0 'smoke' %}class="active"{% endifequal %} id="main-nav"><a class="main-nav-item" href='{% url smoke_index %}'>Smoke</a></li>
468 <li {% ifequal url.0 'sru' %}class="active"{% endifequal %} id="main-nav"><a class="main-nav-item" href='{% url sru %}'>SRU</a></li>
469 <li {% ifequal url.0 'bootspeed' %}class="active"{% endifequal %}id="main-nav"><a class="main-nav-item" href="{% url bootspeed_arch %}">Bootspeed</a></li>
470- <li {% ifequal url.0 'power' %}class="active"{% endifequal %}id="main-nav"><a class="main-nav-item" href="{% url power_arch %}">Power</a></li>
471+ <li {% ifequal url.0 'power' %}class="active"{% endifequal %}id="main-nav"><a class="main-nav-item" href="{% url power_overview %}">Power</a></li>
472 <li {% ifequal url.0 'memory' %}class="active"{% endifequal %}id="main-nav"><a class="main-nav-item" href="{% url memory_arch %}">Memory</a></li>
473 </ul>
474 </nav>
475@@ -46,6 +46,21 @@
476 </nav>
477 </div>
478 {% endblock %}
479+ <div class="grid_15">
480+ {% block breadcrumb_container %}
481+ <!-- breadcrumbs -->
482+ <div id="lava-breadcrumbs">
483+ <ul>
484+ {% block breadcrumbs %}
485+ {% for bread_crumb in bread_crumb_trail %}
486+ <li><a href="{{ bread_crumb.get_absolute_url }}">{{ bread_crumb.get_name }}</a></li>
487+ {% endfor %}
488+ {% endblock %}
489+ </ul>
490+ </div>
491+ <!-- !breadcrumbs -->
492+ {% endblock breadcrumb_container %}
493+ </div>
494 {% block content %}{% endblock %}
495 </div>
496 <div class='container_15 clearfix' id='footer'>
497
498=== modified file 'eventstat/templates/power/eventstat/arch_breakdown.html'
499--- eventstat/templates/power/eventstat/arch_breakdown.html 2013-05-03 00:46:34 +0000
500+++ eventstat/templates/power/eventstat/arch_breakdown.html 2013-05-03 22:59:28 +0000
501@@ -11,12 +11,6 @@
502 {% endblock extra_headers %}
503 {% block content %}
504
505-<div id="breadcrumb">
506- <span class="crumb">
507- <a href="{% url eventstat_arch_overview arch %}">{{ arch }}</a>
508- </span>
509-</div>
510-
511 <div class="grid_2 left_nav" style="height: 100%;">
512 {% include "power/eventstat/task_type_include.html" %}
513
514
515=== modified file 'eventstat/templates/power/eventstat/image_details.html'
516--- eventstat/templates/power/eventstat/image_details.html 2013-04-25 20:07:34 +0000
517+++ eventstat/templates/power/eventstat/image_details.html 2013-05-03 22:59:28 +0000
518@@ -14,12 +14,6 @@
519
520 {% block content %}
521
522-<div id="breadcrumb">
523- <span class="crumb">
524- <a href="{% url eventstat_arch_overview image.arch %}">{{ image.arch }}</a>
525- </span>
526-</div>
527-
528 <div class="grid_2">
529 {% include "power/eventstat/task_type_include.html" %}
530 </div>
531
532=== modified file 'eventstat/templates/power/eventstat/task_details.html'
533--- eventstat/templates/power/eventstat/task_details.html 2013-04-25 20:07:34 +0000
534+++ eventstat/templates/power/eventstat/task_details.html 2013-05-03 22:59:28 +0000
535@@ -18,22 +18,6 @@
536 {% endblock extra_headers %}
537 {% block content %}
538
539-<div id="breadcrumb">
540- <span class="crumb">
541- <a href="{% url eventstat_arch_overview arch %}">{{ arch }}</a>
542- </span>
543- <span class="crumb-sep">
544- >>
545- </span>
546- <span class="crumb">
547- {% if image %}
548- <a href="{% url eventstat_image_details image_id=image.id machine_id=machine.id task_type="all" %}">{{ image.build_number }}</a>
549- {% else %}
550- <a href="{% url eventstat_upgrade_details upgrade_id=upgrade.id machine_id=machine.id task_type="all" %}">{{ upgrade.date }}</a>
551- {% endif %}
552- </span>
553-</div>
554-
555 <div class="grid_15">
556 <h2>
557 Wakeups for {{ task }} using {{ arch }} on {{ machine }} for
558
559=== modified file 'eventstat/templates/power/eventstat/task_overview.html'
560--- eventstat/templates/power/eventstat/task_overview.html 2013-04-25 20:07:34 +0000
561+++ eventstat/templates/power/eventstat/task_overview.html 2013-05-03 22:59:28 +0000
562@@ -11,12 +11,6 @@
563 {% endblock extra_headers %}
564 {% block content %}
565
566-<div id="breadcrumb">
567- <span class="crumb">
568- <a href="{% url eventstat_arch_overview arch %}">{{ arch }}</a>
569- </span>
570-</div>
571-
572 <div class="grid_2">
573 {% include "power/eventstat/days_include.html" with day_urls=day_urls days=days %}
574 </div>
575
576=== modified file 'eventstat/templates/power/eventstat/upgrade_details.html'
577--- eventstat/templates/power/eventstat/upgrade_details.html 2013-04-25 20:07:34 +0000
578+++ eventstat/templates/power/eventstat/upgrade_details.html 2013-05-03 22:59:28 +0000
579@@ -13,12 +13,6 @@
580 {% endblock extra_headers %}
581 {% block content %}
582
583-<div id="breadcrumb">
584- <span class="crumb">
585- <a href="{% url eventstat_arch_overview upgrade.arch %}">{{ upgrade.arch }}</a>
586- </span>
587-</div>
588-
589 <div class="grid_2">
590 {% include "power/eventstat/task_type_include.html" %}
591 </div>
592
593=== modified file 'eventstat/views.py'
594--- eventstat/views.py 2013-05-03 00:46:34 +0000
595+++ eventstat/views.py 2013-05-03 22:59:28 +0000
596@@ -28,6 +28,13 @@
597
598 from django_tables2 import RequestConfig
599
600+from common.bread_crumbs import (
601+ BreadCrumb,
602+ BreadCrumbTrail,
603+)
604+
605+from power.views import overview
606+
607 from eventstat.models import (
608 EventstatDetail,
609 EventstatImage,
610@@ -89,6 +96,7 @@
611 return retval
612
613
614+@BreadCrumb("Wakeups", parent=overview)
615 @require_GET
616 def arch_overview(request, arch=None, days=None):
617 arches = get_arches()
618@@ -153,7 +161,8 @@
619 kwargs={
620 'arch': arch,
621 }
622- )
623+ ),
624+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(arch_overview),
625 }
626
627 return render_to_response(
628@@ -163,6 +172,7 @@
629 )
630
631
632+@BreadCrumb("All", parent=arch_overview)
633 @require_GET
634 def arch_breakdown(
635 request,
636@@ -257,6 +267,12 @@
637 days=days
638 ),
639 ),
640+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
641+ arch_breakdown,
642+ machine_id=machine_id,
643+ arch=arch,
644+ method=method,
645+ ),
646 }
647
648 return render_to_response(
649@@ -266,6 +282,91 @@
650 )
651
652
653+@BreadCrumb(
654+ "{build_number} details",
655+ parent=arch_overview,
656+ needs=['image_id', 'machine_id', 'task_type'],
657+)
658+@require_GET
659+def image_details(request, image_id, task_type, machine_id, days=None):
660+ image = get_object_or_404(EventstatImage, id=image_id)
661+ machine = get_object_or_404(EventstatMachine, id=machine_id)
662+
663+ if days is None:
664+ days = DAYS
665+ else:
666+ days = int(days)
667+
668+ results = EventstatDetail.objects.filter(
669+ publish=True,
670+ image=image,
671+ machine=machine,
672+ ran_at__gt=datetime.datetime.now() - datetime.timedelta(days=days),
673+ ).values(
674+ 'task',
675+ 'image_id',
676+ 'image__arch',
677+ 'machine_id',
678+ ).annotate(
679+ average=models.Avg('total'),
680+ minimum=models.Min('total'),
681+ maximum=models.Max('total'),
682+ stddev=models.StdDev('total'),
683+ )
684+
685+ if task_type != 'all':
686+ results = results.filter(task_type=task_type)
687+
688+ table = TaskInstallTable(results)
689+
690+ RequestConfig(request, paginate={"per_page": 100}).configure(table)
691+
692+ data = {
693+ 'table': table,
694+ 'image': image,
695+ 'task_type': task_type,
696+ 'machine': machine,
697+ 'method': "install",
698+ 'days': days,
699+ 'day_urls': _generate_day_urls(
700+ 'eventstat_image_details',
701+ kwargs={
702+ 'image_id': image_id,
703+ 'machine_id': machine_id,
704+ 'task_type': task_type,
705+ }
706+ ),
707+ 'task_type_urls': _generate_task_type_urls(
708+ 'eventstat_image_details',
709+ kwargs=dict(
710+ machine_id=machine_id,
711+ image_id=image_id,
712+ task_type=task_type,
713+ days=days
714+ ),
715+ ),
716+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
717+ image_details,
718+ machine_id=machine_id,
719+ image_id=image_id,
720+ task_type=task_type,
721+ days=days,
722+ build_number=image.build_number,
723+ ),
724+ }
725+
726+ return render_to_response(
727+ "power/eventstat/image_details.html",
728+ data,
729+ RequestContext(request),
730+ )
731+
732+
733+@BreadCrumb(
734+ "Task overview",
735+ parent=arch_breakdown,
736+ needs=['arch', 'machine_id', 'method', 'method'],
737+)
738 @require_GET
739 def task_overview(request, machine_id, arch, method, task, days=None):
740
741@@ -337,6 +438,13 @@
742 'task': task,
743 }
744 ),
745+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
746+ task_overview,
747+ machine_id=machine_id,
748+ arch=arch,
749+ method=method,
750+ task=task,
751+ ),
752 }
753
754 return render_to_response(
755@@ -346,73 +454,11 @@
756 )
757
758
759-@require_GET
760-def image_details(request, image_id, task_type, machine_id, days=None):
761- image = get_object_or_404(EventstatImage, id=image_id)
762- machine = get_object_or_404(EventstatMachine, id=machine_id)
763-
764- if days is None:
765- days = DAYS
766- else:
767- days = int(days)
768-
769- results = EventstatDetail.objects.filter(
770- publish=True,
771- image=image,
772- machine=machine,
773- ran_at__gt=datetime.datetime.now() - datetime.timedelta(days=days),
774- ).values(
775- 'task',
776- 'image_id',
777- 'image__arch',
778- 'machine_id',
779- ).annotate(
780- average=models.Avg('total'),
781- minimum=models.Min('total'),
782- maximum=models.Max('total'),
783- stddev=models.StdDev('total'),
784- )
785-
786- if task_type != 'all':
787- results = results.filter(task_type=task_type)
788-
789- table = TaskInstallTable(results)
790-
791- RequestConfig(request, paginate={"per_page": 100}).configure(table)
792-
793- data = {
794- 'table': table,
795- 'image': image,
796- 'task_type': task_type,
797- 'machine': machine,
798- 'method': "install",
799- 'days': days,
800- 'day_urls': _generate_day_urls(
801- 'eventstat_image_details',
802- kwargs={
803- 'image_id': image_id,
804- 'machine_id': machine_id,
805- 'task_type': task_type,
806- }
807- ),
808- 'task_type_urls': _generate_task_type_urls(
809- 'eventstat_image_details',
810- kwargs=dict(
811- machine_id=machine_id,
812- image_id=image_id,
813- task_type=task_type,
814- days=days
815- ),
816- ),
817- }
818-
819- return render_to_response(
820- "power/eventstat/image_details.html",
821- data,
822- RequestContext(request),
823- )
824-
825-
826+@BreadCrumb(
827+ "Upgrade details",
828+ parent=image_details,
829+ needs=['upgrade_id', 'machine_id', 'task_type'],
830+)
831 @require_GET
832 def upgrade_details(request, upgrade_id, task_type, machine_id, days=None):
833 upgrade = get_object_or_404(EventstatUpgrade, id=upgrade_id)
834@@ -468,9 +514,17 @@
835 machine_id=machine_id,
836 upgrade_id=upgrade_id,
837 task_type=task_type,
838- days=days
839+ days=days,
840+ build_number=upgrade.build_number,
841 ),
842 ),
843+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
844+ upgrade_details,
845+ machine_id=machine_id,
846+ upgrade_id=upgrade_id,
847+ task_type=task_type,
848+ days=days,
849+ ),
850 }
851
852 return render_to_response(
853@@ -480,6 +534,11 @@
854 )
855
856
857+@BreadCrumb(
858+ "Task details",
859+ parent=image_details,
860+ needs=['image_id', 'machine_id', 'task_type'],
861+)
862 @require_GET
863 def image_task_details(request, image_id, task, machine_id):
864 image = get_object_or_404(EventstatImage, id=image_id)
865@@ -501,6 +560,13 @@
866 'arch': image.arch,
867 'task': task,
868 'machine': machine,
869+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
870+ image_task_details,
871+ machine_id=machine_id,
872+ image_id=image_id,
873+ task_type=task,
874+ build_number=image.build_number,
875+ ),
876 }
877
878 return render_to_response(
879@@ -510,6 +576,11 @@
880 )
881
882
883+@BreadCrumb(
884+ "Task details",
885+ parent=image_details,
886+ needs=['upgrade_id', 'machine_id', 'task'],
887+)
888 @require_GET
889 def upgrade_task_details(request, upgrade_id, task, machine_id):
890 machine = get_object_or_404(EventstatMachine, id=machine_id)
891@@ -530,6 +601,12 @@
892 'machine': machine,
893 'upgrade': upgrade,
894 'arch': upgrade.arch,
895+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
896+ upgrade_task_details,
897+ machine_id=machine_id,
898+ upgrade_id=upgrade_id,
899+ task=task,
900+ ),
901 }
902
903 return render_to_response(
904@@ -539,6 +616,11 @@
905 )
906
907
908+@BreadCrumb(
909+ "Task details",
910+ parent=image_details,
911+ needs=['arch', 'method', 'machine_id', 'task'],
912+)
913 @require_GET
914 def task_details(request, machine_id, arch, method, task, days=None):
915
916@@ -577,6 +659,13 @@
917 'arch': arch,
918 'machine': machine,
919 'task': task,
920+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
921+ task_details,
922+ machine_id=machine_id,
923+ task=task,
924+ arch=arch,
925+ method=method,
926+ ),
927 }
928
929 return render_to_response(
930
931=== modified file 'idle_power/views.py'
932--- idle_power/views.py 2013-04-25 18:27:26 +0000
933+++ idle_power/views.py 2013-05-03 22:59:28 +0000
934@@ -25,6 +25,13 @@
935
936 from django_tables2 import RequestConfig
937
938+from common.bread_crumbs import (
939+ BreadCrumb,
940+ BreadCrumbTrail,
941+)
942+
943+from power.views import overview
944+
945 from idle_power.config import get_testcases
946
947 from idle_power.models import (
948@@ -35,6 +42,7 @@
949 from idle_power.tables import DataTable, MachineTable
950
951
952+@BreadCrumb("Software measurement", parent=overview)
953 @require_GET
954 def arch_overview(request, arch=None):
955 vals = IdlePowerDetail.objects.filter(
956@@ -67,6 +75,7 @@
957 'active': arch,
958 'chart_size': chart_size,
959 'testcases': testcases,
960+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(arch_overview),
961 }
962
963 return render_to_response(
964@@ -76,6 +85,11 @@
965 )
966
967
968+@BreadCrumb(
969+ "Machine details",
970+ parent=arch_overview,
971+ needs=['machine_id', 'arch'],
972+)
973 @require_GET
974 def machine_detail(request, machine_id, arch):
975 machine = get_object_or_404(IdlePowerMachine, id=machine_id)
976@@ -96,6 +110,11 @@
977 'name': machine.name,
978 'arch': arch,
979 'table': table,
980+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
981+ machine_detail,
982+ arch=arch,
983+ machine_id=machine_id,
984+ ),
985 }
986
987 return render_to_response(
988@@ -105,6 +124,11 @@
989 )
990
991
992+@BreadCrumb(
993+ "Detailed view",
994+ parent=machine_detail,
995+ needs=['machine_id', 'arch', 'build_number'],
996+)
997 @require_GET
998 def machine_raw_data(request, machine_id, arch, build_number):
999 machine = get_object_or_404(IdlePowerMachine, id=machine_id)
1000@@ -142,6 +166,12 @@
1001 'variant': details['image__variant'],
1002 'jenkins_url': details['jenkins_url'],
1003 'duration': details['duration'],
1004+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1005+ machine_raw_data,
1006+ arch=arch,
1007+ machine_id=machine_id,
1008+ build_number=build_number,
1009+ ),
1010 }
1011 return render_to_response(
1012 "idle_power/machine_raw_data.html",
1013
1014=== modified file 'memory/templates/memory/machine_details.html'
1015--- memory/templates/memory/machine_details.html 2013-04-11 17:31:16 +0000
1016+++ memory/templates/memory/machine_details.html 2013-05-03 22:59:28 +0000
1017@@ -10,26 +10,6 @@
1018 {% endblock extra_headers %}
1019 {% block content %}
1020
1021-<div id="breadcrumb">
1022- <span class="crumb">
1023- {% if result.image %}
1024- <a href="{% url memory_arch_overview arch %}">{{ arch }} - {{ result.image.variant }}</a>
1025- {% else %}
1026- <a href="{% url memory_arch_overview arch %}">{{ arch }} - {{ result.upgrade.variant }}</a>
1027- {% endif %}
1028- </span>
1029- <span class='crumb-sep'>
1030- >>
1031- </span>
1032- <span class="crumb">
1033- {% if result.image %}
1034- <a href="{% url machine_image_results arch result.machine.id result.image.variant result.name %}">{{ result.name }}</a>
1035- {% else %}
1036- <a href="{% url machine_upgrade_results arch result.machine.id result.upgrade.variant result.name %}">{{ result.name }}</a>
1037- {% endif %}
1038- </span>
1039-</div>
1040-
1041 <div class="grid_15">
1042 <h2>Memory consumption details for {{ result.name|upper }} on {{ result.machine.name }} using {{ arch }}</h2>
1043 </div>
1044
1045=== modified file 'memory/templates/memory/machine_results.html'
1046--- memory/templates/memory/machine_results.html 2013-04-03 17:43:27 +0000
1047+++ memory/templates/memory/machine_results.html 2013-05-03 22:59:28 +0000
1048@@ -7,11 +7,6 @@
1049 <script src="http://yui.yahooapis.com/3.6.0/build/yui/yui-min.js"></script>
1050 {% endblock extra_headers %}
1051 {% block content %}
1052-<div id="breadcrumb">
1053- <span class="crumb">
1054- <a href="{% url memory_arch_overview arch %}">{{ arch }} - {{ variant }}</a>
1055- </span>
1056-</div>
1057
1058 <div class="grid_15">
1059 <h2>Memory consumption results for {{ arch }} on {{ machine }}</h2>
1060
1061=== modified file 'memory/views.py'
1062--- memory/views.py 2013-04-10 19:03:16 +0000
1063+++ memory/views.py 2013-05-03 22:59:28 +0000
1064@@ -27,6 +27,13 @@
1065 redirect,
1066 )
1067
1068+from common.bread_crumbs import (
1069+ BreadCrumb,
1070+ BreadCrumbTrail,
1071+)
1072+
1073+from smoke.views import index
1074+
1075 from memory.models import (
1076 MemoryMachine,
1077 MemoryMetric,
1078@@ -79,6 +86,7 @@
1079 return metrics
1080
1081
1082+@BreadCrumb("Memory testing", parent=index)
1083 @require_GET
1084 def arch_overview(request, arch=None):
1085 arches = _get_memory_arches()
1086@@ -109,6 +117,7 @@
1087 'active': arch,
1088 'chart_size': chart_size,
1089 'image_types': image_types,
1090+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(arch_overview),
1091 }
1092
1093 return render_to_response('memory/arch_overview.html', data,
1094@@ -116,7 +125,7 @@
1095
1096
1097 @require_GET
1098-def _process_overview(request, arch, machine_id, upgrade=False):
1099+def _process_overview(request, arch, machine_id, data, upgrade=False):
1100 arches = _get_memory_arches()
1101
1102 machine = get_object_or_404(MemoryMachine, id=machine_id)
1103@@ -136,7 +145,7 @@
1104 if process is not None:
1105 processes = processes.filter(process=process)
1106
1107- data = {
1108+ data.update({
1109 'machine': machine,
1110 'processes': processes,
1111 'arch': arch,
1112@@ -145,22 +154,63 @@
1113 'chart_size': chart_size,
1114 'upgrade': upgrade,
1115 'type': "upgrade" if upgrade else "image",
1116- }
1117+ })
1118
1119 return render_to_response('memory/process_overview.html', data,
1120 RequestContext(request))
1121
1122
1123+@BreadCrumb(
1124+ "upgrade process overview",
1125+ parent=arch_overview,
1126+ needs=['arch', 'machine_id'],
1127+)
1128 @require_GET
1129 def upgrade_process_overview(request, arch, machine_id):
1130- return _process_overview(request, arch, machine_id, upgrade=True)
1131-
1132-
1133+ data = {
1134+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1135+ upgrade_process_overview,
1136+ arch=arch,
1137+ machine_id=machine_id,
1138+ ),
1139+ }
1140+ return _process_overview(
1141+ request,
1142+ arch,
1143+ machine_id,
1144+ upgrade=True,
1145+ data=data,
1146+ )
1147+
1148+
1149+@BreadCrumb(
1150+ "image process overview",
1151+ parent=arch_overview,
1152+ needs=['arch', 'machine_id'],
1153+)
1154 @require_GET
1155 def image_process_overview(request, arch, machine_id):
1156- return _process_overview(request, arch, machine_id, upgrade=False)
1157-
1158-
1159+ data = {
1160+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1161+ image_process_overview,
1162+ arch=arch,
1163+ machine_id=machine_id,
1164+ ),
1165+ }
1166+ return _process_overview(
1167+ request,
1168+ arch,
1169+ machine_id,
1170+ upgrade=False,
1171+ data=data,
1172+ )
1173+
1174+
1175+@BreadCrumb(
1176+ "Image results",
1177+ parent=arch_overview,
1178+ needs=['arch', 'machine_id', 'variant', 'metric'],
1179+)
1180 @require_GET
1181 def machine_image_results(request, arch, machine_id, variant, metric):
1182 machine = get_object_or_404(MemoryMachine, id=machine_id)
1183@@ -181,12 +231,24 @@
1184 'arch': arch,
1185 'table': table,
1186 'variant': variant,
1187+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1188+ machine_image_results,
1189+ arch=arch,
1190+ machine_id=machine_id,
1191+ variant=variant,
1192+ metric=metric,
1193+ ),
1194 }
1195
1196 return render_to_response('memory/machine_results.html', data,
1197 RequestContext(request))
1198
1199
1200+@BreadCrumb(
1201+ "Machine upgrade results",
1202+ parent=arch_overview,
1203+ needs=['arch', 'machine_id', 'variant', 'metric'],
1204+)
1205 @require_GET
1206 def machine_upgrade_results(request, arch, machine_id, variant, metric):
1207 machine = get_object_or_404(MemoryMachine, id=machine_id)
1208@@ -207,16 +269,27 @@
1209 'arch': arch,
1210 'table': table,
1211 'variant': variant,
1212+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1213+ machine_upgrade_results,
1214+ arch=arch,
1215+ machine_id=machine_id,
1216+ variant=variant,
1217+ metric=metric,
1218+ ),
1219 }
1220
1221 return render_to_response('memory/machine_results.html', data,
1222 RequestContext(request))
1223
1224
1225+@BreadCrumb(
1226+ "Details",
1227+ parent=machine_image_results,
1228+ needs=['arch', 'machine_id', 'result_id'],
1229+)
1230 @require_GET
1231 def machine_image_details(request, arch, machine_id, result_id):
1232 result = get_object_or_404(MemoryResult, id=result_id)
1233-
1234 details = result.memorydetail_set.all()
1235
1236 data = []
1237@@ -279,12 +352,25 @@
1238 'arch': arch,
1239 'table': table,
1240 'totals': totals,
1241+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1242+ machine_image_details,
1243+ arch=arch,
1244+ machine_id=machine_id,
1245+ result_id=result_id,
1246+ variant=result.image.variant,
1247+ metric=result.metric.name,
1248+ ),
1249 }
1250
1251 return render_to_response('memory/machine_details.html', data,
1252 RequestContext(request))
1253
1254
1255+@BreadCrumb(
1256+ "Machine upgrade details",
1257+ parent=arch_overview,
1258+ needs=['arch', 'machine_id', 'result_id'],
1259+)
1260 @require_GET
1261 def machine_upgrade_details(request, arch, machine_id, result_id):
1262 result = get_object_or_404(MemoryResult, id=result_id)
1263@@ -350,6 +436,12 @@
1264 'arch': arch,
1265 'table': table,
1266 'totals': totals,
1267+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1268+ machine_upgrade_details,
1269+ arch=arch,
1270+ machine_id=machine_id,
1271+ result_id=result_id,
1272+ ),
1273 }
1274
1275 return render_to_response('memory/machine_details.html', data,
1276
1277=== modified file 'power/urls.py'
1278--- power/urls.py 2013-04-16 20:47:20 +0000
1279+++ power/urls.py 2013-05-03 22:59:28 +0000
1280@@ -20,7 +20,7 @@
1281
1282 urlpatterns = patterns(
1283 'power.views',
1284- url(r'^$', 'arch_overview', name='power_arch'),
1285+ url(r'^$', 'arch_overview', name='power_overview'),
1286 url(
1287 r'^arch/(?P<arch>\w+)/$',
1288 'arch_overview',
1289
1290=== modified file 'power/views.py'
1291--- power/views.py 2013-03-21 18:59:36 +0000
1292+++ power/views.py 2013-05-03 22:59:28 +0000
1293@@ -24,6 +24,12 @@
1294 redirect,
1295 )
1296
1297+from common.bread_crumbs import (
1298+ BreadCrumb,
1299+ BreadCrumbTrail,
1300+)
1301+
1302+from smoke.views import index
1303 from power.models import (
1304 PowerMachine,
1305 PowerMetric,
1306@@ -58,21 +64,56 @@
1307 return metrics
1308
1309
1310+@BreadCrumb("Power testing", parent=index)
1311 @require_GET
1312 def overview(request):
1313- machines = PowerMachine.objects.filter(publish=True)
1314-
1315- data = {
1316- 'machines': machines,
1317- }
1318-
1319- return render_to_response(
1320- 'power/overview.html',
1321- data,
1322- RequestContext(request)
1323- )
1324-
1325-
1326+ data = {
1327+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(overview),
1328+ }
1329+
1330+ return _arch_overview(request, data=data, add_url='hardware/')
1331+
1332+
1333+@BreadCrumb("Hardware measurement", parent=overview)
1334+@require_GET
1335+def arch_overview(request, arch=None):
1336+ data = {
1337+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(arch_overview),
1338+ }
1339+ return _arch_overview(request, arch, data)
1340+
1341+
1342+@require_GET
1343+def _arch_overview(request, arch=None, data={}, add_url=''):
1344+ arches = _get_power_arches()
1345+
1346+ chart_size = request.GET.get('size', '')
1347+
1348+ if arch is None:
1349+ return redirect(
1350+ '{}arch/{}'.format(
1351+ add_url, arches[0] if len(arches) > 0 else "amd64"
1352+ )
1353+ )
1354+
1355+ result = PowerMetric.objects.filter(image__arch=arch)
1356+ result = result.distinct('machine__name')
1357+ arches = _get_power_arches()
1358+ metrics = _get_metrics(arch=arch)
1359+ data.update({
1360+ 'result': result,
1361+ 'arch': arch,
1362+ 'arches': arches,
1363+ 'active': arch,
1364+ 'metrics': metrics,
1365+ 'chart_size': chart_size,
1366+ })
1367+
1368+ return render_to_response('power/arch_overview.html', data,
1369+ RequestContext(request))
1370+
1371+
1372+@BreadCrumb("Power usage", parent=arch_overview, needs=['machine_id', 'arch'])
1373 @require_GET
1374 def machine_detail(request, machine_id, arch):
1375 machine = get_object_or_404(PowerMachine, id=machine_id)
1376@@ -103,36 +144,12 @@
1377 data = {
1378 'machine': machine,
1379 'table': table,
1380+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1381+ machine_detail,
1382+ machine_id=machine_id,
1383+ arch=arch,
1384+ ),
1385 }
1386
1387 return render_to_response('power/machine_detail.html', data,
1388 RequestContext(request))
1389-
1390-
1391-@require_GET
1392-def arch_overview(request, arch=None):
1393- arches = _get_power_arches()
1394-
1395- chart_size = request.GET.get('size', '')
1396-
1397- if arch is None:
1398- return redirect(
1399- 'arch/{}'.format(arches[0] if len(arches) > 0 else "amd64")
1400- )
1401-
1402- result = PowerMetric.objects.filter(image__arch=arch)
1403- result = result.distinct('machine__name')
1404- arches = _get_power_arches()
1405- metrics = _get_metrics(arch=arch)
1406-
1407- data = {
1408- 'result': result,
1409- 'arch': arch,
1410- 'arches': arches,
1411- 'active': arch,
1412- 'metrics': metrics,
1413- 'chart_size': chart_size,
1414- }
1415-
1416- return render_to_response('power/arch_overview.html', data,
1417- RequestContext(request))
1418
1419=== modified file 'qa_dashboard/urls.py'
1420--- qa_dashboard/urls.py 2013-05-02 16:11:38 +0000
1421+++ qa_dashboard/urls.py 2013-05-03 22:59:28 +0000
1422@@ -26,6 +26,7 @@
1423
1424 urlpatterns = patterns(
1425 '',
1426+ url(r'^power/$', 'power.views.overview', name='power_overview'),
1427 url(r'^power/hardware/', include('power.urls')),
1428 url(r'^api/power/hardware/', include('power.urls_api')),
1429 url(r'^power/idle/', include('idle_power.urls')),
1430@@ -86,11 +87,10 @@
1431
1432 urlpatterns += patterns(
1433 'bootspeed.views',
1434- url(r'^bootspeed/$', 'arch_overview', name='bootspeed_arch'),
1435 url(
1436- r'^bootspeed/arch/(?P<arch>\w+)/$',
1437+ r'^bootspeed/(?:arch/(?P<arch>\w+)/)?$',
1438 'arch_overview',
1439- name='bootspeed_arch_overview'
1440+ name='bootspeed_arch',
1441 ),
1442 url(
1443 r'^bootspeed/machine/(?P<machine_id>\d+)/(?P<arch>\w+)/$',
1444
1445=== modified file 'smoke/templates/smoke/build_overview.html'
1446--- smoke/templates/smoke/build_overview.html 2013-04-29 23:27:29 +0000
1447+++ smoke/templates/smoke/build_overview.html 2013-05-03 22:59:28 +0000
1448@@ -15,20 +15,6 @@
1449 //]]>
1450 </script>
1451 <div class='grid_15'>
1452- <div id='breadcrumb'>
1453- <span class='crumb'>
1454- <a href='{% url smoke_overview release %}flat/'>
1455- Smoke Testing
1456- {{release|capfirst}}
1457- </a>
1458- </span>
1459- <span class='crumb-sep'>
1460- >>
1461- </span>
1462- <span class='crumb'>
1463- Image {{run.build_no}}
1464- </span>
1465- </div>
1466 <h2>
1467 Summary of smoke testing for Ubuntu image {{run.build_no}}
1468 </h2>
1469
1470=== modified file 'smoke/templates/smoke/result_logs.html'
1471--- smoke/templates/smoke/result_logs.html 2013-04-02 21:09:05 +0000
1472+++ smoke/templates/smoke/result_logs.html 2013-05-03 22:59:28 +0000
1473@@ -14,31 +14,6 @@
1474 //]]>
1475 </script>
1476 <div class='grid_15'>
1477- <div id='breadcrumb'>
1478- <span class='crumb'>
1479- <a href='{% url smoke_overview release %}flat/'>
1480- Smoke testing
1481- {{ release|capfirst }}
1482- </a>
1483- </span>
1484- <span class='crumb-sep'>
1485- >>
1486- </span>
1487- <span class='crumb'>
1488- <a href='{% url build_overview release image.run.id %}'>
1489- Image {{image.run.build_no}}
1490- </a>
1491- </span>
1492- <span class='crumb-sep'>
1493- >>
1494- </span>
1495- <span class='crumb'>
1496- Result logs for {{image.run.flavor}}-{{image.variant}}-{{image.arch}} {{result.name}}
1497- </span>
1498- <span class='crumb-sep'>
1499- Logs
1500- </span>
1501- </div>
1502 <h2>
1503 Result Logs
1504 </h2>
1505
1506=== modified file 'smoke/templates/smoke/results.html'
1507--- smoke/templates/smoke/results.html 2013-04-29 23:27:29 +0000
1508+++ smoke/templates/smoke/results.html 2013-05-03 22:59:28 +0000
1509@@ -15,28 +15,6 @@
1510 //]]>
1511 </script>
1512 <div class='grid_15'>
1513- <div id='breadcrumb'>
1514- <span class='crumb'>
1515- <a href='{% url smoke_overview release %}flat/'>
1516- Smoke testing
1517- {{ release|capfirst }}
1518- </a>
1519- </span>
1520- <span class='crumb-sep'>
1521- >>
1522- </span>
1523- <span class='crumb'>
1524- <a href='{% url build_overview release image.run.id %}'>
1525- Image {{image.run.build_no}}
1526- </a>
1527- </span>
1528- <span class='crumb-sep'>
1529- >>
1530- </span>
1531- <span class='crumb'>
1532- Smoke testing results for {{image.run.build_no}} {{image.run.flavor}}-{{image.variant}}-{{image.arch}}
1533- </span>
1534- </div>
1535 <h2>
1536 Results
1537 </h2>
1538
1539=== modified file 'smoke/views.py'
1540--- smoke/views.py 2013-05-02 16:11:38 +0000
1541+++ smoke/views.py 2013-05-03 22:59:28 +0000
1542@@ -18,6 +18,11 @@
1543 from django.template import RequestContext
1544 from django.views.decorators.http import require_GET
1545
1546+from common.bread_crumbs import (
1547+ BreadCrumb,
1548+ BreadCrumbTrail,
1549+)
1550+
1551 from smoke.models import (
1552 Build,
1553 Result,
1554@@ -35,6 +40,7 @@
1555 return releases
1556
1557
1558+@BreadCrumb("QA Dashboard")
1559 @require_GET
1560 def index(request):
1561 release = 'saucy'
1562@@ -43,10 +49,12 @@
1563 'releases': _get_releases(),
1564 'is_flat': True,
1565 'active': release,
1566+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(index),
1567 }
1568 return _process_flat_smoke_overview(request, release, data, limit=100)
1569
1570
1571+@BreadCrumb("{release} smoke testing", parent=index, needs=['release'])
1572 @require_GET
1573 def overview(request, release, is_flat=False):
1574 data = {
1575@@ -54,6 +62,10 @@
1576 'releases': _get_releases(),
1577 'is_flat': False,
1578 'active': release,
1579+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1580+ overview,
1581+ release=release,
1582+ )
1583 }
1584
1585 if request.GET.get('flat') is not None:
1586@@ -100,6 +112,11 @@
1587 )
1588
1589
1590+@BreadCrumb(
1591+ "Image {build_number}",
1592+ parent=overview,
1593+ needs=['release', 'run_id'],
1594+)
1595 @require_GET
1596 def build_overview(request, release, run_id):
1597 try:
1598@@ -109,13 +126,29 @@
1599
1600 # Again, eager loading of associations two levels deeper would be great.
1601 builds = run.build_set.all().exclude(publish=False)
1602+ data = {
1603+ 'release': release,
1604+ 'run': run,
1605+ 'builds': builds,
1606+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1607+ build_overview,
1608+ release=release,
1609+ build_number=run.build_no,
1610+ run_id=run_id,
1611+ ),
1612+ }
1613 return render_to_response(
1614 'smoke/build_overview.html',
1615- locals(),
1616- RequestContext(request)
1617+ data,
1618+ RequestContext(request),
1619 )
1620
1621
1622+@BreadCrumb(
1623+ "{build_number} results",
1624+ parent=overview,
1625+ needs=['release', 'run_id', 'image_id'],
1626+)
1627 @require_GET
1628 def results(request, release, run_id, image_id):
1629 try:
1630@@ -124,15 +157,31 @@
1631 ).select_related().get(id=image_id)
1632 except Build.DoesNotExist:
1633 raise Http404
1634-
1635- results = image.result_set.all().exclude(publish=False)
1636+ image_results = image.result_set.all().exclude(publish=False)
1637+ data = {
1638+ 'release': release,
1639+ 'image': image,
1640+ 'results': image_results,
1641+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1642+ results,
1643+ release=release,
1644+ run_id=run_id,
1645+ image_id=image_id,
1646+ build_number=image.run.build_no,
1647+ )
1648+ }
1649 return render_to_response(
1650 'smoke/results.html',
1651- locals(),
1652+ data,
1653 RequestContext(request)
1654 )
1655
1656
1657+@BreadCrumb(
1658+ "logs",
1659+ parent=results,
1660+ needs=['release', 'run_id', 'image_id', 'result_id'],
1661+)
1662 @require_GET
1663 def results_logs(request, release, run_id, image_id, result_id):
1664 try:
1665@@ -145,8 +194,22 @@
1666
1667 logs = result.resultlog_set.all().exclude(publish=False)
1668
1669+ data = {
1670+ 'release': release,
1671+ 'image': image,
1672+ 'result': result,
1673+ 'logs': logs,
1674+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1675+ results_logs,
1676+ release=release,
1677+ run_id=run_id,
1678+ image_id=image_id,
1679+ result_id=result_id,
1680+ build_number=image.run.build_no,
1681+ )
1682+ }
1683 return render_to_response(
1684 'smoke/result_logs.html',
1685- locals(),
1686+ data,
1687 RequestContext(request)
1688 )
1689
1690=== modified file 'sru/templates/sru/overview_kernel.html'
1691--- sru/templates/sru/overview_kernel.html 2013-04-29 23:27:29 +0000
1692+++ sru/templates/sru/overview_kernel.html 2013-05-03 22:59:28 +0000
1693@@ -15,19 +15,6 @@
1694 //]]>
1695 </script>
1696 <div class='grid_15'>
1697- <div id='breadcrumb'>
1698- <span class='crumb'>
1699- <a href='{% url sru_overview release %}'>
1700- SRU testing for {{release}}
1701- </a>
1702- </span>
1703- <span class='crumb-sep'>
1704- >>
1705- </span>
1706- <span class='crumb'>
1707- Kernel {{kernel_version}}
1708- </span>
1709- </div>
1710 <h3>
1711 Summary of SRU testing for kernel {{kernel_version}}
1712 </h3>
1713
1714=== modified file 'sru/views.py'
1715--- sru/views.py 2013-05-01 16:12:06 +0000
1716+++ sru/views.py 2013-05-03 22:59:28 +0000
1717@@ -18,6 +18,12 @@
1718 from django.template import RequestContext
1719 from datetime import date, timedelta
1720
1721+from common.bread_crumbs import (
1722+ BreadCrumb,
1723+ BreadCrumbTrail,
1724+)
1725+
1726+from smoke.views import index
1727 from sru.models import Kernel
1728
1729
1730@@ -41,12 +47,14 @@
1731 return releases
1732
1733
1734+@BreadCrumb("SRU Testing", parent=index)
1735 @require_GET
1736 def overview(request, release=''):
1737 data = {
1738 'releases': _get_kernel_releases(),
1739 'active': release,
1740- 'show_unknown': True
1741+ 'show_unknown': True,
1742+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(overview),
1743 }
1744
1745 past_date = date.today() - timedelta(days=30)
1746@@ -76,6 +84,11 @@
1747 )
1748
1749
1750+@BreadCrumb(
1751+ "Summary of {kernel}",
1752+ parent=overview,
1753+ needs=['release', 'kernel'],
1754+)
1755 @require_GET
1756 def overview_kernel(request, release=None, kernel_version=None):
1757 kernels = Kernel.objects.filter(version=kernel_version).select_related()
1758@@ -83,7 +96,12 @@
1759 data = {
1760 'kernels': kernels,
1761 'kernel_version': kernel_version,
1762- 'release': release
1763+ 'release': release,
1764+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(
1765+ overview_kernel,
1766+ release=release,
1767+ kernel=kernel_version,
1768+ ),
1769 }
1770
1771 totals = {

Subscribers

People subscribed via source and target branches

to all changes: