Commit 0c482746 authored by David Metzler's avatar David Metzler
Browse files

New custom renderer for crosstabs. Doesn't yet support rowspan.

parent 2113b85a
......@@ -18,6 +18,7 @@ class FrxData extends FrxContext {
private $data_stack = array();
private $id_stack = array();
// For backward compatibility.
public static function instance() {
return Frx::Data();
......@@ -77,6 +78,7 @@ class FrxData extends FrxContext {
return $ret;
}
/**
* Get the value from the data.
* This is used by token_replace method to extract the data based on the path provided.
......
......@@ -81,7 +81,6 @@ class FrxFile {
$entry->include = !$default;
$entry->override = FALSE;
}
}
else {
if (!$entry->override) {
......
......@@ -29,6 +29,8 @@ class FrxReport {
private $ids;
private $data_passed = FALSE;
public $teng;
private $compareType; // Sort compare type
private $sort; // Fields to sort by
public $parms;
public $missing_parms = FALSE;
private $dom;
......@@ -163,11 +165,12 @@ class FrxReport {
// Shortcut process a text node
if ($node_type == XML_TEXT_NODE
|| $node_type == XML_ENTITY_REF_NODE
|| $node_type == XML_ENTITY_NODE
) { $text = $dom_node->textContent;
$o .= $this->teng->replace($text);
return $o;
|| $node_type == XML_ENTITY_REF_NODE
|| $node_type == XML_ENTITY_NODE)
{
$text = $dom_node->textContent;
$o .= $this->teng->replace($text);
return $o;
}
//Handle comment nodes
......@@ -258,11 +261,28 @@ class FrxReport {
else {
$nodes = (array)$data;
}
// Sort values
$sort = @(string)$frx['sort'];
if ($sort) {
$compare_type = @(string)$frx['compare'];
$this->sort($data, $sort, $compare_type);
}
// Group values
$group = @(string)$frx['group'];
if ($group) {
$nodes = $this->group($nodes, $group);
}
$i=0;
//$tmp_attrs = (array)$attrs;
if ($nodes) foreach ($nodes as $x) {
if ($group) {
Frx::Data()->setContext('group', $x[0]);
}
Frx::Data()->push($x, $id);
$i++;
$odd = $i & 1;
......@@ -778,6 +798,104 @@ class FrxReport {
}
return false;
}
public function setSort($sort, $compare_type='') {
if (!$compare_type) {
if (defined(SORT_NATURAL)) $compare_type = SORT_NATURAL;
}
else {
if (is_string($compare_type)) {
if (defined($compare_type)) {
$compare_type = constant($compare_type);
}
else {
$compare_type = SORT_REGULAR;
}
}
}
$this->compareType = $compare_type;
// Assume an array of sort algorithms
if (is_array($sort)) {
$this->sortCriteria = $sort;
}
else {
$this->sortCriteria = (array)$sort;
}
}
/**
* Comparison fucntion for user defined sorts.
*/
public function compareFunction($a, $b) {
$c=0;
foreach ($this->sortCriteria as $sort) {
//Get a value
$this->push($a, '_sort');
$va = $this->teng->replace($sort);
$this->pop();
$this->push($b, '_sort');
$vb = $this->teng->replace($sort);
switch ($this->compareType) {
case SORT_REGULAR:
$c = $c= $va < $vb ? -1 : ($va == $vb ? 0 : 1);
break;
case SORT_NUMERIC:
$va = floatval($va);
$vb = floatval($vb);
$c = $c= $va < $vb ? -1 : ($va == $vb ? 0 : 1);
break;
case SORT_STRING:
$c = strcasecmp($va, $vb);
break;
case SORT_NATURAL:
$c = strnatcasecmp($va, $vb);
break;
default:
$c = $c= $va < $vb ? -1 : ($va == $vb ? 0 : 1);
}
if ($c!==0) break;
}
return $c;
}
/**
* Sort the current data context if it is an array.
* @param $teng Token replacement engine to use for sort
* @param unknown $sort
* @param string $compare_type
*/
public function sort(&$data, $sort, $compare_type='') {
$this->setSort($sort, $compare_type);
if (is_array($data)) {
uasort($data, array($this, 'compareFunction'));
}
}
/**
* Iterate the data based on the provided path.
*
* @param $path xpath to iterate xml on
* @param $group grouping value
* @param $sort Sort criteria
*/
public function group($data, $group='') {
$rows = array();
if (is_array($data) || is_object($data)) {
foreach ($data as $row) {
Frx::Data()->push($row, '_group');
$gval = $this->teng->replace($group);
Frx::Data()->pop();
$rows[$gval][] = $row;
}
}
return $rows;
}
}
......@@ -45,7 +45,7 @@ class FrxSyntaxEngine {
* @param $key
* @return unknown_type
*/
protected function get_value( $key, $raw=FALSE) {
protected function get_value($key, $raw=FALSE) {
$context = '';
$raw_key = $key;
if ($key && strpos($key, '.')) {
......
......@@ -1755,7 +1755,7 @@ function forena_data_select_block_submit($form, &$form_state) {
//if there were not any parameters passed. Use the report parameters
if (!$parms) {
$r = FrxReportGenerator::instance()->get_report($values['report_name']);
$rpt_params = $r->parameters;
$rpt_params = isset($r->parameters) ? $r->parameters : array();
if ($rpt_params) foreach ($block_info['tokens'] as $index => $id) {
$parms[$id] = @$rpt_params[$id]['value'];
}
......
......@@ -1084,6 +1084,11 @@ function forena_forena_controls() {
'class' => 'FrxTitle',
);
$controls[] = array(
'file' => 'renderers/FrxCrosstab.inc',
'class' => 'FrxCrosstab',
);
$library_dir = FrxReportGenerator::instance()->configuration('library_path');
$library = rtrim($library_dir,'/') . '/SVGGraph/SVGGraph.php';
if (file_exists($library)) {
......
<?php
class FrxCrossTab extends FrxRenderer {
private $headers = array();
private $columns = array();
private $dim_columns = array();
private $group_columns = array();
private $dim_headers = array();
private $group_headers = array();
public function render() {
$variables = $this->mergedAttributes();
$path = isset($variables['path']) ? $variables['path'] : '*';
if (!$path) $path = "*";
$group = $variables['group'];
$dim = $variables['dim'];
// Get the current context
$data = Frx::Data()->currentContext();
// Generate the data nodes.
if (is_object($data)) {
if (method_exists($data, 'xpath')) {
$nodes = $data->xpath($path);
}
else {
$nodes = $data;
}
}
else {
$nodes = (array)$data;
}
// Group the data.
$data = $this->frxReport->group($nodes, $group);
$dim_headers = array();
$dim_rows = array();
$dim_values = array();
$rows = array();
foreach ($data as $gk => $group_rows) {
$row_copy = array_values($group_rows);
$dims = $this->frxReport->group($group_rows, $dim);
$rows[$gk] = $group_rows[0];
foreach($dims as $dk=>$r) {
$dims = array_values($r);
$dim_values[$dk] = $dk;
$dim_rows[$gk][$dk] = $r[0];
}
}
dpm($dim_rows, 'Dimensions');
dpm($rows, 'Rows');
dpm($dim_values, 'values');
// Default controling attributes
$this->defaultHeaders($dim_values);
$hrow = array();
foreach ($this->group_headers as $col) {
$cell = $col;
if (count($this->dim_columns) > 1) $cell['rowspan'] = 2;
$hrow[] = $cell;
}
// Add the dimension headers.
foreach ($dim_values as $dk) {
foreach ($this->dim_headers as $col) {
$cell = $col;
$cell['data'] = $dk;
$hrow [] = $cell;
}
}
$trows = array();
foreach ($rows as $k=>$row) {
Frx::Data()->push($row, '_group');
$trow = array();
// Base group
foreach($this->group_columns as $col) {
$cell = $col;
$cell['data'] = $this->teng->replace($col['data']);
$trow[] = $cell;
}
Frx::Data()->pop();
// Dimensions
$dim_data = $dim_rows[$k];
foreach($dim_values as $dk) {
$dim_row = isset($dim_data[$dk]) ? $dim_data[$dk] : array();
frx::Data()->push($dim_row, '_dim');
foreach($this->dim_columns as $col) {
$cell = $col;
$cell['data'] = $this->teng->replace($col['data']);
$trow[] = $cell;
}
frx::Data()->pop();
}
$trows[] = $trow;
}
$vars = array(
'header' => $hrow,
'rows' => $trows,
);
dpm($vars);
$output = theme('table', $vars);
return $output;
}
private function defaultHeaders() {
$node = $this->reportDocNode;
if ($node->thead && $node->thead->tr) {
foreach ($node->thead->tr->children() as $name => $cell) {
$hcol['data'] = (string)$cell;
$hcol['depth'] = 1;
$hcol['colspan'] = @(string)$cell['colspan'];
$hcol['class'] = @(string)$cell['class'];
$hcol['style'] = @(string)$cell['style'];
if ($name == 'th') {
$this->group_headers[] = $hcol;
}
else {
$this->dim_headers[] = $hcol;
}
}
}
if ($node->tbody && $node->tbody->tr) {
foreach ($node->tbody->tr->children() as $name => $cell) {
$col['data'] = @(string)$cell;
$col['class'] = @(string)$cell['class'];
$col['style'] = @(string)['style'];
if ($name == 'th') {
$this->group_columns[] = $col;
}
else {
$this->dim_columns[] = $col;
}
}
}
}
}
\ No newline at end of file
select * from user_distribution WHERE
state in (:state)
order by city, state
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment