<?php
class THINiReport
{
public $Data_Assoc;
public $Data_Grouped;
public $Data_Grouped_HTML;
public $Groups = array();
public $Group_Count = 0;
public $Grouped_Column_Count;
public $Column_Headers = array();
public $Column_Widths = false;
public $ShowSub = array();
public $Subtotals = array();
public function __construct()
{
}
public function __destruct()
{
}
public function Load_Data($data)
{
$this->Data_Assoc = $data;
$this->Column_Headers = array_keys($this->Data_Assoc[0]);
}
/*
* Group_Data($groups)
* Organises an associative array into groupings of common data
* $groups, if specified, is an array of column headers for which data should be grouped
* groupings are ordered by their array index.
* ToDo: If no groupings are specified search the array for recurring data and guess groupings according to number of occurences of given values (number of distinct values in column)
*/
public function Group_Data($groups = false)
{
if(!$groups)
{
$this->Determine_Groups(3);
}
else
{
$this->Groups = $groups;
}
$this->Group_Count = count($this->Groups);
//An in-elegant way of populating a grouping array of up to 6 dimensions. Improvements on a postcard please!
foreach($this->Data_Assoc as $row_number => $row_data)
{
$trimmed_data = $row_data;
for($i = 0; $i < $this->Group_Count; $i ++)
{
unset($trimmed_data[$this->Groups[$i]]);
}
switch($this->Group_Count)
{
case 1:
$data_grouped[$row_data[$this->Groups[0]]][] = $trimmed_data;
break;
case 2:
$data_grouped[$row_data[$this->Groups[0]]][$row_data[$this->Groups[1]]][] = $trimmed_data;
break;
case 3:
$data_grouped[$row_data[$this->Groups[0]]][$row_data[$this->Groups[1]]][$row_data[$this->Groups[2]]][] = $trimmed_data;
break;
case 4:
$data_grouped[$row_data[$this->Groups[0]]][$row_data[$this->Groups[1]]][$row_data[$this->Groups[2]]][$row_data[$this->Groups[3]]][] = $trimmed_data;
break;
case 5:
$data_grouped[$row_data[$this->Groups[0]]][$row_data[$this->Groups[1]]][$row_data[$this->Groups[2]]][$row_data[$this->Groups[3]]][$row_data[$this->Groups[4]]][] = $trimmed_data;
break;
case 6:
$data_grouped[$row_data[$this->Groups[0]]][$row_data[$this->Groups[1]]][$row_data[$this->Groups[2]]][$row_data[$this->Groups[3]]][$row_data[$this->Groups[4]]][$row_data[$this->Groups[5]]][] = $trimmed_data;
break;
}
}
$this->Column_Headers = array_keys($trimmed_data);
$this->Grouped_Column_Count = count($trimmed_data);
$this->Data_Grouped = $data_grouped;
}
public function Determine_Groups($max_groups = 3)
{
/*
//Until this function is written, use the first n ($max_groups) columns as the groupings
for($i = 0; $i < $max_groups; $i ++)
{
$groups[] = $this->Column_Headers[$i];
}
$this->Groups = $groups;
*/
//create an array of columns
foreach($this->Data_Assoc as $row_number => $row_data)
{
foreach($row_data as $column_header => $field_value)
{
$g[$column_header][$field_value] = 1;
}
}
//count distinct values
foreach($g as $k => $v)
{
$x[$k] = count($g[$k]);
}
//print_r($x);
//sort by lowest number of distinct columns (most recurrent values)
asort($x);
reset($x);
//populate groups array in order
foreach($x as $k => $v)
{
$groups[] = $k;
}
//trim array to max number of groupings
$this->Groups = array_slice($groups, 0, $max_groups);
}
public function Assoc_2_XML($data, $i = ''){
$xml = '';
foreach($data as $k => $v){
$k = is_int($k) ? 'row' : $k;
$xml .= is_array($v) ? "$i<$k>\n".$this->Assoc_2_XML($v, "$i ")."$i</$k>\n" : "$i<$k>$v</$k>\n";
}
return $xml;
}
public function Display_Group_Divs($a, $d = 0, $show_count = true, $parent = false)
{
//group headers should be h1 ~ h6
$h = ($d < 6) ? ($d + 1) : 6;
//if the arrays elements, elements are also arrays, we are displaying a grouping
if(is_array(current(current($a))))
{
//display header and contents for each element
foreach($a as $k => $v)
{
$html .= "<h$h>".trim($k)."</h$h>\n";
$html .= "<div class='group'>\n";
$html .= $this->Display_Group_Divs($v, ($d + 1), $show_count, $k);
//display the row count for this grouping (currently only works for last grouping)
if($show_count && !is_array(current(current($v))))
{
$html .= "<div class='group'>\n";
$html .= "<strong>Count: ".count($v)."</strong>\n";
//$html .= "<br style='clear: both;' />\n";
//if there is a subtotal array for this grouping level
if(is_array($this->Subtotals[$d][$k]))
{
foreach($this->Subtotals[$d][$k] as $k_st => $v_st)
{
//if subtotaling is explicitly on for this level and column
if(is_array($this->ShowSub[$k_st]) && in_array($d, $this->ShowSub[$k_st]))
{
$html .= "| <strong>$k_st: $v_st</strong>\n";
}
}
}
$html .= "</div>\n";
}
$html .= "<br style='clear: both;' />\n";
$html .= "</div>\n";
}
}
//else we are displaying the data fields
else
{
//display each field
foreach($a as $k => $v)
{
$class = ($k & 1) ? 'field_odd' : 'field_even';
foreach($v as $fk => $fv)
{
//check for column width settings
$column_width = ($this->Column_Widths) ? " style='width: ".$this->Column_Widths[$fk]."px;'" : '';
$html .= "<div class='$class'$column_width><a title='$fk'>$fv</a></div>\n";
//if subtotaling is explicitly on for this level and column
if(is_numeric($fv) && is_array($this->ShowSub[$fk]) && in_array($d - 1, $this->ShowSub[$fk]))
{
$this->Subtotals[($d - 1)][$parent][$fk] = (isset($this->Subtotals[($d - 1)][$parent][$fk])) ? ($this->Subtotals[($d - 1)][$parent][$fk] + $fv) : $fv;
}
}
$html .= "<br style='clear: both;' />\n";
}
}
$this->Data_Grouped_HTML = $html;
return $html;
}
function Column_Headers()
{
for($i = 0; $i < $this->Group_Count; $i ++)
{
$html .= "<div class='group'>\n";
if($i == ($this->Group_Count - 1))
{
foreach($this->Column_Headers as $ch)
{
//check for column width settings
$column_width = ($this->Column_Widths) ? " style='width: ".$this->Column_Widths[$ch]."px;'" : '';
$html .= "<div class='field'$column_width>$ch</div>\n";
}
$html .= "<br style='clear: both;' />\n";
}
}
for($i = 0; $i < $this->Group_Count; $i ++)
{
$html .= "</div>\n";
}
return $html;
}
/*
* Random_String([$length])
* Generates and returns a random string of lowercase characters
* $length, if specified will determine the length of the string
* $length defaults to a random number between 3 and 6
*/
public function Random_String($length = false)
{
$l = ($l) ? intval($l) : rand(3, 9);
$string = '';
for($i = 0; $i < $length; $i ++)
{
$string .= chr(rand(97, 98));
}
return $string;
}
/*
* Random_Data([int $aCols] [, int $nCols])
* Generates and returns an associative array of random data
* $rows is the number of rows to generate. Defaults to 50
* $aCols is the number of columns with alphabetic strings. Defaults to 6
* $nCols is the number of columns with numeric data. Defaults to 6
*/
public function Random_Data($rows = 50, $aCols = 6, $nCols = 6)
{
for($r = 0; $r < $rows; $r ++)
{
//columns with alpha data
for($a = 65; $a < (65 + $aCols); $a ++)
{
$data[$r]['col'.chr($a)] = $this->Random_String(2);
}
//columns with numeric data
for($n = 1; $n <= $nCols; $n ++)
{
$data[$r]['col'.$n] = rand(1, 200);
}
}
$this->Data_Assoc = $data;
$this->Column_Headers = array_keys($this->Data_Assoc[0]);
return $data;
}
public function Less_Random_Data()
{
$csv = file('lib/assets/uk.csv');
foreach($csv as $line){
$l = explode(',', $line);
$data[] = array('Postcode'=>$l[0], 'Town'=>$l[1], 'County'=>$l[2], 'ItemA'=>rand(100, 1000), 'ItemB'=>rand(100, 1000));
}
$this->Data_Assoc = array_slice($data, 0, 250);
$this->Column_Headers = array_keys($this->Data_Assoc[0]);
return $this->Data_Assoc;
}
}
?>