You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by jf...@apache.org on 2010/10/20 18:17:20 UTC

svn commit: r1025626 - in /incubator/vcl/trunk/web: .ht-inc/dashboard.php .ht-inc/states.php .ht-inc/statistics.php .ht-inc/utils.php css/dashboard.css js/code.js js/dashboard.js js/statistics.js

Author: jfthomps
Date: Wed Oct 20 16:17:19 2010
New Revision: 1025626

URL: http://svn.apache.org/viewvc?rev=1025626&view=rev
Log:
VCL-310 - remove jpgraph dependency
replaced all jpgraph code with dojox's charting api


VCL-399 - add a dashboard where admins can see current state of VCL system
added a new section to the site that is a page displaying various information about the site that gets updated with AJAX every n seconds; currently there are the following sections of information:
Current Status - some general info
Top 5 Images in Use
Top 5 Long Term Images in Use
Top Recent Computer Failures
Top Recent Image Failures
Block Allocation Status
Past 12 Hours of Active Reservations

statistics.php
-removed require_once code for jpgraph
-modified viewStatistics - when computing length of reservation, if $length ends up being < 0, set it to 0
-changed img references to jpgraphs to divs for dojox charts
-added AJgetStatData
-removed sendStatGraphDay
-modified getStatGraphDayData - removed labels from $data, added xlabels to $data, added maxy to $data
-removed statXaxisDayCallback
-removed sendStatGraphHour
-modified getStatGraphHourData - changed $data['points'] to be an array of data, added maxy to $data
-renamed statXaxisHourCallback to statHourFromatX
-removed sendStatGraphDayConUsers
-modified getStatGraphDayConUsersData - removed labels from $data, added xlabels to $data, added maxy to $data
-removed statXaxisDayConUsersCallback
-removed sendStatGraphConBladeUser
-modified getStatGraphConBladeUserData - removed labels from $data, added xlabels to $data, added maxy to $data
-removed statXaxisConBladeUserCallback

utils.php
-modified initGlobals - added dashboard to section that does require_once based on the mode
-modified getNavMenu - removed some old stuff left in there related to ecu $skin; added menu item for dashboard
-modified getDojoHTML - added sections for viewstats and dashboard modes

states.php
-added dashboard to entry actions
-added AJupdateDashboard and AJgetStatData to $noHTMLwrappers
-removed statgraphday, statgraphhour, statgraphdayconcuruser, and statgraphdayconcurblade
-added AJgetStatData
-added dashboard section with modes dashboard and AJupdateDashboard

js/code.js - modified errorHandler function reference - list of args were wrong; changed from 'type, error, data' to 'error, ioArgs'; changed args.dojoType to error.name (not sure if this is correct), changed alert to display better message

dashboard.php - initial add to source control
css/dashboard.css - initial add to source control
js/dashboard.js - initial add to source control
js/statistics.js - initial add to source control

Added:
    incubator/vcl/trunk/web/.ht-inc/dashboard.php
    incubator/vcl/trunk/web/css/dashboard.css
    incubator/vcl/trunk/web/js/dashboard.js
    incubator/vcl/trunk/web/js/statistics.js
Modified:
    incubator/vcl/trunk/web/.ht-inc/states.php
    incubator/vcl/trunk/web/.ht-inc/statistics.php
    incubator/vcl/trunk/web/.ht-inc/utils.php
    incubator/vcl/trunk/web/js/code.js

Added: incubator/vcl/trunk/web/.ht-inc/dashboard.php
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/web/.ht-inc/dashboard.php?rev=1025626&view=auto
==============================================================================
--- incubator/vcl/trunk/web/.ht-inc/dashboard.php (added)
+++ incubator/vcl/trunk/web/.ht-inc/dashboard.php Wed Oct 20 16:17:19 2010
@@ -0,0 +1,422 @@
+<?php
+/*
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+*/
+
+/**
+ * \file
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn dashboard()
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function dashboard() {
+	print "<h2>VCL Dashboard</h2>\n";
+	print "<table summary=\"\">\n";
+	print "<tr>\n";
+
+	# -------- left column ---------
+	print "<td valign=\"top\">\n";
+	print addWidget('status', 'Current Status');
+	print addWidget('topimages', 'Top 5 Images in Use', '(Reservations &lt; 24 hours long)');
+	print addWidget('toplongimages', 'Top 5 Long Term Images in Use', '(Reservations &gt; 24 hours long)');
+	print addWidget('topfailedcomputers', 'Top Recent Computer Failures', '(Failed in the last 5 days)');
+	print "</td>\n";
+	# -------- end left column ---------
+
+	# ---------- right column ---------
+	print "<td valign=\"top\">\n";
+	print addWidget('topfailed', 'Top Recent Image Failures', '(Failed in the last 5 days)');
+	print addWidget('blockallocation', 'Block Allocation Status');
+	print addLineChart('reschart', 'Past 12 Hours of Active Reservations');
+	print "</td>\n";
+	# -------- end right column --------
+
+	print "</tr>\n";
+	print "</table>\n";
+	$cont = addContinuationsEntry('AJupdateDashboard', array('val' => 0), 60, 1, 0);
+	print "<input type=\"hidden\" id=\"updatecont\" value=\"$cont\">\n";
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn 
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function AJupdateDashboard() {
+	$data = array();
+	$data['cont'] = addContinuationsEntry('AJupdateDashboard', array(), 60, 1, 0);
+	$data['status'] = getStatusData();
+	$data['topimages'] = getTopImageData();
+	$data['toplongimages'] = getTopLongImageData();
+	$data['topfailed'] = getTopFailedData();
+	$data['topfailedcomputers'] = getTopFailedComputersData();
+	$data['reschart'] = getActiveResChartData();
+	$data['blockallocation'] = getBlockAllocationData();
+	sendJSON($data);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn addWidget($id, $title)
+///
+/// \param 
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function addWidget($id, $title, $extra='') {
+	$txt  = "<div class=\"dashwidget\">\n";
+	$txt .= "<h3>$title</h3>\n";
+	if($extra != '')
+		$txt .= "<div class=\"extra\">$extra</div>\n";
+	$txt .= "<div id=\"$id\"></div>\n";
+	$txt .= "</div>\n";
+	return $txt;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn addLineChart($id)
+///
+/// \param 
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function addLineChart($id, $title) {
+	$txt  = "<div class=\"dashwidget\">\n";
+	$txt .= "<h3>$title</h3>\n";
+	$txt .= "<div dojoType=\"dojox.charting.widget.Chart2D\" id=\"$id\"\n";
+	$txt .= "     theme=\"dojox.charting.themes.ThreeD\"\n";
+	$txt .= "     style=\"width: 300px; height: 300px;\">\n";
+	$txt .= "<div class=\"axis\"\n";
+	$txt .= "     name=\"x\"\n";
+	$txt .= "     labelFunc=\"timestampToTime\"\n";
+	$txt .= "     maxLabelSize=\"35\"\n";
+	$txt .= "     rotation=\"-90\"\n";
+	$txt .= "     majorTickStep=\"4\"\n";
+	$txt .= "     minorTickStep=\"1\">\n";
+	$txt .= "     </div>\n";
+	$txt .= "<div class=\"axis\" name=\"y\" vertical=\"true\" includeZero=\"true\"></div>\n";
+	$txt .= "<div class=\"plot\" name=\"default\" type=\"Lines\" markers=\"true\"></div>\n";
+	$txt .= "<div class=\"plot\" name=\"grid\" type=\"Grid\" vMajorLines=\"false\"></div>\n";
+	$txt .= "<div class=\"series\" name=\"Main\" data=\"0\"></div>\n";
+	$txt .= "<div class=\"action\" type=\"Tooltip\"></div>\n";
+	$txt .= "<div class=\"action\" type=\"Magnify\"></div>\n";
+	$txt .= "</div>\n";
+	$txt .= "</div>\n";
+	return $txt;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getStatusData()
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function getStatusData() {
+	$data = array();
+	$data[] = array('key' => 'Active Reservations', 'val' => 0);
+	$data[] = array('key' => 'Online Computers', 'val' => 0, 'tooltip' => 'Computers in states available, reserved,<br>reloading, inuse, or timeout');
+	$data[] = array('key' => 'Failed Computers', 'val' => 0);
+	$reloadid = getUserlistID('vclreload@Local');
+	$query = "SELECT COUNT(id) "
+	       . "FROM request "
+	       . "WHERE userid != $reloadid AND "
+	       .       "stateid NOT IN (1, 5, 12) AND "
+	       .       "start < NOW() AND "
+			 .       "end > NOW()";
+	$qh = doQuery($query, 101);
+	if($row = mysql_fetch_row($qh))
+		$data[0]['val'] = $row[0];
+
+	$query = "SELECT COUNT(id) FROM computer WHERE stateid IN (2, 3, 6, 8, 11)";
+	$qh = doQuery($query, 101);
+	if($row = mysql_fetch_row($qh))
+		$data[1]['val'] = $row[0];
+
+	$query = "SELECT COUNT(id) FROM computer WHERE stateid = 5";
+	$qh = doQuery($query, 101);
+	if($row = mysql_fetch_row($qh))
+		$data[2]['val'] = $row[0];
+	return $data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn 
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function getTopImageData() {
+	$query = "SELECT COUNT(c.currentimageid) AS count, "
+	       .        "i.prettyname "
+	       . "FROM computer c, "
+	       .      "image i "
+	       . "WHERE c.currentimageid = i.id AND "
+			 .       "c.stateid = 8 "
+	       . "GROUP BY c.currentimageid "
+	       . "ORDER BY count DESC "
+	       . "LIMIT 5";
+	$query = "SELECT COUNT(rs.imageid) AS count, "
+	       .        "i.prettyname "
+	       . "FROM reservation rs, "
+	       .      "request rq, "
+	       .      "image i "
+	       . "WHERE rs.imageid = i.id AND "
+			 .       "rq.stateid = 8 AND "
+			 .       "rs.requestid = rq.id AND "
+			 .       "TIMESTAMPDIFF(HOUR, rq.start, rq.end) <= 24 "
+	       . "GROUP BY rs.imageid "
+	       . "ORDER BY count DESC "
+	       . "LIMIT 5";
+	$data = array();
+	$qh = doQuery($query, 101);
+	while($row = mysql_fetch_assoc($qh))
+		$data[] = $row;
+	return $data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn 
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function getTopLongImageData() {
+	$query = "SELECT COUNT(rs.imageid) AS count, "
+	       .        "i.prettyname, "
+	       .        "TIMESTAMPDIFF(HOUR, rq.start, rq.end) AS reslen "
+	       . "FROM reservation rs, "
+	       .      "request rq, "
+	       .      "image i "
+	       . "WHERE rs.imageid = i.id AND "
+			 .       "rq.stateid = 8 AND "
+			 .       "rs.requestid = rq.id "
+	       . "GROUP BY rs.imageid "
+	       . "HAVING reslen > 24 "
+	       . "ORDER BY count DESC "
+	       . "LIMIT 5";
+	$query = "SELECT COUNT(rs.imageid) AS count, "
+	       .        "i.prettyname "
+	       . "FROM reservation rs, "
+	       .      "request rq, "
+	       .      "image i "
+	       . "WHERE rs.imageid = i.id AND "
+			 .       "rq.stateid = 8 AND "
+			 .       "rs.requestid = rq.id AND "
+			 .       "TIMESTAMPDIFF(HOUR, rq.start, rq.end) > 24 "
+	       . "GROUP BY rs.imageid "
+	       . "ORDER BY count DESC "
+	       . "LIMIT 5";
+	$data = array();
+	$qh = doQuery($query, 101);
+	while($row = mysql_fetch_assoc($qh))
+		$data[] = $row;
+	return $data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getTopFailedData()
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function getTopFailedData() {
+	$query = "SELECT COUNT(l.imageid) AS count, "
+	       .        "i.prettyname "
+	       . "FROM log l, "
+	       .      "image i "
+	       . "WHERE l.imageid = i.id AND "
+	       .       "l.ending = 'failed' AND "
+	       .       "l.start > DATE_SUB(NOW(), INTERVAL 5 DAY) "
+	       . "GROUP BY l.imageid "
+	       . "ORDER BY count DESC "
+	       . "LIMIT 5";
+	$data = array();
+	$qh = doQuery($query, 101);
+	while($row = mysql_fetch_assoc($qh))
+		$data[] = $row;
+	return $data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getTopFailedComputersData()
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function getTopFailedComputersData() {
+	$query = "SELECT COUNT(s.computerid) AS count, "
+	       .        "c.hostname "
+	       . "FROM log l, "
+	       .      "sublog s, "
+	       .      "computer c "
+	       . "WHERE s.logid = l.id AND "
+	       .       "s.computerid = c.id AND "
+	       .       "l.ending = 'failed' AND "
+	       .       "l.start > DATE_SUB(NOW(), INTERVAL 5 DAY) "
+	       . "GROUP BY s.computerid "
+	       . "ORDER BY count DESC "
+	       . "LIMIT 5";
+	$data = array();
+	$qh = doQuery($query, 101);
+	while($row = mysql_fetch_assoc($qh))
+		$data[] = $row;
+	return $data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getActiveResChartData()
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function getActiveResChartData() {
+	$data = array();
+	$chartstart = unixFloor15(time() - (12 * 3600));
+	$chartend = $chartstart + (12 * 3600) + 900;
+	for($time = $chartstart, $i = 0; $time < $chartend; $time += 900, $i++) {
+		$fmttime = date('g:i a', $time);
+		$data["points"][$i] = array('x' => $i, 'y' => 0, 'value' => $i, 'text' => $fmttime);
+	}
+	$data['maxy'] = 0;
+	$reloadid = getUserlistID('vclreload@Local');
+	$query = "SELECT id, "
+	       .        "UNIX_TIMESTAMP(start) AS start, "
+	       .        "UNIX_TIMESTAMP(finalend) AS end "
+	       . "FROM log "
+	       . "WHERE start < NOW() AND "
+	       .       "finalend > DATE_SUB(NOW(), INTERVAL 12 HOUR) AND "
+	       .       "ending NOT IN ('failed', 'failedtest') AND "
+	       .       "wasavailable = 1 AND "
+	       .       "userid != $reloadid";
+	$qh = doQuery($query, 101);
+	while($row = mysql_fetch_assoc($qh)) {
+		for($binstart = $chartstart, $binend = $chartstart + 900, $binindex = 0; 
+		   $binend <= $chartend;
+		   $binstart += 900, $binend += 900, $binindex++) {
+			if($binend <= $row['start'])
+				continue;
+			elseif($row['start'] < $binend &&
+				$row['end'] > $binstart) {
+				$data["points"][$binindex]['y']++;
+			}
+			elseif($binstart >= $row['end'])
+				break;
+		}
+	}
+	for($time = $chartstart, $i = 0; $time < $chartend; $time += 900, $i++) {
+		if($data["points"][$i]['y'] > $data['maxy'])
+			$data['maxy'] = $data['points'][$i]['y'];
+		$data["points"][$i]['tooltip'] = "{$data['points'][$i]['text']}: {$data['points'][$i]['y']}";
+	}
+	return $data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getBlockAllocationData()
+///
+/// \param 
+///
+/// \return
+///
+/// \brief 
+///
+////////////////////////////////////////////////////////////////////////////////
+function getBlockAllocationData() {
+	# active block allocation
+	$query = "SELECT COUNT(id) "
+	       . "FROM blockTimes "
+	       . "WHERE skip = 0 AND "
+	       .       "start < NOW() AND "
+			 .       "end > NOW()";
+	$qh = doQuery($query, 101);
+	$row = mysql_fetch_row($qh);
+	$blockcount = $row[0];
+	# computers in blockComputers for active allocations
+	$query = "SELECT bc.computerid, "
+	       .        "c.stateid "
+	       . "FROM blockComputers bc "
+	       . "LEFT JOIN computer c ON (c.id = bc.computerid) "
+	       . "LEFT JOIN blockTimes bt ON (bt.id = bc.blockTimeid) "
+	       . "WHERE c.stateid IN (2, 3, 6, 8, 19) AND "
+	       .       "bt.start < NOW() AND "
+	       .       "bt.end > NOW()";
+	$qh = doQuery($query, 101);
+	$total = 0;
+	$used = 0;
+	while($row = mysql_fetch_assoc($qh)) {
+		$total++;
+		if($row['stateid'] == 3 || $row['stateid'] == 8)
+			$used++;
+	}
+	if($total)
+		$compused = sprintf('%d / %d (%0.2f %%)', $used, $total, ($used / $total * 100));
+	else
+		$compused = 0;
+	# number of computers that should be allocated
+	$query = "SELECT br.numMachines "
+	       . "FROM blockRequest br "
+	       . "LEFT JOIN blockTimes bt ON (bt.blockRequestid = br.id) "
+	       . "WHERE bt.start < NOW() AND "
+	       .       "bt.end > NOW() AND "
+	       .       "bt.skip = 0";
+	$alloc = 0;
+	$qh = doQuery($query, 101);
+	while($row = mysql_fetch_assoc($qh))
+		$alloc += $row['numMachines'];
+	if($alloc)
+		$failed = sprintf('%d / %d (%0.2f %%)', ($alloc - $total), $alloc, (($alloc - $total) / $alloc * 100));
+	else
+		$failed = 0;
+	$data = array();
+	$data[] = array('title' => 'Active Block Allocations', 'val' => $blockcount);
+	$data[] = array('title' => 'Block Computer Usage', 'val' => $compused);
+	$data[] = array('title' => 'Failed Block Computers', 'val' => $failed);
+	return $data;
+}
+?>

Modified: incubator/vcl/trunk/web/.ht-inc/states.php
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/web/.ht-inc/states.php?rev=1025626&r1=1025625&r2=1025626&view=diff
==============================================================================
--- incubator/vcl/trunk/web/.ht-inc/states.php (original)
+++ incubator/vcl/trunk/web/.ht-inc/states.php Wed Oct 20 16:17:19 2010
@@ -55,6 +55,7 @@ $actions["entry"] = array('main',
                           'continuationsError',
                           'requestBlockAllocation',
                           'siteMaintenance',
+                          'dashboard',
 );
 
 $noHTMLwrappers = array('sendRDPfile',
@@ -142,6 +143,8 @@ $noHTMLwrappers = array('sendRDPfile',
                         'AJgetScheduleTimesData',
                         'AJsaveScheduleTimes',
                         'AJvalidateUserid',
+                        'AJupdateDashboard',
+                        'AJgetStatData',
 );
 
 # main
@@ -555,19 +558,12 @@ $actions['pages']['userLookup'] = "userL
 $actions['pages']['submitUserLookup'] = "userLookup";
 
 # statistics
-# TODO might need the statgraph modes to be entry modes
 $actions['mode']['selectstats'] = "selectStatistics"; # entry
 $actions['mode']['viewstats'] = "viewStatistics";
-$actions['mode']['statgraphday'] = "sendStatGraphDay";
-$actions['mode']['statgraphhour'] = "sendStatGraphHour";
-$actions['mode']['statgraphdayconcuruser'] = "sendStatGraphDayConUsers";
-$actions['mode']['statgraphdayconcurblade'] = "sendStatGraphConBladeUser";
+$actions['mode']['AJgetStatData'] = "AJgetStatData";
 $actions['pages']['selectstats'] = "statistics";
 $actions['pages']['viewstats'] = "statistics";
-$actions['pages']['statgraphday'] = "statistics";
-$actions['pages']['statgraphhour'] = "statistics";
-$actions['pages']['statgraphdayconcuruser'] = "statistics";
-$actions['pages']['statgraphdayconcurblade'] = "statistics";
+$actions['pages']['AJgetStatData'] = "statistics";
 
 # help
 $actions['mode']['helpform'] = "printHelpForm"; # entry
@@ -635,6 +631,12 @@ $actions['pages']['AJgetDelSiteMaintenan
 $actions['pages']['AJeditSiteMaintenance'] = "sitemaintenance";
 $actions['pages']['AJdeleteSiteMaintenance'] = "sitemaintenance";
 
+# dashboard
+$actions['mode']['dashboard'] = "dashboard";
+$actions['mode']['AJupdateDashboard'] = "AJupdateDashboard";
+$actions['pages']['dashboard'] = "dashboard";
+$actions['pages']['AJupdateDashboard'] = "dashboard";
+
 # RPC
 $actions['mode']['xmlrpccall'] = "xmlrpccall";
 $actions['mode']['xmlrpcaffiliations'] = "xmlrpcgetaffiliations";

Modified: incubator/vcl/trunk/web/.ht-inc/statistics.php
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/web/.ht-inc/statistics.php?rev=1025626&r1=1025625&r2=1025626&view=diff
==============================================================================
--- incubator/vcl/trunk/web/.ht-inc/statistics.php (original)
+++ incubator/vcl/trunk/web/.ht-inc/statistics.php Wed Oct 20 16:17:19 2010
@@ -16,21 +16,6 @@
   limitations under the License.
 */
 
-if($phpVer == 5) {
-	if(is_dir(".ht-inc/jpgraph")) {
-		require_once(".ht-inc/jpgraph/jpgraph.php");
-		require_once(".ht-inc/jpgraph/jpgraph_bar.php");
-		require_once(".ht-inc/jpgraph/jpgraph_line.php");
-	}
-}
-else {
-	if(is_dir(".ht-inc/jpgraph.old")) {
-		require_once(".ht-inc/jpgraph.old/jpgraph.php");
-		require_once(".ht-inc/jpgraph.old/jpgraph_bar.php");
-		require_once(".ht-inc/jpgraph.old/jpgraph_line.php");
-	}
-}
-
 /**
  * \file
  */
@@ -289,6 +274,8 @@ function viewStatistics() {
 
 		# lengths
 		$length = $row["finalend"] - $row["start"];
+		if($length < 0)
+			$length = 0;
 		if($length <= 1800)
 			$lengths["30min"]++;
 		elseif($length <= 3600)
@@ -467,6 +454,7 @@ function viewStatistics() {
 	print "  </TR>\n";
 	print "</TABLE>\n";
 	print "<br>\n";
+	print "</div>\n";
 
 	$unixstart = datetimeToUnix($start);
 	$unixend = datetimeToUnix($end);
@@ -476,30 +464,38 @@ function viewStatistics() {
 	               'end' => $end,
 	               'affilid' => $affilid);
 	print "<H2>Reservations by Day</H2>\n";
-	$cont = addContinuationsEntry('statgraphday', $cdata);
-	print "<img src=" . BASEURL . SCRIPT . "?continuation=$cont>";
+	print "<small>(Reservations with start time on given day)</small><br>\n";
+	$cdata['divid'] = 'resbyday';
+	$cont = addContinuationsEntry('AJgetStatData', $cdata);
+	print "<input type=hidden id=statdaycont value=\"$cont\">\n";
+	print "<div id=\"resbyday\" style=\"width: 400px; height: 310px;\">(Loading...)</div>\n";
 
 	print "<H2>Max Concurrent Reservations By Day</H2>\n";
 	if($unixend - $unixstart > SECINMONTH)
 		print "(this graph only available for up to a month of data)<br>\n";
 	else {
-		$cont = addContinuationsEntry('statgraphdayconcuruser', $cdata);
-		print "<img src=" . BASEURL . SCRIPT . "?continuation=$cont>";
+		$cdata['divid'] = 'maxconcurresday';
+		$cont = addContinuationsEntry('AJgetStatData', $cdata);
+		print "<input type=hidden id=statconcurrescont value=\"$cont\">\n";
+		print "<div id=\"maxconcurresday\" style=\"width: 400px; height: 310px;\">Loading graph data...</div>\n";
 	}
 
 	print "<H2>Max Concurrent Blade Reservations By Day</H2>\n";
 	if($unixend - $unixstart > SECINMONTH)
 		print "(this graph only available for up to a month of data)<br>\n";
 	else {
-		$cont = addContinuationsEntry('statgraphdayconcurblade', $cdata);
-		print "<img src=" . BASEURL . SCRIPT . "?continuation=$cont>";
+		$cdata['divid'] = 'maxconcurbladeday';
+		$cont = addContinuationsEntry('AJgetStatData', $cdata);
+		print "<input type=hidden id=statconcurbladecont value=\"$cont\">\n";
+		print "<div id=\"maxconcurbladeday\" style=\"width: 400px; height: 310px;\">Loading graph data...</div>\n";
 	}
 
 	print "<H2>Reservations by Hour</H2>\n";
-	print "(Averaged over the time period)<br><br>\n";
-	$cont = addContinuationsEntry('statgraphhour', $cdata);
-	print "<img src=" . BASEURL . SCRIPT . "?continuation=$cont>";
-	print "</div>\n";
+	print "<small>(Active reservations during given hour averaged over selected dates)</small><br><br>\n";
+	$cdata['divid'] = 'resbyhour';
+	$cont = addContinuationsEntry('AJgetStatData', $cdata);
+	print "<input type=hidden id=statreshourcont value=\"$cont\">\n";
+	print "<div id=\"resbyhour\" style=\"width: 400px; height: 310px;\">Loading graph data...</div>\n";
 
 	$endtime = microtime(1);
 	$end = $endtime - $timestart;
@@ -508,36 +504,26 @@ function viewStatistics() {
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn sendStatGraphDay()
+/// \fn AJgetStatData()
 ///
-/// \brief sends a graph image
+/// \brief gets statistical data for a dojox chart and returns it in json format
 ///
 ////////////////////////////////////////////////////////////////////////////////
-function sendStatGraphDay() {
-	global $xaxislabels, $inContinuation;
-	if(! $inContinuation)
-		return;
+function AJgetStatData() {
 	$start = getContinuationVar("start");
 	$end = getContinuationVar("end");
 	$affilid = getContinuationVar("affilid");
-	$graphdata = getStatGraphDayData($start, $end, $affilid);
-	$count = count($graphdata["labels"]);
-	if($count < 8)
-		$labelinterval = 1;
-	else
-		$labelinterval = $count / 7;
-	$xaxislabels = $graphdata["labels"];
-	$graph = new Graph(300, 300, "auto");
-	$graph->SetScale("textlin");
-	$plot = new BarPlot($graphdata["points"]);
-	$graph->Add($plot);
-	$graph->xaxis->SetLabelFormatCallback('statXaxisDayCallback');
-	$graph->xaxis->SetLabelAngle(90);
-	$graph->xaxis->SetTextLabelInterval($labelinterval);
-	$graph->yaxis->SetTitle('Reservations with start time on given day', 
-	                        'high');
-	$graph->SetMargin(40,40,20,80);
-	$graph->Stroke();
+	$divid = getContinuationVar('divid');
+	if($divid == 'resbyday')
+		$data = getStatGraphDayData($start, $end, $affilid);
+	elseif($divid == 'maxconcurresday')
+		$data = getStatGraphDayConUsersData($start, $end, $affilid);
+	elseif($divid == 'maxconcurbladeday')
+		$data = getStatGraphConBladeUserData($start, $end, $affilid);
+	elseif($divid == 'resbyhour')
+		$data = getStatGraphHourData($start, $end, $affilid);
+	$data['id'] = $divid;
+	sendJSON($data);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -548,9 +534,13 @@ function sendStatGraphDay() {
 /// \param $end - ending day in YYYY-MM-DD format
 /// \param $affilid - affiliationid of data to gather
 ///
-/// \return an array whose keys are the days (in YYYY-MM-DD format) between
-/// $start and $end, inclusive, and whose values are the number of reservations
-/// on each day
+/// \return an array with three keys:\n
+/// \b points - an array with y and tooltip keys that have the same value which
+///             is the y value at that point\n
+/// \b xlabels - an array with value and text keys, value's value is just an
+///              increasing integer starting from 1, text's value is the label
+///              to display on the x axis for the poing\n
+/// \b maxy - the max y value of the data
 ///
 /// \brief queries the log table to get reservations between $start and $end
 /// and creates an array with the number of reservations on each day
@@ -562,10 +552,12 @@ function getStatGraphDayData($start, $en
 
 	$data = array();
 	$data["points"] = array();
-	$data["labels"] = array();
+	$data['xlabels'] = array();
+	$data['maxy'] = 0;
 	$reloadid = getUserlistID('vclreload@Local');
+	$cnt = 0;
 	for($i = $startunix; $i < $endunix; $i += SECINDAY) {
-		array_push($data["labels"], date('Y-m-d', $i));
+		$cnt++;
 		$startdt = unixToDatetime($i);
 		$enddt = unixToDatetime($i + SECINDAY);
 		if($affilid != 0) {
@@ -589,69 +581,35 @@ function getStatGraphDayData($start, $en
 		}
 		$qh = doQuery($query, 295);
 		if($row = mysql_fetch_row($qh))
-			array_push($data["points"], $row[0]);
+			$value = $row[0];
 		else
-			array_push($data["points"], 0);
+			$value = 0;
+		$label = date('m/d/Y', $i);
+		$data['points'][] = array('y' => (int)$value, 'tooltip' => "$label: " . (int)$value);
+		if($value > $data['maxy'])
+			$data['maxy'] = (int)$value;
+		$data['xlabels'][] = array('value' => $cnt, 'text' => $label);
 	}
 	return($data);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn statXaxisDayCallback($val)
-///
-/// \param $val - value passed in by SetLabelFormatCallback
-///
-/// \return day of week
-///
-/// \brief formats $val into day of week
-///
-////////////////////////////////////////////////////////////////////////////////
-function statXaxisDayCallback($val) {
-	global $xaxislabels;
-	if(array_key_exists((int)$val, $xaxislabels)) {
-		return date('n/d/Y', datetimeToUnix($xaxislabels[$val] . " 00:00:00")) . " ";
-	}
-	else {
-		return $val;
-	}
-}
-
-////////////////////////////////////////////////////////////////////////////////
-///
-/// \fn sendStatGraphHour()
-///
-/// \brief sends a graph image
-///
-////////////////////////////////////////////////////////////////////////////////
-function sendStatGraphHour() {
-	global $xaxislabels, $inContinuation;
-	if(! $inContinuation)
-		return;
-	$start = getContinuationVar("start");
-	$end = getContinuationVar("end");
-	$affilid = getContinuationVar("affilid");
-	$graphdata = getStatGraphHourData($start, $end, $affilid);
-	$graph = new Graph(300, 300, "auto");
-	$graph->SetScale("textlin");
-	$plot = new LinePlot($graphdata["points"]);
-	$graph->Add($plot);
-	$graph->xaxis->SetLabelFormatCallback('statXaxisHourCallback');
-	$graph->xaxis->SetLabelAngle(90);
-	$graph->xaxis->SetTextLabelInterval(2);
-	$graph->yaxis->SetTitle('Active reservations during given hour', 'high');
-	$graph->SetMargin(40,40,20,80);
-	$graph->Stroke();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-///
 /// \fn getStatGraphHourData($start, $end, $affilid)
 ///
 /// \param $start - starting day in YYYY-MM-DD format
 /// \param $end - ending day in YYYY-MM-DD format
 /// \param $affilid - affiliationid of data to gather
 ///
+/// \return an array with two keys:\n
+/// \b points - an array with 5 keys:\n
+///             \t x - increasing integer starting from 1\n
+///             \t y - y value for the point\n
+///             \t value - same as x\n
+///             \t text - label for x axis for the point\n
+///             \t tooltip - tooltip to be displayed when point is hovered\n
+/// \b maxy - the max y value of the data
+///
 /// \return an array whose keys are the days (in YYYY-MM-DD format) between
 /// $start and $end, inclusive, and whose values are the number of reservations
 /// on each day
@@ -671,8 +629,9 @@ function getStatGraphHourData($start, $e
 	$data = array();
 	$data["points"] = array();
 	for($i = 0; $i < 24; $i++) {
-		$data["points"][$i] = 0;
+		$data["points"][$i] = array('x' => $i, 'y' => 0, 'value' => $i, 'text' => statHourFormatX($i), 'tooltip' => 0);
 	}
+	$data["maxy"] = 0;
 
 	$reloadid = getUserlistID('vclreload@Local');
 	if($affilid != 0) {
@@ -701,7 +660,6 @@ function getStatGraphHourData($start, $e
 		       .       "l.wasavailable = 1";
 	}
 	$qh = doQuery($query, 296);
-	$count = 0;
 	while($row = mysql_fetch_assoc($qh)) {
 		$startmin = ($row['shour'] * 60) + $row['smin'];
 		$endmin = ($row['ehour'] * 60) + $row['emin'];
@@ -713,7 +671,7 @@ function getStatGraphHourData($start, $e
 				continue;
 			elseif($startmin < $binend &&
 				$endmin > $binstart) {
-				$data["points"][$binindex]++;
+				$data["points"][$binindex]['y']++;
 			}
 			elseif($binstart >= $endmin)
 				break;
@@ -721,70 +679,39 @@ function getStatGraphHourData($start, $e
 	}
 
 	# comment this to change graph to be aggregate instead of average
-	foreach($data["points"] as $key => $val)
-		$data["points"][$key] = $val / $days;
+	foreach($data["points"] as $key => $val) {
+		$newval = $val['y'] / $days;
+		if($newval - (int)$newval != 0)
+			$newval = sprintf('%.2f', $newval);
+		$data['points'][$key]['y'] = $newval;
+		$data['points'][$key]['tooltip'] = $newval;
+		if($newval > $data['maxy'])
+			$data['maxy'] = $newval;
+	}
 
 	return($data);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn statXaxisHourCallback($val)
+/// \fn statHourFormatX($val)
 ///
-/// \param $val - value passed in by SetLabelFormatCallback
+/// \param $val - hour of day (0-23)
 ///
 /// \return day of week
 ///
-/// \brief formats $val into day of week
+/// \brief formats $val into "hour am/pm"
 ///
 ////////////////////////////////////////////////////////////////////////////////
-function statXaxisHourCallback($val) {
-	if($val == 0) {
+function statHourFormatX($val) {
+	if($val == 0)
 		return "12 am ";
-	}
-	elseif($val < 12) {
+	elseif($val < 12)
 		return "$val am ";
-	}
-	elseif($val == 12) {
+	elseif($val == 12)
 		return "$val pm ";
-	}
-	else {
-		return $val - 12 . " pm ";
-	}
-}
-
-////////////////////////////////////////////////////////////////////////////////
-///
-/// \fn sendStatGraphDayConUsers()
-///
-/// \brief sends a graph image
-///
-////////////////////////////////////////////////////////////////////////////////
-function sendStatGraphDayConUsers() {
-	global $xaxislabels, $inContinuation;
-	if(! $inContinuation)
-		return;
-	$start = getContinuationVar("start");
-	$end = getContinuationVar("end");
-	$affilid = getContinuationVar("affilid");
-	$graphdata = getStatGraphDayConUsersData($start, $end, $affilid);
-	$count = count($graphdata["labels"]);
-	if($count < 8)
-		$labelinterval = 1;
 	else
-		$labelinterval = $count / 7;
-	$xaxislabels = $graphdata["labels"];
-	$graph = new Graph(300, 300, "auto");
-	$graph->SetScale("textlin");
-	$plot = new BarPlot($graphdata["points"]);
-	$graph->Add($plot);
-	$graph->xaxis->SetLabelFormatCallback('statXaxisDayConUsersCallback');
-	$graph->xaxis->SetLabelAngle(90);
-	$graph->xaxis->SetTextLabelInterval($labelinterval);
-	$graph->yaxis->SetTitle('Maximum concurrent reservations per day', 
-	                        'high');
-	$graph->SetMargin(40,40,20,80);
-	$graph->Stroke();
+		return $val - 12 . " pm ";
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -795,9 +722,13 @@ function sendStatGraphDayConUsers() {
 /// \param $end - ending day in YYYY-MM-DD format
 /// \param $affilid - affiliationid of data to gather
 ///
-/// \return an array whose keys are the days (in YYYY-MM-DD format) between
-/// $start and $end, inclusive, and whose values are the max concurrent users
-/// on each day
+/// \return an array with three keys:\n
+/// \b points - an array with y and tooltip keys that have the same value which
+///             is the y value at that point\n
+/// \b xlabels - an array with value and text keys, value's value is just an
+///              increasing integer starting from 1, text's value is the label
+///              to display on the x axis for the poing\n
+/// \b maxy - the max y value of the data
 ///
 /// \brief queries the log table to get reservations between $start and $end
 /// and creates an array with the max concurrent users per day
@@ -812,16 +743,17 @@ function getStatGraphDayConUsersData($st
 
 	$data = array();
 	$data["points"] = array();
-	$data["labels"] = array();
+	$data["xlabels"] = array();
+	$data["maxy"] = 0;
 
 	$reloadid = getUserlistID('vclreload@Local');
+	$cnt = 0;
 	for($daystart = $startunix; $daystart < $endunix; $daystart += SECINDAY) {
-		array_push($data["labels"], date('Y-m-d', $daystart));
+		$cnt++;
 		$count = array();
 		for($j = 0; $j < 24; $j++) {
 			$count[$j] = 0;
 		}
-
 		$startdt = unixToDatetime($daystart);
 		$enddt = unixToDatetime($daystart + SECINDAY);
 		if($affilid != 0) {
@@ -863,77 +795,30 @@ function getStatGraphDayConUsersData($st
 			}
 		}
 		rsort($count);
-		array_push($data["points"], $count[0]);
+		$label = date('m/d/Y', $daystart);
+		$data["points"][] = array('y' => $count[0], 'tooltip' => "$label: {$count[0]}");
+		if($count[0] > $data['maxy'])
+			$data['maxy'] = $count[0];
+		$data['xlabels'][] = array('value' => $cnt, 'text' => $label);
 	}
 	return($data);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn statXaxisDayConUsersCallback($val)
-///
-/// \param $val - value passed in by SetLabelFormatCallback
-///
-/// \return day of week
-///
-/// \brief formats $val into day of week
-///
-////////////////////////////////////////////////////////////////////////////////
-function statXaxisDayConUsersCallback($val) {
-	global $xaxislabels;
-	if(array_key_exists((int)$val, $xaxislabels)) {
-		return date('n/d/Y', datetimeToUnix($xaxislabels[$val] . " 00:00:00")) . " ";
-	}
-	else {
-		return $val;
-	}
-}
-
-////////////////////////////////////////////////////////////////////////////////
-///
-/// \fn sendStatGraphConBladeUser()
-///
-/// \brief sends a graph image of max concurrent users of blades per day
-///
-////////////////////////////////////////////////////////////////////////////////
-function sendStatGraphConBladeUser() {
-	global $xaxislabels, $inContinuation;
-	if(! $inContinuation)
-		return;
-	$start = getContinuationVar("start");
-	$end = getContinuationVar("end");
-	$affilid = getContinuationVar("affilid");
-	$graphdata = getStatGraphConBladeUserData($start, $end, $affilid);
-	$count = count($graphdata["labels"]);
-	if($count < 8)
-		$labelinterval = 1;
-	else
-		$labelinterval = $count / 7;
-	$xaxislabels = $graphdata["labels"];
-	$graph = new Graph(300, 300, "auto");
-	$graph->SetScale("textlin");
-	$plot = new BarPlot($graphdata["points"]);
-	$graph->Add($plot);
-	$graph->xaxis->SetLabelFormatCallback('statXaxisDayConUsersCallback');
-	$graph->xaxis->SetLabelAngle(90);
-	$graph->xaxis->SetTextLabelInterval($labelinterval);
-	$graph->yaxis->SetTitle('Maximum concurrent reservations per day', 
-	                        'high');
-	$graph->SetMargin(40,40,20,80);
-	$graph->Stroke();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-///
 /// \fn getStatGraphConBladeUserData($start, $end, $affilid)
 ///
 /// \param $start - starting day in YYYY-MM-DD format
 /// \param $end - ending day in YYYY-MM-DD format
 /// \param $affilid - affiliationid of data to gather
 ///
-/// \return an array whose keys are the days (in YYYY-MM-DD format) between
-/// $start and $end, inclusive, and whose values are the max concurrent users
-/// of blades on each day
+/// \return an array with three keys:\n
+/// \b points - an array with y and tooltip keys that have the same value which
+///             is the y value at that point\n
+/// \b xlabels - an array with value and text keys, value's value is just an
+///              increasing integer starting from 1, text's value is the label
+///              to display on the x axis for the poing\n
+/// \b maxy - the max y value of the data
 ///
 /// \brief queries the log table to get reservations between $start and $end
 /// and creates an array with the max concurrent users of blades per day
@@ -948,11 +833,13 @@ function getStatGraphConBladeUserData($s
 
 	$data = array();
 	$data["points"] = array();
-	$data["labels"] = array();
+	$data["xlabels"] = array();
+	$data["maxy"] = 0;
 
 	$reloadid = getUserlistID('vclreload@Local');
+	$cnt = 0;
 	for($daystart = $startunix; $daystart < $endunix; $daystart += SECINDAY) {
-		array_push($data["labels"], date('Y-m-d', $daystart));
+		$cnt++;
 		$count = array();
 		for($j = 0; $j < 24; $j++) {
 			$count[$j] = 0;
@@ -1010,29 +897,12 @@ function getStatGraphConBladeUserData($s
 			}
 		}
 		rsort($count);
-		array_push($data["points"], $count[0]);
+		$label = date('m/d/Y', $daystart);
+		$data["points"][] = array('y' => $count[0], 'tooltip' => "$label: {$count[0]}");
+		if($count[0] > $data['maxy'])
+			$data['maxy'] = $count[0];
+		$data['xlabels'][] = array('value' => $cnt, 'text' => $label);
 	}
 	return($data);
 }
-
-////////////////////////////////////////////////////////////////////////////////
-///
-/// \fn statXaxisConBladeUserCallback($val)
-///
-/// \param $val - value passed in by SetLabelFormatCallback
-///
-/// \return day of week
-///
-/// \brief formats $val into day of week
-///
-////////////////////////////////////////////////////////////////////////////////
-function statXaxisConBladeUserCallback($val) {
-	global $xaxislabels;
-	if(array_key_exists((int)$val, $xaxislabels)) {
-		return date('n/d/Y', datetimeToUnix($xaxislabels[$val] . " 00:00:00")) . " ";
-	}
-	else {
-		return $val;
-	}
-}
 ?>

Modified: incubator/vcl/trunk/web/.ht-inc/utils.php
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/web/.ht-inc/utils.php?rev=1025626&r1=1025625&r2=1025626&view=diff
==============================================================================
--- incubator/vcl/trunk/web/.ht-inc/utils.php (original)
+++ incubator/vcl/trunk/web/.ht-inc/utils.php Wed Oct 20 16:17:19 2010
@@ -307,6 +307,9 @@ function initGlobals() {
 		case 'vm':
 			require_once(".ht-inc/vm.php");
 			break;
+		case 'dashboard':
+			require_once(".ht-inc/dashboard.php");
+			break;
 		default:
 			require_once(".ht-inc/requests.php");
 	}
@@ -8776,14 +8779,10 @@ function getNavMenu($inclogout, $inchome
 	$rt .= menulistLI('statistics');
 	$rt .= "<a href=\"" . BASEURL . SCRIPT . "?mode=selectstats\">";
 	$rt .= "Statistics</a></li>\n";
-	if($skin != 'ecu') {
-		$rt .= menulistLI('help');
-		$rt .= "<a href=\"" . HELPURL . "\">Help</a></li>\n";
-	}
-	if($skin == 'ecu') {
-		$rt .= "<li><a href=\"http://www.ecu.edu/cs-itcs/vcl/connect.cfm\">Requirements</a></li>\n";
-		$rt .= "<li><a href=\"http://www.ecu.edu/cs-itcs/vcl/save.cfm\">File Saving</a></li>\n";
-		$rt .= "<li><a href=\"http://www.ecu.edu/cs-itcs/vcl/faqs.cfm\">Help</a></li>\n";
+	if($viewmode == ADMIN_DEVELOPER) {
+		$rt .= menulistLI('dashboard');
+		$rt .= "<a href=\"" . BASEURL . SCRIPT . "?mode=dashboard\">";
+		$rt .= "Dashboard</a></li>\n";
 	}
 	if(in_array("userGrant", $user["privileges"]) ||
 		in_array("resourceGrant", $user["privileges"]) ||
@@ -8989,6 +8988,21 @@ function getDojoHTML($refresh) {
 			                      'dijit.Tooltip',
 			                      'dijit.Dialog');
 			break;
+		case 'viewstats':
+			$dojoRequires = array('dojo.parser',
+			                      'dojox.charting.Chart2D',
+			                      'dojox.charting.action2d.Tooltip',
+			                      'dojox.charting.action2d.Magnify',
+			                      'dojox.charting.themes.ThreeD');
+			break;
+		case 'dashboard':
+			$dojoRequires = array('dojo.parser',
+			                      'dijit.Tooltip',
+			                      'dojox.charting.widget.Chart2D',
+			                      'dojox.charting.action2d.Tooltip',
+			                      'dojox.charting.action2d.Magnify',
+			                      'dojox.charting.themes.ThreeD');
+			break;
 	}
 	if(empty($dojoRequires))
 		return '';
@@ -9313,6 +9327,39 @@ function getDojoHTML($refresh) {
 			$rt .= "   });\n";
 			$rt .= "</script>\n";
 			return $rt;
+		case "viewstats":
+			$rt .= "<style type=\"text/css\">\n";
+			$rt .= "   @import \"themes/$skin/css/dojo/$skin.css\";\n";
+			$rt .= "</style>\n";
+			$rt .= "<script type=\"text/javascript\" src=\"js/statistics.js\"></script>\n";
+			$rt .= "<script type=\"text/javascript\" src=\"dojo/dojo/dojo.js\"\n";
+			$rt .= "   djConfig=\"parseOnLoad: true\">\n";
+			$rt .= "</script>\n";
+			$rt .= "<script type=\"text/javascript\">\n";
+			$rt .= "   dojo.addOnLoad(function() {\n";
+			foreach($dojoRequires as $req)
+				$rt .= "   dojo.require(\"$req\");\n";
+			$rt .= "   generateGraphs();\n";
+			$rt .= "   });\n";
+			$rt .= "</script>\n";
+			return $rt;
+		case "dashboard":
+			$rt .= "<style type=\"text/css\">\n";
+			$rt .= "   @import \"themes/$skin/css/dojo/$skin.css\";\n";
+			$rt .= "   @import \"css/dashboard.css\";\n";
+			$rt .= "</style>\n";
+			$rt .= "<script type=\"text/javascript\" src=\"js/dashboard.js\"></script>\n";
+			$rt .= "<script type=\"text/javascript\" src=\"dojo/dojo/dojo.js\"\n";
+			$rt .= "   djConfig=\"parseOnLoad: true, debug: true\">\n";
+			$rt .= "</script>\n";
+			$rt .= "<script type=\"text/javascript\">\n";
+			$rt .= "   dojo.addOnLoad(function() {\n";
+			foreach($dojoRequires as $req)
+				$rt .= "   dojo.require(\"$req\");\n";
+			$rt .= "   updateDashboard();\n";
+			$rt .= "   });\n";
+			$rt .= "</script>\n";
+			return $rt;
 
 		default:
 			$rt .= "<style type=\"text/css\">\n";

Added: incubator/vcl/trunk/web/css/dashboard.css
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/web/css/dashboard.css?rev=1025626&view=auto
==============================================================================
--- incubator/vcl/trunk/web/css/dashboard.css (added)
+++ incubator/vcl/trunk/web/css/dashboard.css Wed Oct 20 16:17:19 2010
@@ -0,0 +1,23 @@
+.dashwidget {
+	border: 1px solid;
+	text-align: center;
+	/*border-radius: 10px;
+	-moz-border-radius: 10px;*/
+	margin-bottom: 8px;
+}
+
+.dashwidget .extra {
+	margin-bottom: 5px;
+}
+
+.dashwidget table {
+	margin-left: auto;
+	margin-right: auto;
+}
+
+.dashwidget h3 {
+	background: #0053fb;
+	color: white;
+	margin-top: 0px;
+	margin-bottom: 2px;
+}

Modified: incubator/vcl/trunk/web/js/code.js
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/web/js/code.js?rev=1025626&r1=1025625&r2=1025626&view=diff
==============================================================================
--- incubator/vcl/trunk/web/js/code.js (original)
+++ incubator/vcl/trunk/web/js/code.js Wed Oct 20 16:17:19 2010
@@ -123,10 +123,10 @@ var genericCB = function(type, data, evt
 	eval(data);
 }
 
-var errorHandler = function(type, error, data) {
-	if(args.dojoType == 'cancel')
+var errorHandler = function(error, ioArgs) {
+	if(error.name == 'cancel')
 		return;
-	alert('error occurred' + error.message + data.responseText);
+	alert('AJAX Error: ' + error.message + '\nLine ' + error.lineNumber + ' in ' + error.fileName);
 }
 
 function errorHandler(data, ioArgs) {

Added: incubator/vcl/trunk/web/js/dashboard.js
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/web/js/dashboard.js?rev=1025626&view=auto
==============================================================================
--- incubator/vcl/trunk/web/js/dashboard.js (added)
+++ incubator/vcl/trunk/web/js/dashboard.js Wed Oct 20 16:17:19 2010
@@ -0,0 +1,204 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+function RPCwrapper(data, CB, dojson) {
+	if(dojson) {
+		dojo.xhrPost({
+			url: 'index.php',
+			load: CB,
+			handleAs: "json",
+			error: errorHandler,
+			content: data,
+			timeout: 15000
+		});
+	}
+	else {
+		dojo.xhrPost({
+			url: 'index.php',
+			load: CB,
+			error: errorHandler,
+			content: data,
+			timeout: 15000
+		});
+	}
+}
+
+function generalReqCB(data, ioArgs) {
+	eval(data);
+	document.body.style.cursor = 'default';
+}
+
+function updateDashboard() {
+	var cont = dojo.byId('updatecont').value;
+	RPCwrapper({continuation: cont}, updateDashboardCB, 1);
+}
+
+function updateDashboardCB(data, ioArgs) {
+	dojo.byId('updatecont').value = data.items.cont;
+	updateStatus(data.items.status);
+	updateTopImages(data.items.topimages);
+	updateTopLongImages(data.items.toplongimages);
+	updateTopFailed(data.items.topfailed);
+	updateTopFailedComputers(data.items.topfailedcomputers);
+	updateResChart(data.items.reschart);
+	updateBlockAllocation(data.items.blockallocation);
+	setTimeout(updateDashboard, 15000);
+}
+
+function updateStatus(data) {
+	var obj = dojo.byId('status');
+	var txt = '<table>';
+	for(var i = 0; i < data.length; i++) {
+		txt += '<tr><th align="right">'
+		if(data[i].tooltip) {
+			txt += '<span id="status' + i + '">'
+			    + data[i].key
+			    + '</span>'
+			    + '</th><td>'
+			    + data[i].val
+			    + '</td></tr>';
+		}
+		else {
+			txt += data[i].key
+			    + '</th><td>'
+			    + data[i].val
+			    + '</td></tr>';
+		}
+	}
+	txt += '</table>';
+	obj.innerHTML = txt;
+	for(var i = 0; i < data.length; i++) {
+		if(data[i].tooltip) {
+			var tt = new dijit.Tooltip({
+				connectId: ['status' + i],
+				label: data[i].tooltip
+			});
+		}
+	}
+}
+
+function updateTopImages(data) {
+	var obj = dojo.byId('topimages');
+	if(data.length == 0) {
+		obj.innerHTML = 'No recent reservations';
+		return;
+	}
+	var txt = '<table>';
+	for(var i = 0; i < data.length; i++) {
+		txt += '<tr><th align="right">'
+		    + data[i].prettyname
+		    + '</th><td>'
+		    + data[i].count
+		    + '</td></tr>';
+	}
+	txt += '</table>';
+	obj.innerHTML = txt;
+}
+
+function updateTopLongImages(data) {
+	var obj = dojo.byId('toplongimages');
+	if(data.length == 0) {
+		obj.innerHTML = 'No recent reservations';
+		return;
+	}
+	var txt = '<table>';
+	for(var i = 0; i < data.length; i++) {
+		txt += '<tr><th align="right">'
+		    + data[i].prettyname
+		    + '</th><td>'
+		    + data[i].count
+		    + '</td></tr>';
+	}
+	txt += '</table>';
+	obj.innerHTML = txt;
+}
+
+function updateTopFailed(data) {
+	var obj = dojo.byId('topfailed');
+	if(data.length == 0) {
+		obj.innerHTML = 'No recent reservations';
+		return;
+	}
+	var txt = '<table>';
+	for(var i = 0; i < data.length; i++) {
+		txt += '<tr><th align="right">'
+		    + data[i].prettyname
+		    + '</th><td>'
+		    + data[i].count
+		    + '</td></tr>';
+	}
+	txt += '</table>';
+	obj.innerHTML = txt;
+}
+
+function updateTopFailedComputers(data) {
+	var obj = dojo.byId('topfailedcomputers');
+	if(data.length == 0) {
+		obj.innerHTML = 'No recent reservations';
+		return;
+	}
+	var txt = '<table>';
+	for(var i = 0; i < data.length; i++) {
+		txt += '<tr><th align="right">'
+		    + data[i].hostname
+		    + '</th><td>'
+		    + data[i].count
+		    + '</td></tr>';
+	}
+	txt += '</table>';
+	obj.innerHTML = txt;
+}
+
+function updateBlockAllocation(data) {
+	var obj = dojo.byId('blockallocation');
+	var txt = '<table>';
+	for(var i = 0; i < data.length; i++) {
+		txt += '<tr><th align="right">'
+		    + data[i].title
+		    + '</th><td>'
+		    + data[i].val
+		    + '</td></tr>';
+	}
+	txt += '</table>';
+	obj.innerHTML = txt;
+}
+
+function updateResChart(data) {
+	var graph = dijit.byId('reschart').chart;
+	graph.updateSeries('Main', data.points);
+	graph.labeldata = data.points;
+	graph.render();
+}
+
+function timestampToTime(val) {
+	if(! dijit.byId('reschart').chart.labeldata)
+		return '';
+	else
+		var data = dijit.byId('reschart').chart.labeldata;
+	return data[val]['text'];
+	var d = new Date();
+	d.setTime(val*1000);
+	var h = d.getHours();
+	if(h == 0)
+		return "12:" + d.getMinutes() + " am";
+	else if(h < 12)
+		return h + ":" + d.getMinutes() + " am";
+	else if(h == 12)
+		return "12:" + d.getMinutes() + " pm";
+	else
+		return (h - 12) + ":" + d.getMinutes() + "pm";
+}

Added: incubator/vcl/trunk/web/js/statistics.js
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/web/js/statistics.js?rev=1025626&view=auto
==============================================================================
--- incubator/vcl/trunk/web/js/statistics.js (added)
+++ incubator/vcl/trunk/web/js/statistics.js Wed Oct 20 16:17:19 2010
@@ -0,0 +1,144 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+function RPCwrapper(data, CB, dojson) {
+	if(dojson) {
+		dojo.xhrPost({
+			url: 'index.php',
+			load: CB,
+			handleAs: "json",
+			error: errorHandler,
+			content: data,
+			timeout: 300000
+		});
+	}
+	else {
+		dojo.xhrPost({
+			url: 'index.php',
+			load: CB,
+			error: errorHandler,
+			content: data,
+			timeout: 300000
+		});
+	}
+}
+
+function generalReqCB(data, ioArgs) {
+	eval(data);
+	document.body.style.cursor = 'default';
+}
+
+function generateGraphs() {
+	var cont = dojo.byId('statdaycont').value;
+	RPCwrapper({continuation: cont}, generateColGraphsCB, 1);
+	var cont = dojo.byId('statreshourcont').value;
+	RPCwrapper({continuation: cont}, generateHourGraphsCB, 1);
+	if(dojo.byId('statconcurrescont')) {
+		var cont = dojo.byId('statconcurrescont').value;
+		RPCwrapper({continuation: cont}, generateColGraphsCB, 1);
+	}
+	if(dojo.byId('statconcurbladecont')) {
+		var cont = dojo.byId('statconcurbladecont').value;
+		RPCwrapper({continuation: cont}, generateColGraphsCB, 1);
+	}
+}
+
+function generateColGraphsCB(data, ioArgs) {
+	dojo.byId(data.items.id).innerHTML = '';
+	var graph = new dojox.charting.Chart2D(data.items.id);
+	if(data.items.maxy <= 400)
+		var majortick = 50;
+	else if(data.items.maxy <= 600)
+		var majortick = 100;
+	else if(data.items.maxy <= 1000)
+		var majortick = 200;
+	else {
+		var majortick = data.items.maxy / 10;
+		majortick = (majortick - (majortick % 200)) || 200;
+	}
+	graph.setTheme(dojox.charting.themes.ThreeD);
+	if(data.items.points.length < 10)
+		var gap = 6;
+	else if(data.items.points.length < 20)
+		var gap = 3;
+	else if(data.items.points.length < 50)
+		var gap = 2;
+	else
+		var gap = 0;
+	var xtickstep = parseInt(data.items.xlabels.length / 10) || 1;
+	graph.addAxis("x", {
+		includeZero: false,
+		labels: data.items.xlabels,
+		rotation: -90,
+		minorTicks: false,
+		font: 'normal normal normal 11px verdana',
+		majorTickStep: xtickstep
+	});
+	graph.addAxis('y', {
+		vertical: true,
+		fixUpper: "major",
+		includeZero: true,
+		minorTicks: true,
+		minorLabels: false,
+		majorTickStep: majortick,
+		minorTickStep: majortick / 2,
+	});
+	graph.addPlot('default', {type: "Columns", gap: gap});
+	graph.addPlot('Grid', {type: 'Grid', hMajorLines: true, vMajorLines: false});
+	graph.addSeries("Main", data.items.points, {stroke: {width: 1}});
+	var a = new dojox.charting.action2d.Tooltip(graph);
+	graph.render();
+}
+
+function generateHourGraphsCB(data, ioArgs) {
+	dojo.byId(data.items.id).innerHTML = '';
+	var graph = new dojox.charting.Chart2D(data.items.id);
+	if(data.items.maxy <= 50)
+		var majortick = 5;
+	else if(data.items.maxy <= 100)
+		var majortick = 10;
+	else if(data.items.maxy <= 200)
+		var majortick = 20;
+	else {
+		var majortick = data.items.maxy / 10;
+		majortick = (majortick - (majortick % 100)) || 100;
+	}
+	graph.setTheme(dojox.charting.themes.ThreeD);
+	graph.addAxis("x", {
+		includeZero: true,
+		labels: data.items.points,
+		rotation: -90,
+		minorTicks: false,
+		font: 'normal normal normal 11px verdana',
+		majorTickStep: 2
+	});
+	graph.addAxis('y', {
+		vertical: true,
+		fixUpper: "major",
+		includeZero: true,
+		minorTicks: true,
+		minorLabels: false,
+		majorTickStep: majortick,
+		minorTickStep: majortick / 5,
+	});
+	graph.addPlot('default', {markers: true});
+	graph.addPlot('Grid', {type: 'Grid', hMajorLines: true, vMajorLines: false});
+	graph.addSeries("Main", data.items.points);
+	var a = new dojox.charting.action2d.Tooltip(graph);
+	var a = new dojox.charting.action2d.Magnify(graph);
+	graph.render();
+}