Open Source Billing

This commit is contained in:
Deon George
2013-10-10 13:44:53 +11:00
commit b02d70adf0
2344 changed files with 392978 additions and 0 deletions

View File

@@ -0,0 +1,217 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides access to Google's Chart API
*
* @package GoogleChart
* @category Helper
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class GoogleChart implements Iterator,Countable {
// Hold the data for our chart
protected $_data = array();
protected $_axis = array();
private $_plotdata = array();
protected $_max = array();
// Chart title
protected $_title = '';
protected $_dataurl = '';
protected $_divname = 'gchart';
// Default chart size.
protected $_height = '200';
protected $_width = '700';
// Colors to use for series
private $series_colors = array('AAACCC','E0E0E0','CCC888','EEEBBB','666CCC','888888');
// Implementation Methods
public function count() {
return count($this->_plotdata);
}
public function current() {
return current($this->_plotdata);
}
public function key() {
return key($this->_plotdata);
}
public function next() {
return next($this->_plotdata);
}
public function rewind() {
reset($this->_plotdata);
}
public function valid() {
return key($this->_plotdata) ? TRUE : FALSE;
}
public function __call($name,$args) {
switch ($name) {
case 'dataurl': $this->_dataurl = array_shift($args);
break;
case 'div': $this->_divname = array_shift($args);
break;
case 'height': $this->_height = array_shift($args);
break;
case 'title': $this->_title = array_shift($args);
break;
case 'width': $this->_width = array_shift($args);
break;
default:
throw new Kohana_Exception('Unknown method :name',array(':name'=>$name));
}
return $this;
}
/**
* Render the URL that generates the graph
*/
final public function __toString() {
try {
return (string)$this->render();
} catch (Exception $e) {
echo Debug::vars($e);
}
}
/**
* Pick the driver that will render the graph
* @param $class The child class to invoke
*/
final public static function factory($class) {
$c = sprintf('%s_%s',get_called_class(),$class);
if (! class_exists($c))
throw new Kohana_Exception('Unknown Google Chart Type :class',array(':class'=>$class));
else
return new $c();
}
// Render the chart data in a json format
abstract public function json();
// Our child class should define how to render as a string
abstract public function render();
/**
* Record one series of data
* @param $axis Axis used and legend title, eg: (yr->"Right Y",yl->"Left Y")
* @param $data Data for Axis in the format ("x label"=>value);
*
* Example:
* $this->data('yl'=>'Base Down Peak',array('11-12'=>1,'11-11'=>2));
*/
public function sdata(array $axis,array $data) {
// Some sanity checking
if (count($axis) != 1)
throw new Kohana_Exception('We can only take 1 series at time.');
// This should only iterate once
foreach ($axis as $key => $l) {
if (! in_array($key,array('yr','yl')))
throw new Kohaan_Exception('Unknown AXIS :axis',array(':axis'=>$key));
$this->_axis[$l] = $key;
$this->_data[$l] = $data[$l];
// Upate our plot data
foreach ($data[$l] as $k=>$v)
$this->_plotdata[$k][$l] = $v;
ksort($this->_plotdata);
$this->_max[$l] = max($data[$l]);
}
return $this;
}
/**
* Record on plot event
* @param $data Should contain an "X" with a "YL" and/or "YR"
*/
public function pdata($x,array $data) {
if (! is_string($x))
throw new Kohana_Exception('X should be a string');
foreach ($data as $key => $values) {
switch ($key) {
case 'yr':
case 'yl':
foreach ($values as $k=>$v) {
if (! in_array($k,$this->_axis))
$this->_axis[$k] = $key;
$this->_data[$k][$x] = $v;
$this->_plotdata[$x][$k] = $v;
}
break;
default:
throw new Kohana_Exception('Unknown key :key',array(':key'=>$key));
}
}
}
/**
* Return the colors that will be used for this series
*/
protected function seriescolors() {
return array_slice($this->series_colors,0,count($this->_axis));
}
/**
* Render the chart data in a table format
*/
public function table($vertical=TRUE,$class=array()) {
if (! $this->_data)
return sprintf('<table><tr><td>%s</td></tr></table>',_('No Data'));
$output = sprintf('<table %s>',
isset($class['table']) ? $class['table'] : 'class="google-data-table"');
if ($vertical) {
$output .= '<tr>';
$output .= '<td>&nbsp;</td>';
foreach ($this->_axis as $l => $axis)
$output .= sprintf('<th style="text-align: right;">%s</th>',$l);
$output .= '</tr>';
foreach ($this as $k => $details) {
$output .= '<tr>';
$output .= sprintf('<th style="text-align: right;">%s</th>',$k);
foreach ($this->_axis as $l => $axis)
$output .= sprintf('<td style="text-align: right;">%s</td>',$details[$l]);
$output .= '</tr>';
}
// Horizontal table
} else {
$output .= '<tr>';
$output .= '<td>&nbsp;</td>';
foreach ($this as $k => $details)
$output .= sprintf('<th style="text-align: right;">%s</th>',$k);
$output .= '</tr>';
foreach ($this->_axis as $l => $axis) {
$output .= '<tr>';
$output .= sprintf('<th style="text-align: right;">%s</th>',$l);
foreach ($this as $k => $v)
$output .= sprintf('<td style="text-align: right;">%s</td>',$v[$l]);
$output .= '</tr>';
}
}
$output .= '</table>';
return $output;
}
}
?>

View File

@@ -0,0 +1,170 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides access to Google's Chart API
*
* @package GoogleChart
* @category Helper
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
*/
class GoogleChart_ComboChart extends GoogleChart {
// Should the Y column range be a log() function
protected $_logy = FALSE;
// Should the bar values be stacked
protected $_stacked = FALSE;
// Default line type to use
protected $_type = 'bars';
public function logy($value) {
$this->_logy = $value;
return $this;
}
public function stacked($value) {
$this->_stacked = $value;
return $this;
}
/**
* Set the type of the chart
* @param $type Chart type as per $this->cht
*/
public function ltitle($side,$title) {
if (! in_array($side,array('yl','yr','x')))
throw new Kohana_Exception('Unknown side :side',array(':side'=>$side));
$this->_ltitle[$side] = $title;
return $this;
}
public function json() {
$result = array();
$result['cols'][] = array(
'id'=>'date',
'label'=>'date',
'type'=>'string',
);
// Columns
foreach (array_keys($this->_axis) as $l) {
$result['cols'][] = array(
'id'=>$l,
'label'=>$l,
'type'=>'number',
);
}
// Values
foreach ($this as $k => $v) {
$data = array();
array_push($data,array('v'=>$k));
foreach ($this->_axis as $l => $axis)
array_push($data,array('v'=>isset($v[$l]) ? $v[$l] : 0));
$result['rows'][] = array('c'=>$data);
}
$options = array(
'bar' => array('groupWidth'=>'75%'),
'vAxis' => array('logScale'=>$this->_logy ? 1:0),
'title' => $this->_title,
'isStacked' => $this->_stacked ? 1:0,
'seriesType' => $this->_type,
'series' => $this->series(),
);
return json_encode(array('data'=>$result,'options'=>$options));
}
public function render() {
Script::add(array(
'type'=>'src',
'data'=>'https://www.google.com/jsapi',
));
Script::add(array(
'type'=>'stdin',
'data'=>'google.load("visualization", "1", {packages: ["corechart"]});',
));
Script::add(array(
'type'=>'stdin',
'data'=>"
function drawChart_".$this->_divname."() {
div = document.getElementById('".$this->_divname."');
if (! div) {
alert(\"Unable to render chart, DIV ['".$this->_divname."'] doesnt exist.\");
return;
}
$.ajax({
url: '".$this->_dataurl."',
dataType: 'json',
async: true,
timeout: 10000,
success: function(jsonData) {
for(var key in jsonData) {
if (key == 'data')
data = jsonData[key];
else if (key == 'options')
options = jsonData[key];
else
alert('UNKNOWN Key: '+key);
}
// Create our data table out of JSON data loaded from server.
var x = new google.visualization.DataTable(data);
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.ComboChart(document.getElementById('".$this->_divname."'));
chart.draw(x, options);
},
error: function(x, t, m) {
if (t==='timeout') {
alert('got timeout');
} else {
alert('t is: '+t+', X: '+x+', M: '+m);
}
}
});
}
",
));
Script::add(array(
'type'=>'stdin',
'data'=>'$(document).ready(function() {google.setOnLoadCallback(drawChart_'.$this->_divname.'); });',
));
return sprintf('<div id="%s" style="width: %spx; height: %spx;"></div>',$this->_divname,$this->_width,$this->_height);
}
private function series() {
$result = array();
$c = $this->seriescolors();
$j = count($c);
$i = 0;
foreach ($this->_axis as $l => $axis) {
// @todo This shouldnt be hard coded
if ($axis == 'yl')
array_push($result,array('type'=>'bar','color'=>$c[$i%$j],'targetAxisIndex'=>0));
else
array_push($result,array('type'=>'line','color'=>$c[$i%$j],'targetAxisIndex'=>1));
$i++;
}
return $result;
}
}
?>

View File

@@ -0,0 +1,220 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides access to Google's Chart Legacy API
*
* @package GoogleChart
* @category Helper
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
*/
class GoogleChart_Legacy extends GoogleChart {
// Chart URL
private $_url = 'http://chart.apis.google.com/chart';
// The type of chart we'll plot
private $_type = 'bvs';
// Data encoding type to use
private $_encodetype = 's';
// Chart Types
private $cht = array(
// Line
// 'line' => 'lc',
// 'sparkline' => 'ls',
// 'line_xy' => 'lxy',
// Bar
// 'horizontal_bar' => 'bhs',
'vertical_bar' => 'bvs',
// 'horizontal_bar_grp' => 'bhg',
// 'vertical_bar_grp' => 'bvg',
// Pie
// 'pie' => 'p',
// 'pie_3d' => 'p3',
// 'pie_concentric' => 'pc',
// Venn
// 'venn' => 'v',
// Scatter
// 'scatter' => 's',
// Radar
// 'radar' => 'r',
// 'radar_fill' => 'rs',
// Maps
// 'map' => 't',
// Google-o-meter
// 'google_o_meter' => 'gom',
// QR
// 'qr' => 'qr',
);
/**
* Set the type of the chart
* @param $type Chart type as per $this->cht
*/
public function type($type) {
if (empty($this->cht[$type]))
throw new Kohana_Exception('Unknown chart type :type for :class',array(':type'=>$type,':class'=>get_class($this)));
$this->_type = $this->cht[$type];
return $this;
}
/**
* Count how many metrics are being graphed per side
* @param $side Side YL (left) OR YR (right)
*/
private function axiscount($side) {
$i = 0;
foreach ($this->_axis as $l => $axis)
if ($axis == $side)
$i++;
return $i;
}
/**
* Calculate our maximum for each side of the chart
*/
private function maxes() {
$result = array();
foreach ($this->_axis as $l => $axis) {
if (! isset($result[$axis]))
$result[$axis] = 0;
$result[$axis] += $this->_max[$l]*1.1; // @todo This scaleup should be configurable
}
return $result;
}
/** CHART FIELDS **/
private function chd() {
$result = array();
$maxes = $this->maxes();
// Perform our encoding
foreach ($this->_axis as $l => $axis)
array_push($result,$this->encode($this->_data[$l],$maxes[$axis]));
$prefix = (count($maxes) > 1) ? sprintf('%s:',$this->axiscount('yl')) : ':';
// If encoding is text, we need to separate the series with a |
return ($this->_encodetype == 't') ? $prefix.implode('|',$result) : $prefix.implode(',',$result);
}
private function chm() {
$result = array();
$sc = $this->seriescolors();
$i = 0;
foreach ($this->_axis as $l => $axis) {
if ($axis == 'yr')
array_push($result,sprintf('%s,%s,%s,%s,%s,%s','D',$sc[$i],$i,0,2,2));// @todo 'D,0,2,2' May need to be configurable
$i++;
}
return count($result) ? implode('|',$result) : '';
}
private function chxl() {
$result = array();
// @todo This should be performed better - it may be a wrong assumption that all keys in the series have data.
foreach ($this->_data as $series => $data)
// @todo Why the 0:?
return '0:|'.implode('|',array_keys($data));
}
private function chxr() {
$result = array();
$i = 1;
foreach ($this->maxes() as $key => $value)
array_push($result,sprintf('%s,0,%s,0',$i++,$value));
return implode('|',$result);
}
public function json() {}
/**
* Return URL that renders the chart
*/
public function render() {
return sprintf('<img src="%s?%s" alt="%s">',$this->_url,http_build_query($this->build()),_('Google Chart'));
}
/**
* Build the chart
*/
private function build() {
if ($this->_data ) {
return array(
'chf'=>'bg,s,FFFFFF00',
'cht'=>$this->_type,
'chs'=>sprintf('%sx%s',$this->_width,$this->_height),
'chtt'=>$this->_title,
'chbh'=>'a', // @todo This might need to be calculated, valid options (a,r);
'chg'=>'7.7,12.5,1,5', // @todo This should be calculated
'chco'=>implode(',',$this->seriescolors()),
'chdl'=>implode('|',array_keys($this->_axis)),
'chd'=>$this->_encodetype.$this->chd(),
'chm'=>$this->chm(),
'chxt'=>'x,y,r', // @todo configurable?
'chxl'=>$this->chxl(),
'chxr'=>$this->chxr(),
);
} else
return array();
}
/**
* Encode the series data
* @param $data String of data to encode
* @param $max The maximum to scale to
*/
private function encode($data,$max=NULL) {
$table = array();
$table['simple'] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$table['extend'] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.';
$size = array();
$size['simple'] = strlen($table['simple']);
$size['extend'] = strlen($table['extend']);
if (is_null($max) OR $max == 0)
$max = max($data) > 0 ? max($data) : 1;
$encode = '';
switch ($this->_encodetype) {
case 't' :
return join(',',$data);
case 's' :
foreach ($data as $v)
$encode .= ($v > -1) ? substr($table['simple'],($size['simple']-1)*($v/$max),1) : '_';
break;
case 'e' :
foreach ($data as $v) {
# Convert to a 0-4095 data range
$y = 4095*$v/$max;
$first = substr($table['extend'],floor($y/$size['extend']),1);
$second = substr($table['extend'],$y%$size['extend'],1);
$encode .= "$first$second";
}
break;
}
return $encode;
}
}
?>