<?php
/*
$Id: 35_ows_heatmap.php 112 2007-11-21 14:27:19Z randomperson83 $
Obsessive Web Statistics
Copyright (C) 2007 Dustin Spicuzza <hide@address.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
TODO: Need to add more options to this plugin, different things to display
*/
class OWSHeatmap implements iPlugin, iFilterPlugin{
var $prefix = '';
function __construct(){
$this->prefix = $this->getPluginId() . '_';
}
// this should return a unique ID identifying the plugin, should start with an alpha,
// should use basename instead of just __FILE__ otherwise it could expose path information
public function getPluginId(){
return 'p'. md5(basename(__FILE__) . get_class());
}
// returns an associative array describing the plugin
public function getPluginInformation(){
// automagically increment the revision number :)
$revision = trim(str_replace('Rev:','',str_replace('$','','$Rev: 112 $')));
return array(
'author' => 'Dustin Spicuzza (OWS builtin)',
'pluginName' => 'Heatmap Plugin',
'version' => "1.0.$revision",
'description' => 'Shows visual analysis using heatmaps',
'url' => 'http://obsessive.sourceforge.net/'
);
}
// returns name of filter to show to user
public function getDisplayName(){
return "Heatmap Analysis";
}
// this function outputs html which is displayed inside of a form
// submit button is already defined for you
public function showOptions(){
echo '<div class="floater">
<p><u>Heatmap Types</u></p>
<table>
<tr><td>Heat "Source":</td><td>' . generate_select_from_array($this->prefix . 'heatkey','host',$this->heat_keys) . '</td></tr>
<tr><td>Detail:</td><td>' . generate_select_from_array($this->prefix . 'details','hourly',$this->details) . '</td></tr>
</table>
</div>';
show_common_limits();
}
var $heat_keys = array(
array('unihost','Unique Visitors'),
array('unireferrer','Unique External Referrers'),
array('referrer','External Referrers'),
array('noreferrer','No Referrer'),
array('pages','Pages'),
array('hits','Hits'),
array('bytes','Bytes Served')
);
var $details = array(
array('daily','Daily'),
array('hourly','Hourly')
);
// this function shows the filtered results
public function showResults($domain){
global $cfg;
$heatkey = get_post_var($this->prefix . 'heatkey');
$details = get_post_var($this->prefix . 'details');
if ($details == 'daily')
echo "<div class=\"floater\"><h4>Month-Day Heatmap: ";
else
echo "<div class=\"floater\"><h4>Hourly Heatmap: ";
$query = new SQLSelect($domain);
$d_dimension = $query->DIMENSION('date');
$query->SELECT("$d_dimension.month");
$query->SELECT("$d_dimension.day_of_month");
if ($details == 'hourly'){
$h_dimension = $query->DIMENSION('time');
$query->SELECT("$h_dimension.hour");
}
switch($heatkey){
case 'unihost':
echo "Unique Visitors";
$dimension = $query->DIMENSION('host');
$query->SELECT("COUNT(DISTINCT $dimension.host)");
break;
case 'unireferrer':
echo "Unique External Referrers";
$dimension = $query->DIMENSION('referrer');
$query->SELECT("COUNT(DISTINCT $dimension.referrer)");
$query->WHERE("$dimension.is_external = FALSE");
break;
case 'referrer':
echo "External Referrers";
$dimension = $query->DIMENSION('referrer');
$query->SELECT("COUNT($dimension.referrer)");
$query->WHERE("$dimension.is_external = FALSE");
break;
case 'noreferrer':
echo "Hits with no referrers";
$dimension = $query->DIMENSION('referrer');
$query->SELECT("COUNT($dimension.referrer)");
$query->WHERE("$dimension.referrer = ''");
break;
case 'hits':
echo "Hits";
$query->SELECT("COUNT(" . $query->FACT_TABLE() . ".id)");
break;
case 'pages':
echo "Pages";
$dimension = $query->DIMENSION('request');
$query->SELECT("COUNT($dimension.is_page)");
$query->WHERE("$dimension.is_page = TRUE");
break;
case 'bytes':
echo "Bandwidth";
$dimension = $query->DIMENSION('bytes');
$query->SELECT("SUM($dimension.bytes)");
break;
}
echo "</h4>";
$query->GROUP_BY("$d_dimension.month");
$query->GROUP_BY("$d_dimension.day_of_month");
$query->ORDER_BY("$d_dimension.month ASC");
$query->ORDER_BY("$d_dimension.day_of_month ASC");
if ($details == 'hourly'){
$query->GROUP_BY("$h_dimension.hour");
$query->ORDER_BY("$h_dimension.hour ASC");
}
if (!($q = $query->generateQuery()))
return;
if (!($result = db_query($q)))
return;
$rows = array();
$cols = array();
if ($details == 'daily'){
// generate the item list
$cur_month = 1;
$cur_day = 1;
$items = array();
$i_count = 0;
while($row = db_fetch_row($result)){
while ($row[0] != $cur_month || $row[1] != $cur_day){
$i_count += 1;
$cur_day += 1;
if ($cur_day == 32){
$cur_day = 1;
$cur_month += 1;
}
}
$cur_day += 1;
if ($cur_day == 32){
$cur_day = 1;
$cur_month += 1;
}
$items[$i_count++] = $row[2];
}
// generate the rows
for ($i = 1;$i < 13;$i++)
$rows[] = date('M',mktime(0,0,0,$i,1,2000));
// generate the columns
for ($i = 1; $i < 32;$i++)
$cols[] = sprintf("%02d",$i);
}else{
// details == hourly
// generate the item list
$first = true;
$items = array();
$i_count = 0;
while($row = db_fetch_row($result)){
if ($first){
$cur_month = $row[0];
$cur_day = $row[1];
$cur_hour = 0;
$rows[] = "$cur_month/$cur_day";
$first = false;
}
while ($row[0] != $cur_month || $row[1] != $cur_day || $row[2] != $cur_hour){
$i_count += 1;
$cur_hour += 1;
if ($cur_hour == 24){
$cur_hour = 0;
$cur_day += 1;
if ($cur_day == 32){
$cur_day = 1;
$cur_month += 1;
}
$rows[] = "$cur_month/$cur_day";
}
}
$cur_hour += 1;
if ($cur_hour == 24){
$cur_hour = 0;
$cur_day += 1;
if ($cur_day == 32){
$cur_day = 1;
$cur_month += 1;
}
$rows[] = "$cur_month/$cur_day";
}
$items[$i_count++] = $row[3];
}
// generate the columns
for ($i = 0; $i < 24;$i++)
$cols[] = sprintf("%02d",$i);
}
if (count($items) < 1)
echo "No results returned.";
else
// show the heatmap
$this->heatmap($items, $rows, $cols, $heatkey == 'bytes');
}
/*
displays a heatmap
$items Array of numbers to be displayed, should be length(row X cols)
$rows row identifiers
$cols column identifiers
$use_file_size Use the file size for the key
*/
function heatmap($items, $rows, $cols, $use_file_size = false){
// do the heatmap now, using the first three things
echo "<table>";
$max = max($items);
$cur_item = 0;
$cur_row = 0;
$num_rows = count($rows);
$cur_col = 1;
$num_cols = count($cols);
// row counter
while ($cur_row != $num_rows){
// header
if ($cur_col == 1)
echo "<tr><td>" . $rows[$cur_row] . "</td>";
if (isset($items[$cur_item]) && $items[$cur_item] > 0){
echo "<td title=\"$items[$cur_item]\" style=\"background: " . $this->getHeatColor($items[$cur_item],$max) . ";\"> </td>";
}else
echo "<td title=\"0\" style=\"background: #000000;\"> </td>";
// end condition for column
if ($cur_col == $num_cols){
$mx1 = intval(($max / $num_rows) * $cur_row);
echo "<td style=\"background:#ffffff\"> </td><td>" . ($use_file_size ? get_file_size($mx1) : $mx1) . "</td><td style=\"background: " .
$this->getHeatColor($mx1,$max) . ";\"> </td></tr>";
$cur_col = 0;
$cur_row += 1;
}
$cur_col += 1;
$cur_item += 1;
}
echo "<tr><td> </td>";
foreach ($cols as $col)
echo "<td>" . $col . "</td>";
echo "<td colspan=\"3\"> </td></tr></table>";
echo "</div>";
}
// returns a HTML color representing the heat color
function getHeatColor($num, $max, $h_start = 320, $s_start = 0.8, $v_start = 100, $h_end = 360){
$h = 0; // 0 to 360
$s = 0; // 0 to 1
$v = 0; // 0 to 255
$num = min($num,$max);
$h = $h_start + (($h_end-$h_start) * ($num / ($max+1)));
$s = $s_start + (1-$s_start) *($num / ($max+1));
$v = $v_start + ((255-$v_start) * ($num / ($max+1)));
//prn(hsv2rgb($h,$s,$v));
list($r,$g,$b) = $this->hsv2rgb($h,$s,$v);
return '#' . sprintf('%02X',$r) . sprintf('%02X',$g) . sprintf('%02X',$b);
}
// HSV transform, from wikipedia
function hsv2rgb($h,$s,$v){
$h = max(0,min(360,$h));
$s = max(0,min(1,$s));
$v = max(0,min(255,$v));
$hi = intval(floor($h / 60) % 6);
$f = $h / 60 - $hi;
$p = $v * (1 - $s);
$q = $v * (1 - $f * $s);
$t = $v * (1 - (1 - $f) * $s);
//echo "Hi: $hi, f: $f, p $p, q $q, t $t<br/>";
switch ($hi){
case 0:
$r = $v; $g = $t; $b = $p;
break;
case 1:
$r = $q; $g = $v; $b = $p;
break;
case 2:
$r = $p; $g = $v; $b = $t;
break;
case 3:
$r = $p; $g = $q; $b = $v;
break;
case 4:
$r = $t; $g = $p; $b = $v;
break;
case 5:
$r = $v; $g = $p; $b = $q;
break;
default:
$r = 0; $g = 0; $b = 0;
}
return array(intval($r),intval($g),intval($b));
}
}
register_plugin('filter',new OWSHeatmap());
?>