=== modified file 'lava_scheduler_app/models.py'
--- lava_scheduler_app/models.py	2012-05-23 11:42:47 +0000
+++ lava_scheduler_app/models.py	2012-05-28 07:21:03 +0000
@@ -55,6 +55,10 @@
     health_check_job = models.TextField(
         null=True, blank=True, default=None, validators=[validate_job_json])
 
+    @models.permalink
+    def get_absolute_url(self):
+        return ("lava.scheduler.device_type.detail", [self.pk])
+
 
 class Device(models.Model):
     """

=== added file 'lava_scheduler_app/templates/lava_scheduler_app/alldevices.html'
--- lava_scheduler_app/templates/lava_scheduler_app/alldevices.html	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/templates/lava_scheduler_app/alldevices.html	2012-05-28 09:40:20 +0000
@@ -0,0 +1,11 @@
+{% extends "lava_scheduler_app/_content.html" %}
+
+{% load django_tables2 %}
+
+{% block content %}
+
+<h2>All Devices</h2>
+
+{% render_table devices_table %}
+
+{% endblock %}

=== added file 'lava_scheduler_app/templates/lava_scheduler_app/device_type.html'
--- lava_scheduler_app/templates/lava_scheduler_app/device_type.html	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/templates/lava_scheduler_app/device_type.html	2012-05-24 09:12:26 +0000
@@ -0,0 +1,16 @@
+{% extends "lava_scheduler_app/_content.html" %}
+
+{% load django_tables2 %}
+
+{% block content %}
+<h2>{{device_type}} Status</h2>
+
+{{running_jobs_num}} jobs running / {{queued_jobs_num}} jobs queued
+
+<h3>Health Job Summary</h3>
+{% render_table health_job_summary_table %}
+
+<h3>Devices Overview</h3>
+{% render_table devices_table_no_dt %}
+
+{% endblock %}

=== modified file 'lava_scheduler_app/templates/lava_scheduler_app/index.html'
--- lava_scheduler_app/templates/lava_scheduler_app/index.html	2012-03-05 00:23:16 +0000
+++ lava_scheduler_app/templates/lava_scheduler_app/index.html	2012-05-28 09:40:20 +0000
@@ -3,11 +3,16 @@
 {% load django_tables2 %}
 
 {% block content %}
-<h2>Devices</h2>
-
-{% render_table devices_table %}
-
-<a href="{% url lava.scheduler.labhealth %}">All Devices Health</a>
+
+<h2>Device Type Overview</h2>
+<p>{{device_status}} devices online</p>
+<p>{{health_check_status}} health check jobs passed in 24 hours</p>
+
+{% render_table device_type_table %}
+
+<p><a href="{% url lava.scheduler.alldevices %}">All Devices</a></p>
+
+<p><a href="{% url lava.scheduler.labhealth %}">All Devices Health</a></p>
 
 <h2>Active Jobs</h2>
 

=== modified file 'lava_scheduler_app/urls.py'
--- lava_scheduler_app/urls.py	2012-05-23 11:42:47 +0000
+++ lava_scheduler_app/urls.py	2012-05-28 09:40:20 +0000
@@ -18,6 +18,18 @@
     url(r'^alljobs_json$',
         'alljobs_json',
         name='lava.scheduler.job.list_json'),
+    url(r'^device_type/(?P<pk>[-_a-zA-Z0-9]+)$',
+        'device_type_detail',
+        name='lava.scheduler.device_type.detail'),
+    url(r'^device_type_json$',
+        'device_type_json',
+        name='lava.scheduler.device_type.device_type_json'),
+    url(r'^device_type/(?P<pk>[-_a-zA-Z0-9]+)/index_nodt_devices_json$',
+        'index_nodt_devices_json',
+        name='lava.scheduler.device_type.index_nodt_devices_json'),
+    url(r'^alldevices$',
+        'device_list',
+        name='lava.scheduler.alldevices'),
     url(r'^device/(?P<pk>[-_a-zA-Z0-9]+)$',
         'device_detail',
         name='lava.scheduler.device.detail'),

=== modified file 'lava_scheduler_app/views.py'
--- lava_scheduler_app/views.py	2012-05-23 11:42:47 +0000
+++ lava_scheduler_app/views.py	2012-05-28 09:40:20 +0000
@@ -3,6 +3,8 @@
 import os
 import simplejson
 import StringIO
+import datetime
+from dateutil.relativedelta import relativedelta
 
 from django.conf import settings
 from django.core.urlresolvers import reverse
@@ -38,13 +40,14 @@
     getDispatcherLogMessages
     )
 from lava_scheduler_app.models import (
+    Tag,
     Device,
+    DeviceType,
     DeviceStateTransition,
     TestJob,
     )
 
 
-
 def post_only(func):
     def decorated(request, *args, **kwargs):
         if request.method != 'POST':
@@ -69,6 +72,7 @@
             record.get_absolute_url(),
             escape(record.pk)))
 
+
 class IDLinkColumn(Column):
 
     def __init__(self, verbose_name="ID", **kw):
@@ -97,6 +101,7 @@
             }).all()
 
 
+
 class JobTable(DataTablesTable):
 
     def render_device(self, record):
@@ -157,12 +162,23 @@
 def index_devices_json(request):
     return DeviceTable.json(request)
 
+def health_jobs_in_hr(hr=-24):
+    return TestJob.objects.filter(health_check=True,
+           start_time__gte=(datetime.datetime.now() + relativedelta(hours=hr)))
 
 @BreadCrumb("Scheduler", parent=lava_index)
 def index(request):
     return render_to_response(
         "lava_scheduler_app/index.html",
         {
+            'device_status': "%s/%s" % (
+                Device.objects.filter(
+                    status__in=[Device.IDLE, Device.RUNNING]).count(),
+                    Device.objects.count()),
+            'health_check_status': "%s/%s" % (
+                health_jobs_in_hr().filter(status=TestJob.COMPLETE).count(),
+                health_jobs_in_hr().count()),
+            'device_type_table': DeviceTypeTable('devicetype', reverse(device_type_json)),
             'devices_table': DeviceTable('devices', reverse(index_devices_json)),
             'active_jobs_table': IndexJobTable(
                 'active_jobs', reverse(index_active_jobs_json)),
@@ -170,11 +186,107 @@
         },
         RequestContext(request))
 
+@BreadCrumb("All Devices", parent=index)
+def device_list(request):
+    return render_to_response(
+        "lava_scheduler_app/alldevices.html",
+        {
+            'devices_table': DeviceTable('devices', reverse(index_devices_json)),
+            'bread_crumb_trail': BreadCrumbTrail.leading_to(device_list),
+        },
+        RequestContext(request))
 
 def get_restricted_job(user, pk):
     return get_object_or_404(
         TestJob.objects.accessible_by_principal(user), pk=pk)
 
+class DeviceTypeTable(DataTablesTable):
+
+    def get_queryset(self):
+        return DeviceType.objects.all()
+
+    def render_status(self, record):
+        idle_num = Device.objects.filter(device_type=record.name,
+                status=Device.IDLE).count()
+        offline_num = Device.objects.filter(device_type=record.name,
+                status__in=[Device.OFFLINE, Device.OFFLINING]).count()
+        running_num = Device.objects.filter(device_type=record.name,
+                status=Device.RUNNING).count()
+        return "%s idle, %s offline, %s busy" % (idle_num, offline_num,
+                running_num)
+
+    name = IDLinkColumn("name")
+    status = Column()
+
+    searchable_columns = ['name']
+
+
+class HealthJobSummaryTable(DataTablesTable):
+    """
+    The Table will return 1 day, 1 week, 1 month offset health job count.
+    The value is defined when table instance is created in device_type_detail()
+    """
+
+    def render_name(self, record):
+        matrix = {-24:"24hours", -24*7:"Week", -24*7*30:"Month"}
+        return matrix[record]
+
+    def render_Complete(self, record):
+        device_type = self.params[0]
+        num = health_jobs_in_hr(record).filter(
+                actual_device__in=Device.objects.filter(
+                device_type=device_type), status=TestJob.COMPLETE).count()
+        return num
+
+    def render_Failed(self, record):
+        device_type = self.params[0]
+        num = health_jobs_in_hr(record).filter(
+                actual_device__in=Device.objects.filter(
+                device_type=device_type), status=TestJob.INCOMPLETE).count()
+        return num
+
+    name = Column()
+    Complete = Column()
+    Failed = Column()
+
+def device_type_json(request):
+    return DeviceTypeTable.json(request)
+
+class NoDTDeviceTable(DeviceTable):
+    def get_queryset(self, device_type):
+        return Device.objects.filter(device_type=device_type)
+
+    class Meta:
+        exclude = ('device_type',)
+
+def index_nodt_devices_json(request, pk):
+    device_type = get_object_or_404(DeviceType, pk=pk)
+    return NoDTDeviceTable.json(request, params=(device_type,))
+
+@BreadCrumb("Device Type {pk}", parent=index, needs=['pk'])
+def device_type_detail(request, pk):
+    dt = get_object_or_404(DeviceType, pk=pk)
+    return render_to_response(
+        "lava_scheduler_app/device_type.html",
+        {
+            'device_type': dt,
+            'running_jobs_num': TestJob.objects.filter(
+                actual_device__in=Device.objects.filter(device_type=dt),
+                status=TestJob.RUNNING).count(),
+            # Fix me: doesn't count actual_device not set but requested
+            # device type jobs.
+            'queued_jobs_num': TestJob.objects.filter(
+                actual_device__in=Device.objects.filter(device_type=dt),
+                status=TestJob.SUBMITTED).count(),
+            # data return 1 day, 1 week, 1 month offset
+            'health_job_summary_table': HealthJobSummaryTable(
+                'device_type', params=(dt,), data=[-24, -24*7, -24*7*30]),
+            'devices_table_no_dt': NoDTDeviceTable('devices',
+                reverse(index_nodt_devices_json, kwargs=dict(pk=pk)), params=(dt,)),
+            'bread_crumb_trail': BreadCrumbTrail.leading_to(device_type_detail, pk=pk),
+        },
+        RequestContext(request))
+
 
 class DeviceHealthTable(DataTablesTable):
 

