olap/olap.php

729 lines
28 KiB
PHP
Raw Normal View History

2010-07-27 08:51:32 +04:00
<?php
# Простая OLAPообразная статистика
# См. http://yourcmc.ru/wiki/Общий_вид_статистики
2010-07-27 08:51:32 +04:00
# admin_olap.tpl, admin_olap_csv.tpl
# (c) Vitaliy Filippov 2010
$is_adm = true;
require_once 'lib/templating.php';
require_once 'olap_config.php';
OLAP::execute($_REQUEST);
exit;
class OLAP
{
static $sources;
static $current_srcid, $current_src;
2010-08-14 13:35:48 +04:00
static $add_spanned;
2010-07-27 08:51:32 +04:00
static $functions = array(
2010-07-27 09:39:12 +04:00
'year' => array('name' => 'Год', 'time_format' => 'Y', 'sql' => 'SUBSTR($,1,4)'),
'month' => array('name' => 'Месяц', 'time_format' => 'Y-m', 'sql' => 'SUBSTR($,1,7)'),
'day' => array('name' => 'Дата', 'time_format' => 'Y-m-d', 'sql' => 'SUBSTR($,1,10)'),
'weekday' => array('name' => 'День недели', 'time_format' => 'N', 'sql' => 'WEEKDAY($)'),
'week' => array('name' => '№ недели', 'time_format' => 'W', 'sql' => 'WEEK($)'),
'hour' => array('name' => 'Час', 'time_format' => 'H', 'sql' => 'HOUR($)'),
'minute' => array('name' => 'Минута', 'time_format' => 'H:i', 'sql' => 'DATE_FORMAT($, "%k:%i")'),
'second' => array('name' => 'Секунда', 'time_format' => 'H:i:s', 'sql' => 'DATE_FORMAT($, "%k:%i:%s")'),
2010-07-27 08:51:32 +04:00
);
static $aggregates = array(
2010-07-29 02:19:38 +04:00
'count' => array('name' => 'Количество', 'sql' => 'COUNT($)'),
'sum' => array('name' => 'Сумма', 'sql' => 'SUM($)'),
'avg' => array('name' => 'Среднее', 'sql' => 'AVG($)'),
'min' => array('name' => 'Минимум', 'sql' => 'MIN($)'),
'max' => array('name' => 'Максимум', 'sql' => 'MAX($)'),
'stddev' => array('name' => 'Дисперсия', 'sql' => 'STDDEV($)'),
'n_uniq' => array('name' => 'Количество уникальных', 'sql' => 'COUNT(DISTINCT $)'),
2010-07-28 19:07:42 +04:00
);
static $group_types = array(
'tables' => 'Несколько таблиц',
'tr' => 'По вертикали',
'td' => 'По горизонтали',
'cell' => 'В ячейке',
2010-11-06 01:15:15 +03:00
'graph' => 'График',
2010-07-27 08:51:32 +04:00
);
2010-08-14 13:35:48 +04:00
static $cell = array();
2010-07-27 08:51:32 +04:00
static function execute($request)
{
global $template;
set_time_limit(300);
$time_start = microtime(true);
$template->compiletime_functions['fformat'] = "OLAP::tpl_field_format";
2010-07-30 10:16:33 +04:00
foreach (self::$sources as $k => &$s)
$s['id'] = $k;
/* выбираем источник данных */
2010-07-27 08:51:32 +04:00
self::$current_srcid = $request['datasource'];
self::$current_src = self::$sources[self::$current_srcid];
$template->vars('sources', array_values(self::$sources));
if (!self::$current_src)
{
$template->vars('select', true);
print $template->parse('admin_olap.tpl');
exit;
}
/* задаём параметры соединения с БД из источника*/
global $db, $dbhost, $dbuser, $dbpwd;
if (self::$current_src['db'])
list($db, $dbhost, $dbuser, $dbpwd) = self::$current_src['db'];
/* дополняем всяким бредом всякий бред */
2010-07-27 08:51:32 +04:00
foreach (self::$current_src['fielddescs'] as $k => &$v)
{
$v['id'] = $k;
2010-08-31 02:16:06 +04:00
if ($v['autofilter'])
{
$f = $v['sql'];
if (!$f)
$f = $k;
$v['options'] = mysql_select(self::$current_src['tables'], "$f id, $f name", self::$current_src['where'], array('GROUP BY' => $f));
}
2010-07-27 08:51:32 +04:00
if ($v['options'])
{
if (is_callable($v['options']))
$v['options'] = call_user_func($v['options']);
2010-07-27 08:51:32 +04:00
foreach ($v['options'] as &$o)
$v['options_hash'][$o['id']] = $o['name'];
}
2010-07-27 08:51:32 +04:00
}
foreach (self::$functions as $k => &$v)
$v['id'] = $k;
foreach (self::$aggregates as $k => &$v)
$v['id'] = $k;
unset($v);
2010-08-14 13:35:48 +04:00
$i = 0;
foreach (self::$group_types as $k => $v)
self::$group_types[$k] = array('id' => $k, 'num' => $i++, 'name' => $v);
2010-07-27 08:51:32 +04:00
/* читаем параметры запроса */
2010-07-27 08:51:32 +04:00
$wh = array();
$where = array();
$group_fields = array();
2010-08-12 16:30:25 +04:00
foreach ($request as $k => $v)
2010-07-27 08:51:32 +04:00
{
/* условия выборки: where-ОПЕРАЦИЯ-ПОЛЕ = ЗНАЧЕНИЕ */
2010-08-12 16:30:25 +04:00
if (substr($k, 0, 6) == 'where-')
2010-07-27 08:51:32 +04:00
{
2010-08-12 16:30:25 +04:00
list($t, $f) = explode('-', substr($k, 6), 2);
2010-07-27 08:51:32 +04:00
$fd = self::$current_src['fielddescs'][$f];
2010-08-31 02:16:06 +04:00
if (!$fd)
{
$f = str_replace('_', ' ', $f);
$fd = self::$current_src['fielddescs'][$f];
}
2010-08-12 16:30:25 +04:00
if (!$fd || !strlen($v))
2010-07-27 08:51:32 +04:00
continue;
2010-11-01 22:22:42 +03:00
$wh[$k] = $v;
2010-07-27 08:51:32 +04:00
if ($fd['is_time'])
2010-08-12 16:30:25 +04:00
$v = Template::timestamp($v, self::$current_src['fielddescs'][$f]['format']);
2010-07-28 19:07:42 +04:00
$dn = $fd['sql'];
2010-07-27 08:51:32 +04:00
if (!$dn)
$dn = $f;
2010-07-28 19:07:42 +04:00
$v = mysql_ecranize($v);
2010-07-27 08:51:32 +04:00
if ($t == 'eq')
2010-07-28 19:07:42 +04:00
$where[] = "$dn = $v";
2010-08-12 16:30:25 +04:00
elseif ($t == 'ge' && $fd['le_ge'])
2010-07-28 19:07:42 +04:00
$where[] = "$dn >= $v";
2010-08-12 16:30:25 +04:00
elseif ($t == 'le' && $fd['le_ge'])
2010-07-28 19:07:42 +04:00
$where[] = "$dn <= $v";
2010-08-12 16:30:25 +04:00
else
unset($wh[$k]);
2010-07-27 08:51:32 +04:00
}
/* описание группировки */
elseif (substr($k, 0, 11) == 'group-type-')
{
$i = substr($k, 11);
$gf = array();
foreach(array('type', 'field', 'func', 'sort_dir', 'sort_field', 'sort_aggr', 'sort_func') as $k)
$gf[$k] = $request["group-$k-$i"];
2010-11-06 01:15:15 +03:00
if (self::$group_types[$gf['type']] && strlen($gf['field']) &&
($gf['type'] != 'graph' || !$request['csv']))
{
$gf['num'] = count($group_fields);
$group_fields[] = $gf;
}
}
2010-07-27 08:51:32 +04:00
}
usort($group_fields, 'OLAP::group_type_cmp');
2010-07-27 08:51:32 +04:00
2010-08-14 13:35:48 +04:00
$nfields = intval($request['field_count']);
if ($nfields <= 0)
$nfields = 5;
if (count($group_fields) > $nfields)
$nfields = count($group_fields);
/* описание показателя */
2010-08-14 13:35:48 +04:00
foreach(array('field', 'func', 'aggr') as $x)
self::$cell[$x] = $request["cell_$x"];
2010-07-27 08:51:32 +04:00
$template->vars(array(
'wh' => $wh,
'group_fields' => $group_fields,
2010-08-14 13:35:48 +04:00
'cell' => &self::$cell,
'group_types' => array_values(self::$group_types),
'field_count' => $nfields,
2010-07-27 08:51:32 +04:00
'srcid' => self::$current_srcid,
'src' => self::$current_src,
'fielddescs' => array_values(self::$current_src['fielddescs']),
'functions' => array_values(self::$functions),
'aggregates' => array_values(self::$aggregates),
));
2010-08-14 13:35:48 +04:00
$cell = &self::$cell;
2010-07-27 08:51:32 +04:00
if (!$request['build'])
{
print $template->parse('admin_olap.tpl');
exit;
}
2010-07-27 09:39:12 +04:00
$fields = array();
$group_by = array();
foreach ($group_fields as $i => &$v)
2010-07-28 19:07:42 +04:00
{
$v['sql'] = self::sql_trans_field($v['field'], $v['func']);
2010-11-01 22:22:42 +03:00
$v['keys'] = array();
$fields[] = $v['sql'].' f'.$i;
$group_by[] = $v['sql'];
2010-07-28 19:07:42 +04:00
}
2010-08-14 13:35:48 +04:00
$cell['sql'] = self::sql_trans_field($cell['field'], $cell['func']);
if (self::$aggregates[$cell['aggr']]['sql'])
$fields[] = str_replace('$', $cell['sql'], self::$aggregates[$cell['aggr']]['sql']).' c';
2010-07-27 08:51:32 +04:00
else
{
/* поддерживаются агрегатные функции, вычисляемые внутри PHP, а не в базе */
2010-07-27 09:39:12 +04:00
$do_aggr = true;
2010-08-14 13:35:48 +04:00
$fields[] = $cell['sql'].' c';
$group_by[] = $cell['sql'];
2010-07-27 08:51:32 +04:00
}
2010-07-27 09:39:12 +04:00
/* сохраняем данные в $data и наборы значений для колонок в $tdkeys */
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), array('GROUP BY' => $group_by), MS_RESULT);
$data = array();
$tdkeys = array();
2010-07-27 09:39:12 +04:00
while ($r = mysql_fetch_assoc($result))
2010-07-27 08:51:32 +04:00
{
$dv = &$data;
$tdv = &$tdkeys;
foreach ($group_fields as $i => &$v)
{
$v['keys'][$r["f$i"]] = true;
$dv = &$dv[$r["f$i"]];
if ($v['type'] == 'tables' || $v['type'] == 'td')
{
if (!$tdv[$r["f$i"]])
$tdv[$r["f$i"]] = array();
$tdv = &$tdv[$r["f$i"]];
}
}
2010-07-27 09:39:12 +04:00
if ($do_aggr)
2010-08-14 13:35:48 +04:00
call_user_func_array('self::aggr_update_'.$cell['aggr'], array(&$dv, &$r['c']));
2010-07-28 19:07:42 +04:00
else
2010-08-14 13:35:48 +04:00
$dv = $r['c'];
2010-07-28 19:07:42 +04:00
}
2010-07-27 08:51:32 +04:00
/* поддерживается "завершение" вычисления агрегатных функций, которые вне базы */
2010-08-14 13:35:48 +04:00
if ($do_aggr && is_callable('self::aggr_finish_'.$cell['aggr']))
2010-07-27 09:39:12 +04:00
{
$code = '';
2010-08-14 13:35:48 +04:00
$upd = 'self::aggr_finish_'.$cell['aggr'].'(&$v'.(count($group_fields)-1);
foreach ($group_fields as $i => &$v)
{
$code .= 'foreach('.($i ? '$v'.($i-1) : '$data').' as $k'.$i.' => $v'.$i.') ';
$upd .= ', $k'.$i;
}
$code .= $upd;
eval($code);
2010-07-27 09:39:12 +04:00
}
2010-07-27 08:51:32 +04:00
/* читаем значения, по которым следует сортировать значения полей группировки */
foreach ($group_fields as $i => &$v)
2010-07-27 09:39:12 +04:00
{
$fields = array();
$group_by = array();
if (strlen($v['sort_aggr']) && strlen($v['sort_field']))
{
$code = 'while($r = mysql_fetch_assoc($result)) { $v[\'sort\']';
foreach ($group_fields as $j => $prev)
{
if ($j > $i)
break;
/* сюда также включается само поле $v! */
if ($prev['type'] == 'tables' || $prev['type'] == $v['type'])
{
$fields[] = $prev['sql']." f$j";
$group_by[] = $prev['sql'];
$code .= "[\$r['f$j']]";
}
}
$code .= ' = $r[\'s\']; }';
$v['sort_sql'] = self::sql_trans_field($v['sort_field'], $v['sort_func']);
$fields[] = str_replace('$', $v['sort_sql'], self::$aggregates[$v['sort_aggr']]['sql']).' s';
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), array('GROUP BY' => $group_by), MS_RESULT);
eval($code);
}
$v['keys'] = array_keys($v['keys']);
if (count($group_by) <= 1)
self::do_sort($v['keys'], $v['sort'], $v['sort_dir']);
2010-07-27 09:39:12 +04:00
}
2010-07-27 08:51:32 +04:00
2010-08-14 13:35:48 +04:00
if ($request['csv'])
self::$add_spanned = true;
2010-07-27 08:51:32 +04:00
$vars = array(
'build' => 1,
'rpt_link' => "?".http_build_query($_GET+$_POST),
);
2010-11-06 01:15:15 +03:00
if ($group_fields[0]['type'] == 'graph')
$vars['graphs'] = self::build_graphs($group_fields, $data, $tdkeys);
else
{
$vars['tables'] = self::build_tables($group_fields, $data, $tdkeys);
$vars['csv_link'] = "?csv=1&".http_build_query($_GET+$_POST);
}
$vars['memory'] = file_size_string(memory_get_usage());
$vars['time_elapsed'] = sprintf("%.3f", microtime(true)-$time_start);
2010-07-27 08:51:32 +04:00
$template->vars($vars);
if ($request['csv'])
{
header("Content-Type: text/csv; charset=windows-1251");
header("Content-Disposition: inline; filename=OLAP-".date('YmdHis').".csv");
$out = $template->parse('admin_olap_csv.tpl');
print iconv("utf-8", "windows-1251", $out);
}
else
print $template->parse('admin_olap.tpl');
}
2010-11-06 01:15:15 +03:00
static $graph_scale = 600; /* пикселей */
static $graph_logarithmic = false;
static function build_graphs($fields, &$data, &$tdkeys)
{
$my = array_shift($fields);
$keys = $my['sort'] ? array_keys($my['sort']) : $my['keys'];
self::do_sort($keys, $my['sort'], $my['sort_dir']);
$graphs = array();
if (count($fields))
{
foreach ($fields as &$v)
if ($v['sort'])
$v['orig_sort'] = &$v['sort'];
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$gr = self::build_graphs($fields, $data[$k], $tdkeys);
foreach ($gr as &$g)
{
array_unshift($g['desc'], array(
'field' => self::$current_src['fielddescs'][$my['field']]['name'],
'func' => self::$functions[$my['func']]['name'],
'value' => self::field_format($my['field'], '', '', $k)
));
$graphs[] = $g;
}
}
}
}
else
{
$gr = array(
'desc' => array(),
'field' => self::$current_src['fielddescs'][$my['field']]['name'],
'func' => self::$functions[$my['func']]['name'],
'data' => array(),
'max' => NULL,
'min' => NULL,
);
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
2010-11-07 00:23:02 +03:00
$v = $ov = floatval($data[$k]);
2010-11-06 01:15:15 +03:00
if (self::$graph_logarithmic)
{
if ($v >= 1)
$v = 1+log($v);
elseif ($v <= -1)
$v = -1-log(-$v);
}
2010-11-07 00:23:02 +03:00
$ov = str_replace('.00', '', sprintf("%.2f", $ov));
$gr['data'][] = array(self::field_format($my['field'], '', '', $k), $ov, $v, 0);
2010-11-06 01:15:15 +03:00
if ($gr['max'] === NULL || $gr['max'] < $v)
$gr['max'] = $v;
if ($gr['min'] === NULL || $gr['min'] > $v)
$gr['min'] = $v;
}
}
if ($gr['min'] >= 0)
$gr['min'] = 0;
else
$gr['bi_sign'] = true;
if ($gr['max'] > $gr['min'])
{
if ($gr['bi_sign'])
{
foreach ($gr['data'] as &$d)
{
2010-11-07 00:23:02 +03:00
if ($d[2] >= 0)
2010-11-06 01:15:15 +03:00
{
2010-11-07 00:23:02 +03:00
$d[2] = intval($d[2]/$gr['max']*self::$graph_scale/2);
$d[3] = 0;
2010-11-06 01:15:15 +03:00
}
else
{
2010-11-07 00:23:02 +03:00
$d[3] = intval($d[2]/$gr['min']*self::$graph_scale/2);
$d[2] = 0;
2010-11-06 01:15:15 +03:00
}
}
}
else
2010-11-07 00:23:02 +03:00
{
2010-11-06 01:15:15 +03:00
foreach ($gr['data'] as &$d)
2010-11-07 00:23:02 +03:00
$d[2] = intval($d[2]/$gr['max']*self::$graph_scale);
}
2010-11-06 01:15:15 +03:00
}
$graphs[] = $gr;
}
return $graphs;
}
static function recurse_tables($fields, &$bytype, &$data, &$tdkeys)
{
if ($fields && $fields[0]['type'] == 'tables')
{
$tables = array();
$my = array_shift($fields);
foreach ($fields as &$v)
if ($v['sort'])
$v['orig_sort'] = &$v['sort'];
$keys = $my['sort'] ? array_keys($my['sort']) : $my['keys'];
self::do_sort($keys, $my['sort'], $my['sort_dir']);
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$sub = self::recurse_tables($fields, $bytype, $data[$k], $tdkeys[$k]);
foreach ($sub as &$t)
{
array_unshift($t['desc'], array(
'field' => self::$current_src['fielddescs'][$my['field']]['name'],
'func' => self::$functions[$my['func']]['name'],
'value' => self::field_format($my['field'], '', '', $k)
));
$tables[] = $t;
}
}
}
return $tables;
}
$tdhead = self::recurse_head($bytype['td'], $tdkeys);
$trhead = self::recurse_head($bytype['tr'], $data);
$cells = self::recurse_cells($fields, $data, $tdkeys);
2010-11-03 19:53:14 +03:00
if (!is_array($cells))
$cells = array(array($cells));
$rows = array();
foreach ($tdhead as $i => &$v)
{
foreach ($v as &$h)
$h['class'] = "h$i";
$vcopy = $v;
if ($trhead)
array_unshift($vcopy, array('class' => 'empty', 'colspan' => count($trhead)));
$rows[] = $vcopy;
}
foreach ($trhead as $i => &$v)
{
$rownum = count($tdhead);
foreach ($v as &$h)
{
$rows[$rownum][] = array('text' => $h['text'], 'rowspan' => $h['colspan'], 'class' => "v$i");
$rownum += $h['colspan'];
}
}
$i = count($tdhead);
foreach ($cells as &$v)
{
if (!$rows[$i])
$rows[$i] = array();
$rows[$i] = array_merge($rows[$i], $v);
$i++;
}
foreach ($tdhead as &$v)
{
if ($v[0]['sort'])
{
$r = array();
if ($trhead)
$r[] = array('class' => 'empty', 'colspan' => count($trhead));
foreach ($v as &$h)
$r[] = array('text' => $h['sort'], 'colspan' => $h['colspan'], 'class' => 'hsort');
$rows[] = $r;
}
}
foreach (array_reverse($trhead) as $v)
{
if ($v[0]['sort'])
{
for ($rownum = 0; $rownum < count($tdhead); $rownum++)
$rows[$rownum][] = array('class' => 'empty');
foreach ($v as &$h)
{
$rows[$rownum][] = array('text' => $h['sort'], 'rowspan' => $h['colspan'], 'class' => 'vsort');
$rownum += $h['colspan'];
}
for (; $rownum < count($rows); $rownum++)
$rows[$rownum][] = array('class' => 'empty');
}
}
ksort($rows);
$table = array(
'desc' => array(),
'rows' => $rows,
);
return array($table);
}
static function recurse_cells($fields, &$data, &$tdkeys)
{
if (!$fields)
2010-08-14 13:35:48 +04:00
return self::field_format(self::$cell['field'], self::$cell['func'], self::$cell['aggr'], $data);
$my = array_shift($fields);
foreach ($fields as &$v)
if ($v['sort'])
$v['orig_sort'] = &$v['sort'];
$keys = $my['sort'] ? array_keys($my['sort']) : $my['keys'];
self::do_sort($keys, $my['sort'], $my['sort_dir']);
if ($my['type'] == 'tr')
{
$rows = array();
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['type'] == $my['type'] && $v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$r = self::recurse_cells($fields, $data[$k], $tdkeys);
if (is_array($r))
$rows = array_merge($rows, $r);
else
$rows[] = array($r);
}
}
return $rows;
}
elseif ($my['type'] == 'td')
{
$cols = array();
foreach ($keys as $k)
{
if (array_key_exists($k, $tdkeys))
{
foreach ($fields as &$v)
if ($v['type'] == $my['type'] && $v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$r = self::recurse_cells($fields, $data[$k], $tdkeys[$k]);
if (is_array($r))
$cols = array_merge($cols, $r[0]);
else
$cols[] = $r;
}
}
return array($cols);
}
else // if ($my['type'] == 'cell')
{
$list = '<ul>';
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['type'] == $my['type'] && $v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$list .= '<li>'.self::field_format($my['field'], $my['func'], '', $k).':&nbsp;';
if ($fields)
$list .= "\n";
$sub = self::recurse_cells($fields, $data[$k], $tdkeys);
$sub = str_replace('<li>', ' <li>', $sub);
$list .= $sub;
$list .= "</li>\n";
}
}
$list .= '</ul>';
return $list;
}
}
static function recurse_head($fields, &$data)
{
$rows = array();
if (!$fields)
return $rows;
$rows[] = array();
$my = array_shift($fields);
foreach ($fields as &$v)
if ($v['sort'])
$v['orig_sort'] = &$v['sort'];
$keys = $my['sort'] ? array_keys($my['sort']) : $my['keys'];
self::do_sort($keys, $my['sort'], $my['sort_dir']);
foreach ($keys as $k)
{
if (array_key_exists($k, $data))
{
foreach ($fields as &$v)
if ($v['type'] == $my['type'] && $v['orig_sort'])
$v['sort'] = &$v['orig_sort'][$k];
$span = 0;
if ($fields)
{
$subrows = self::recurse_head($fields, $data[$k]);
foreach ($subrows[0] as $td)
$span += $td['colspan'];
foreach ($subrows as $i => &$row)
{
if (!$rows[$i+1])
$rows[$i+1] = array();
$rows[$i+1] = array_merge($rows[$i+1], $row);
}
}
else
$span = 1;
$c = array('text' => self::field_format($my['field'], $my['func'], '', $k), 'colspan' => $span);
if ($my['sort_field'])
$c['sort'] = self::field_format($my['sort_field'], $my['sort_func'], $my['sort_aggr'], $my['sort'][$k]);
2010-08-14 13:35:48 +04:00
if (self::$add_spanned)
{
$c['colspan'] = 1;
$rows[0][] = $c;
$c['spanned'] = 1;
for ($i = 1; $i < $span; $i++)
$rows[0][] = $c;
}
else
$rows[0][] = $c;
}
}
return $rows;
}
static function array_refcopy(&$a)
{
$b = array();
foreach ($a as $k => &$v)
$b[$k] = &$v;
return $b;
}
static function build_tables($group_fields, &$data, &$tdkeys)
{
$bytype = array();
2010-11-01 22:22:42 +03:00
foreach ($group_fields as $gf)
$bytype[$gf['type']][] = $gf;
$tables = self::recurse_tables($group_fields, $bytype, $data, $tdkeys);
return $tables;
}
2010-07-27 09:39:12 +04:00
static function sql_trans_field($field, $func)
{
2010-07-28 19:07:42 +04:00
$fd = &self::$current_src['fielddescs'][$field];
2010-07-30 10:16:33 +04:00
if ($field === '')
$sql = '1';
else
{
$sql = $field;
if ($fd['sql'])
$sql = $fd['sql'];
}
2010-07-28 19:07:42 +04:00
if ($fd['is_time'] && $fd['format'] == TS_UNIX &&
2010-07-27 09:39:12 +04:00
(!self::$functions[$func] || self::$functions[$func]['time_format']))
$sql = "FROM_UNIXTIME($sql)";
if (self::$functions[$func])
$sql = str_replace('$', $sql, self::$functions[$func]['sql']);
return $sql;
}
2010-07-27 08:51:32 +04:00
static function do_sort(&$values, &$sortvalues, &$sort_dir)
{
$fn = 'sort';
$sort_dir = strtolower($sort_dir);
if ($sort_dir == 'desc')
$fn = 'rsort';
if ($sortvalues)
{
$ufn = $sort_dir == 'desc' ? "OLAP::custom_reverse_cmp" : "OLAP::custom_sort_cmp";
self::$sort = &$sortvalues;
usort($values, $ufn);
}
else
$fn($values);
}
static $sort;
static function custom_sort_cmp($a, $b)
{
return self::$sort[$a] < self::$sort[$b] ? -1 : (self::$sort[$a] > self::$sort[$b] ? 1 : 0);
}
static function custom_reverse_cmp($a, $b)
{
return self::$sort[$a] > self::$sort[$b] ? -1 : (self::$sort[$a] < self::$sort[$b] ? 1 : 0);
}
static function group_type_cmp($a, $b)
{
2010-11-06 01:15:15 +03:00
if ($a == 'graph')
return -1;
elseif ($b == 'graph')
return 1;
$r = self::$group_types[$a['type']]['num']-self::$group_types[$b['type']]['num'];
if ($r == 0)
$r = $a['num']-$b['num'];
return $r;
}
2010-07-27 08:51:32 +04:00
static $is_html_format = true;
2010-07-27 09:39:12 +04:00
static $decimal = '%.3f';
2010-07-27 08:51:32 +04:00
static function field_format($field, $func, $aggr, $value)
{
$o = &self::$current_src['fielddescs'][$field]['options_hash'];
2010-11-06 01:15:15 +03:00
if ($o && $o[$value] && !$aggr)
2010-07-27 08:51:32 +04:00
return $o[$value];
2010-07-27 09:39:12 +04:00
elseif (preg_match('/^-?\d+\.\d+$/s', $value))
{
$value = sprintf(self::$decimal, $value);
$value = preg_replace('/\.0+$|(\.\d*?)0+$/', '\1', $value);
return $value;
}
2010-11-03 19:53:14 +03:00
elseif (($proc = self::$current_src['fielddescs'][$field]['format_func']) &&
is_callable($proc))
{
return $proc($value, self::$is_html_format);
}
2010-07-27 08:51:32 +04:00
$fn = self::$is_html_format ? 'htmlspecialchars' : 'addslashes';
return $fn($value);
}
static function tpl_field_format($field, $func, $aggr, $value)
{
return "OLAP::field_format($field, $func, $aggr, $value)";
}
static function o_tsformat($f, &$row, $field)
{
$tf = "_t$field";
if (!$row[$tf])
$row[$tf] = Template::timestamp($row[$field]);
return date($f, $row[$tf]);
}
2010-11-06 01:15:15 +03:00
}