Patch details:
- Mark any host that hasn't checked in the last 5 minutes as red
- Mark all other hosts as green
- Place all hosts on one page so one does not have to scroll thru and find any possibly downed hosts

--- ./koji-1.6.0/www/kojiweb/index.py    2010-12-16 16:13:17.000000000 -0500
+++ ./index.py.0    2011-12-01 13:53:54.169121521 -0500
@@ -1419,8 +1419,27 @@
         host['last_update'] = lastUpdate
 
     # Paginate after retrieving last update info so we can sort on it
-    kojiweb.util.paginateList(values, hosts, start, 'hosts', 'host', order)
-
+    #kojiweb.util.paginateList(values, hosts, start, 'hosts', 'host', order)
+  
+    if (order != None):
+        hosts.sort(kojiweb.util.sortByKeyFunc(order, False))
+    for host in hosts:
+        flag = 0
+        try:
+            utct = time.localtime()
+            utcs = int(time.strftime("%s", utct))
+            chkt = time.strptime(host['last_update'][0:19], "%Y-%m-%d %H:%M:%S")
+            chks = int(time.strftime("%s", chkt))
+            if ((utcs - (5 * 60)) >= chks):
+                flag = 1
+        except:
+            flag = 1
+        if (flag == 1):
+            host['name'] = ("<font color='#C00'>" + host['name'] + "</font>")
+        else:
+            host['name'] = ("<font color='#0C0'>" + host['name'] + "</font>")
+    kojiweb.util._populateValues(values, 'hosts', 'host', hosts, len(hosts), 0, len(hosts), len(hosts) + 5, order)
+  
     return _genHTML(req, 'hosts.chtml')
 
 def hostinfo(req, hostID=None, userID=None):

Patch details:
- Produce a list of the latest buildArch attempts made by a host on the "hostinfo" page (up to max 50)

--- ./koji-1.6.0/www/kojiweb/index.py    2010-12-16 16:13:17.000000000 -0500
+++ ./index.py.1    2011-12-01 13:54:22.748122221 -0500
@@ -1423,6 +1423,18 @@
 
     return _genHTML(req, 'hosts.chtml')
 
+def saferemo(inptstri, begistri, endistri, remoflag=True):
+    outpstri = inptstri
+    begilist = inptstri.split(begistri, 1)
+    if (len(begilist) > 1):
+        endilist = begilist[1].split(endistri, 1)
+        if (len(endilist) > 1):
+            if (remoflag):
+                outpstri = (begilist[0] + endilist[1])
+            else:
+                outpstri = (begistri + endilist[0] + endistri)
+    return outpstri
+
 def hostinfo(req, hostID=None, userID=None):
     values = _initValues(req, 'Host Info', 'hosts')
     server = _getServer(req)
@@ -1461,7 +1473,16 @@
     else:
         values['perms'] = []
    
-    return _genHTML(req, 'hostinfo.chtml')
+    hout = _genHTML(req, 'hostinfo.chtml')
+    hidn = hostID
+    tout = tasks(req, state='all', method='buildArch', view='flat', hostID=hidn)
+    tout = tout.replace("\t", "").replace("\r", "").replace("\n", "")
+    tout = saferemo(tout, "<form action=\"tasks\">", "</form>")
+    tout = saferemo(tout, "<table class=\"data-list\">", "</table>", remoflag=False)
+   
+    hout = hout.replace("<p id=\"footer\">", tout + "<p id=\"footer\">")
+   
+    return hout
 
 def hostedit(req, hostID):
     server = _getServer(req)

Patch details:
- Grab the last 5 buildArch statuses from Koji for each builder using ajax
- It will place that short build history beside each builder (sort of like a little "life-line")

--- ./koji-1.6.0/www/kojiweb/includes/header.chtml    2010-12-16 16:13:17.000000000 -0500
+++ ./header.chtml    2011-12-01 14:03:44.414136005 -0500
@@ -37,8 +37,95 @@
     <link rel="stylesheet" type="text/css" media="screen" title="Koji Style" href="/koji-static/koji.css"/>
     <link rel="alternate stylesheet" type="text/css" media="screen" title="Debug" href="/koji-static/debug.css"/>
     <link rel="alternate" type="application/rss+xml" title="Koji: recent builds" href="/koji/recentbuilds"/>
+    <script>
+        var hostxmld = null, hostlist = [];
+    function hostresp()
+    {
+        if (hostxmld.readyState == 4)
+        {
+            if (hostxmld.status == 200)
+        {
+            var x, machobjc, infostri = "", infolist = [];
+            var regxfail = /^(<a href="taskinfo\?taskID=[0-9]*" class="taskfailed" title=")failed(">)(.*)(<\/a>)$/;
+            var regxpass = /^(<a href="taskinfo\?taskID=[0-9]*" class="taskclosed" title=")closed(">)(.*)(<\/a>)$/;
+            var taskdata = hostxmld.responseText.replace(/<a /g, "\n<a ").replace(/<\/a>/g, "</a>\n");
+            var tasklist = taskdata.split("\n");
+            for (x = 0; x < tasklist.length; ++x)
+            {
+                machobjc = regxfail.exec(tasklist[x]);
+                        if (machobjc != null)
+                        {
+                            infolist.push([machobjc[1], machobjc[3], machobjc[2], "F", machobjc[4]]);
+                        }
+            machobjc = regxpass.exec(tasklist[x]);
+                if (machobjc != null)
+            {
+                infolist.push([machobjc[1], machobjc[3], machobjc[2], "C", machobjc[4]]);
+            }
+            }
+            for (x = 0; (x < infolist.length) && (x < 5); ++x)
+            {
+                        if (x != 0)
+            {
+                infostri += ".";
+            }
+                infostri += (infolist[x][0] + infolist[x][1] + infolist[x][2] + infolist[x][3] + infolist[x][4]);
+            }
+            if (infostri != "")
+            {
+                infostri = ("[ " + infostri + " ]");
+            }
+            hostlist[0][1].innerHTML = (hostlist[0][1].innerHTML + " " + infostri);
+        }
+        hostlist.shift();
+            hostxmld = null;
+        }
+    }
+    function hostrequ(hostnumb)
+    {
+        if (hostxmld != null)
+        {
+            return 0;
+        }
+        hostxmld = new XMLHttpRequest();
+        hostxmld.onreadystatechange = hostresp;
+        hostxmld.open("GET", "tasks?method=buildArch&state=all&view=flat&order=-id&hostID=" + hostnumb, true);
+        hostxmld.send();
+        return 1;
+    }
+    function hostloop()
+    {
+        if (hostlist.length > 0)
+        {
+            var hostiden = hostlist[0][0].innerHTML.replace(/[^0-9]/g, "");
+        hostrequ(hostiden);
+            setTimeout("hostloop();", 100);
+            }
+    }
+        function hostinfo()
+        {
+        if (location.href.match(/^.*\/hosts$/) || location.href.match(/^.*\/hosts\?[^\/]*$/))
+        {
+            var x, y;
+            var tabllist = document.getElementsByClassName("data-list");
+        for (x = 0; x < tabllist.length; ++x)
+        {
+            var rowslist = tabllist[x].getElementsByTagName("tr");
+            for (y = 0; y < rowslist.length; ++y)
+            {
+                if (rowslist[y].className.match(/^.*row-odd.*$/) || rowslist[y].className.match(/^.*row-even.*$/))
+            {
+                var colslist = rowslist[y].getElementsByTagName("td");
+                hostlist.push(colslist);
+            }
+            }
+        }
+        }
+        hostloop();
+    }
+    </script>
   </head>
-  <body id="$pageID">
+  <body id="$pageID" onload="hostinfo();">
 
     <div id="wrap">
       <div id="innerwrap">