Merge lp:~qzhang/lava-scheduler/dt-page-revised into lp:lava-scheduler

Proposed by Spring Zhang
Status: Merged
Merged at revision: 173
Proposed branch: lp:~qzhang/lava-scheduler/dt-page-revised
Merge into: lp:lava-scheduler
Diff against target: 276 lines (+166/-6)
6 files modified
lava_scheduler_app/models.py (+4/-0)
lava_scheduler_app/templates/lava_scheduler_app/alldevices.html (+11/-0)
lava_scheduler_app/templates/lava_scheduler_app/device_type.html (+16/-0)
lava_scheduler_app/templates/lava_scheduler_app/index.html (+10/-5)
lava_scheduler_app/urls.py (+12/-0)
lava_scheduler_app/views.py (+113/-1)
To merge this branch: bzr merge lp:~qzhang/lava-scheduler/dt-page-revised
Reviewer Review Type Date Requested Status
Linaro Validation Team Pending
Review via email: mp+107598@code.launchpad.net

Description of the change

Previous to last mp, use data-backed-table lava-server branch code Table

To post a comment you must log in.
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Looks good, I'll merge it now.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lava_scheduler_app/models.py'
2--- lava_scheduler_app/models.py 2012-05-23 11:42:47 +0000
3+++ lava_scheduler_app/models.py 2012-05-28 09:42:18 +0000
4@@ -55,6 +55,10 @@
5 health_check_job = models.TextField(
6 null=True, blank=True, default=None, validators=[validate_job_json])
7
8+ @models.permalink
9+ def get_absolute_url(self):
10+ return ("lava.scheduler.device_type.detail", [self.pk])
11+
12
13 class Device(models.Model):
14 """
15
16=== added file 'lava_scheduler_app/templates/lava_scheduler_app/alldevices.html'
17--- lava_scheduler_app/templates/lava_scheduler_app/alldevices.html 1970-01-01 00:00:00 +0000
18+++ lava_scheduler_app/templates/lava_scheduler_app/alldevices.html 2012-05-28 09:42:18 +0000
19@@ -0,0 +1,11 @@
20+{% extends "lava_scheduler_app/_content.html" %}
21+
22+{% load django_tables2 %}
23+
24+{% block content %}
25+
26+<h2>All Devices</h2>
27+
28+{% render_table devices_table %}
29+
30+{% endblock %}
31
32=== added file 'lava_scheduler_app/templates/lava_scheduler_app/device_type.html'
33--- lava_scheduler_app/templates/lava_scheduler_app/device_type.html 1970-01-01 00:00:00 +0000
34+++ lava_scheduler_app/templates/lava_scheduler_app/device_type.html 2012-05-28 09:42:18 +0000
35@@ -0,0 +1,16 @@
36+{% extends "lava_scheduler_app/_content.html" %}
37+
38+{% load django_tables2 %}
39+
40+{% block content %}
41+<h2>{{device_type}} Status</h2>
42+
43+{{running_jobs_num}} jobs running / {{queued_jobs_num}} jobs queued
44+
45+<h3>Health Job Summary</h3>
46+{% render_table health_job_summary_table %}
47+
48+<h3>Devices Overview</h3>
49+{% render_table devices_table_no_dt %}
50+
51+{% endblock %}
52
53=== modified file 'lava_scheduler_app/templates/lava_scheduler_app/index.html'
54--- lava_scheduler_app/templates/lava_scheduler_app/index.html 2012-03-05 00:23:16 +0000
55+++ lava_scheduler_app/templates/lava_scheduler_app/index.html 2012-05-28 09:42:18 +0000
56@@ -3,11 +3,16 @@
57 {% load django_tables2 %}
58
59 {% block content %}
60-<h2>Devices</h2>
61-
62-{% render_table devices_table %}
63-
64-<a href="{% url lava.scheduler.labhealth %}">All Devices Health</a>
65+
66+<h2>Device Type Overview</h2>
67+<p>{{device_status}} devices online</p>
68+<p>{{health_check_status}} health check jobs passed in 24 hours</p>
69+
70+{% render_table device_type_table %}
71+
72+<p><a href="{% url lava.scheduler.alldevices %}">All Devices</a></p>
73+
74+<p><a href="{% url lava.scheduler.labhealth %}">All Devices Health</a></p>
75
76 <h2>Active Jobs</h2>
77
78
79=== modified file 'lava_scheduler_app/urls.py'
80--- lava_scheduler_app/urls.py 2012-05-23 11:42:47 +0000
81+++ lava_scheduler_app/urls.py 2012-05-28 09:42:18 +0000
82@@ -18,6 +18,18 @@
83 url(r'^alljobs_json$',
84 'alljobs_json',
85 name='lava.scheduler.job.list_json'),
86+ url(r'^device_type/(?P<pk>[-_a-zA-Z0-9]+)$',
87+ 'device_type_detail',
88+ name='lava.scheduler.device_type.detail'),
89+ url(r'^device_type_json$',
90+ 'device_type_json',
91+ name='lava.scheduler.device_type.device_type_json'),
92+ url(r'^device_type/(?P<pk>[-_a-zA-Z0-9]+)/index_nodt_devices_json$',
93+ 'index_nodt_devices_json',
94+ name='lava.scheduler.device_type.index_nodt_devices_json'),
95+ url(r'^alldevices$',
96+ 'device_list',
97+ name='lava.scheduler.alldevices'),
98 url(r'^device/(?P<pk>[-_a-zA-Z0-9]+)$',
99 'device_detail',
100 name='lava.scheduler.device.detail'),
101
102=== modified file 'lava_scheduler_app/views.py'
103--- lava_scheduler_app/views.py 2012-05-23 11:42:47 +0000
104+++ lava_scheduler_app/views.py 2012-05-28 09:42:18 +0000
105@@ -3,6 +3,8 @@
106 import os
107 import simplejson
108 import StringIO
109+import datetime
110+from dateutil.relativedelta import relativedelta
111
112 from django.conf import settings
113 from django.core.urlresolvers import reverse
114@@ -38,13 +40,14 @@
115 getDispatcherLogMessages
116 )
117 from lava_scheduler_app.models import (
118+ Tag,
119 Device,
120+ DeviceType,
121 DeviceStateTransition,
122 TestJob,
123 )
124
125
126-
127 def post_only(func):
128 def decorated(request, *args, **kwargs):
129 if request.method != 'POST':
130@@ -69,6 +72,7 @@
131 record.get_absolute_url(),
132 escape(record.pk)))
133
134+
135 class IDLinkColumn(Column):
136
137 def __init__(self, verbose_name="ID", **kw):
138@@ -97,6 +101,7 @@
139 }).all()
140
141
142+
143 class JobTable(DataTablesTable):
144
145 def render_device(self, record):
146@@ -157,12 +162,23 @@
147 def index_devices_json(request):
148 return DeviceTable.json(request)
149
150+def health_jobs_in_hr(hr=-24):
151+ return TestJob.objects.filter(health_check=True,
152+ start_time__gte=(datetime.datetime.now() + relativedelta(hours=hr)))
153
154 @BreadCrumb("Scheduler", parent=lava_index)
155 def index(request):
156 return render_to_response(
157 "lava_scheduler_app/index.html",
158 {
159+ 'device_status': "%s/%s" % (
160+ Device.objects.filter(
161+ status__in=[Device.IDLE, Device.RUNNING]).count(),
162+ Device.objects.count()),
163+ 'health_check_status': "%s/%s" % (
164+ health_jobs_in_hr().filter(status=TestJob.COMPLETE).count(),
165+ health_jobs_in_hr().count()),
166+ 'device_type_table': DeviceTypeTable('devicetype', reverse(device_type_json)),
167 'devices_table': DeviceTable('devices', reverse(index_devices_json)),
168 'active_jobs_table': IndexJobTable(
169 'active_jobs', reverse(index_active_jobs_json)),
170@@ -170,11 +186,107 @@
171 },
172 RequestContext(request))
173
174+@BreadCrumb("All Devices", parent=index)
175+def device_list(request):
176+ return render_to_response(
177+ "lava_scheduler_app/alldevices.html",
178+ {
179+ 'devices_table': DeviceTable('devices', reverse(index_devices_json)),
180+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(device_list),
181+ },
182+ RequestContext(request))
183
184 def get_restricted_job(user, pk):
185 return get_object_or_404(
186 TestJob.objects.accessible_by_principal(user), pk=pk)
187
188+class DeviceTypeTable(DataTablesTable):
189+
190+ def get_queryset(self):
191+ return DeviceType.objects.all()
192+
193+ def render_status(self, record):
194+ idle_num = Device.objects.filter(device_type=record.name,
195+ status=Device.IDLE).count()
196+ offline_num = Device.objects.filter(device_type=record.name,
197+ status__in=[Device.OFFLINE, Device.OFFLINING]).count()
198+ running_num = Device.objects.filter(device_type=record.name,
199+ status=Device.RUNNING).count()
200+ return "%s idle, %s offline, %s busy" % (idle_num, offline_num,
201+ running_num)
202+
203+ name = IDLinkColumn("name")
204+ status = Column()
205+
206+ searchable_columns = ['name']
207+
208+
209+class HealthJobSummaryTable(DataTablesTable):
210+ """
211+ The Table will return 1 day, 1 week, 1 month offset health job count.
212+ The value is defined when table instance is created in device_type_detail()
213+ """
214+
215+ def render_name(self, record):
216+ matrix = {-24:"24hours", -24*7:"Week", -24*7*30:"Month"}
217+ return matrix[record]
218+
219+ def render_Complete(self, record):
220+ device_type = self.params[0]
221+ num = health_jobs_in_hr(record).filter(
222+ actual_device__in=Device.objects.filter(
223+ device_type=device_type), status=TestJob.COMPLETE).count()
224+ return num
225+
226+ def render_Failed(self, record):
227+ device_type = self.params[0]
228+ num = health_jobs_in_hr(record).filter(
229+ actual_device__in=Device.objects.filter(
230+ device_type=device_type), status=TestJob.INCOMPLETE).count()
231+ return num
232+
233+ name = Column()
234+ Complete = Column()
235+ Failed = Column()
236+
237+def device_type_json(request):
238+ return DeviceTypeTable.json(request)
239+
240+class NoDTDeviceTable(DeviceTable):
241+ def get_queryset(self, device_type):
242+ return Device.objects.filter(device_type=device_type)
243+
244+ class Meta:
245+ exclude = ('device_type',)
246+
247+def index_nodt_devices_json(request, pk):
248+ device_type = get_object_or_404(DeviceType, pk=pk)
249+ return NoDTDeviceTable.json(request, params=(device_type,))
250+
251+@BreadCrumb("Device Type {pk}", parent=index, needs=['pk'])
252+def device_type_detail(request, pk):
253+ dt = get_object_or_404(DeviceType, pk=pk)
254+ return render_to_response(
255+ "lava_scheduler_app/device_type.html",
256+ {
257+ 'device_type': dt,
258+ 'running_jobs_num': TestJob.objects.filter(
259+ actual_device__in=Device.objects.filter(device_type=dt),
260+ status=TestJob.RUNNING).count(),
261+ # Fix me: doesn't count actual_device not set but requested
262+ # device type jobs.
263+ 'queued_jobs_num': TestJob.objects.filter(
264+ actual_device__in=Device.objects.filter(device_type=dt),
265+ status=TestJob.SUBMITTED).count(),
266+ # data return 1 day, 1 week, 1 month offset
267+ 'health_job_summary_table': HealthJobSummaryTable(
268+ 'device_type', params=(dt,), data=[-24, -24*7, -24*7*30]),
269+ 'devices_table_no_dt': NoDTDeviceTable('devices',
270+ reverse(index_nodt_devices_json, kwargs=dict(pk=pk)), params=(dt,)),
271+ 'bread_crumb_trail': BreadCrumbTrail.leading_to(device_type_detail, pk=pk),
272+ },
273+ RequestContext(request))
274+
275
276 class DeviceHealthTable(DataTablesTable):
277

Subscribers

People subscribed via source and target branches