2010-07-27 08:51:32 +04:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
# Простая OLAPообразная статистика
|
|
|
|
|
# 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-07-29 02:19:38 +04:00
|
|
|
|
static $current_spec;
|
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
|
|
|
|
);
|
2010-08-13 14:03:56 +04:00
|
|
|
|
static $specf = array('cell_field', 'cell_aggr', 'cell_func');
|
|
|
|
|
static $group_types = array(
|
|
|
|
|
'tables' => 'Несколько таблиц',
|
|
|
|
|
'tr' => 'По вертикали',
|
|
|
|
|
'td' => 'По горизонтали',
|
|
|
|
|
'cell' => 'В ячейке',
|
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-08-13 14:03:56 +04:00
|
|
|
|
|
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;
|
|
|
|
|
}
|
2010-08-13 14:03:56 +04:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
if ($v['options'])
|
2010-08-13 14:03:56 +04:00
|
|
|
|
{
|
|
|
|
|
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-08-13 14:03:56 +04:00
|
|
|
|
}
|
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;
|
2010-08-13 14:03:56 +04:00
|
|
|
|
$i = 0;
|
|
|
|
|
foreach (self::$group_types as $k => &$v)
|
|
|
|
|
$v = array('id' => $k, 'num' => $i++, 'name' => $v);
|
2010-07-27 08:51:32 +04:00
|
|
|
|
unset($v);
|
|
|
|
|
|
|
|
|
|
$wh = array();
|
|
|
|
|
$where = array();
|
2010-08-13 14:03:56 +04:00
|
|
|
|
$group_fields = array();
|
2010-08-12 16:30:25 +04:00
|
|
|
|
foreach ($request as $k => $v)
|
2010-07-27 08:51:32 +04:00
|
|
|
|
{
|
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-12 16:30:25 +04:00
|
|
|
|
if (!$fd || !strlen($v))
|
2010-07-27 08:51:32 +04:00
|
|
|
|
continue;
|
2010-08-12 16:30:25 +04: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
|
|
|
|
}
|
2010-08-13 14:03:56 +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"];
|
|
|
|
|
if (self::$group_types[$gf['type']] && strlen($gf['field']))
|
|
|
|
|
{
|
|
|
|
|
$gf['num'] = count($group_fields);
|
|
|
|
|
$group_fields[] = $gf;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-27 08:51:32 +04:00
|
|
|
|
}
|
2010-08-13 14:03:56 +04:00
|
|
|
|
usort($group_fields, 'OLAP::group_type_cmp');
|
2010-07-27 08:51:32 +04:00
|
|
|
|
|
|
|
|
|
$template->vars(array(
|
|
|
|
|
'wh' => $wh,
|
2010-08-13 14:03:56 +04:00
|
|
|
|
'group_fields' => $group_fields,
|
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),
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
if (!$request['build'])
|
|
|
|
|
{
|
|
|
|
|
$vars = array();
|
|
|
|
|
foreach (self::$specf as $f)
|
|
|
|
|
$vars[$f] = $$f;
|
|
|
|
|
$template->vars($vars);
|
|
|
|
|
print $template->parse('admin_olap.tpl');
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-29 02:19:38 +04:00
|
|
|
|
self::$current_spec = array();
|
2010-07-27 08:51:32 +04:00
|
|
|
|
foreach(self::$specf as $x)
|
2010-07-29 02:19:38 +04:00
|
|
|
|
{
|
2010-07-27 08:51:32 +04:00
|
|
|
|
$$x = $request[$x];
|
2010-07-29 02:19:38 +04:00
|
|
|
|
self::$current_spec[$x] = $request[$x];
|
|
|
|
|
}
|
2010-07-27 08:51:32 +04:00
|
|
|
|
|
2010-07-27 09:39:12 +04:00
|
|
|
|
$fields = array();
|
|
|
|
|
$options = array('GROUP BY' => array());
|
2010-08-13 14:03:56 +04:00
|
|
|
|
foreach ($group_fields as $i => &$v)
|
2010-07-28 19:07:42 +04:00
|
|
|
|
{
|
2010-08-13 14:03:56 +04:00
|
|
|
|
$v['sql'] = self::sql_trans_field($v['field'], $v['func']);
|
|
|
|
|
$fields[] = $v['sql'].' gf_'.$i;
|
|
|
|
|
$options['GROUP BY'][] = $v['sql'];
|
2010-07-28 19:07:42 +04:00
|
|
|
|
}
|
2010-08-13 14:03:56 +04:00
|
|
|
|
|
2010-07-27 09:39:12 +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']).' cell_field';
|
2010-07-27 08:51:32 +04:00
|
|
|
|
else
|
|
|
|
|
{
|
2010-07-27 09:39:12 +04:00
|
|
|
|
$do_aggr = true;
|
|
|
|
|
$fields[] = $cell_sql.' cell_field';
|
|
|
|
|
$options['GROUP BY'][] = $cell_sql;
|
2010-07-27 08:51:32 +04:00
|
|
|
|
}
|
2010-07-27 09:39:12 +04:00
|
|
|
|
|
|
|
|
|
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), $options, MS_RESULT);
|
2010-08-13 14:03:56 +04:00
|
|
|
|
$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
|
|
|
|
{
|
2010-08-13 14:03:56 +04:00
|
|
|
|
$dv = &$data;
|
|
|
|
|
$tdv = &$tdkeys;
|
|
|
|
|
foreach ($group_fields as $i => &$v)
|
|
|
|
|
{
|
|
|
|
|
$v['keys'][$r["gf_$i"]] = true;
|
|
|
|
|
$dv = &$dv[$r["gf_$i"]];
|
|
|
|
|
if ($v['type'] == 'tables' || $v['type'] == 'td')
|
|
|
|
|
{
|
|
|
|
|
if (!$tdkeys[$r["gf_$i"]])
|
|
|
|
|
$tdkeys[$r["gf_$i"]] = array();
|
|
|
|
|
$tdv = &$tdv[$r["gf_$i"]];
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-27 09:39:12 +04:00
|
|
|
|
if ($do_aggr)
|
2010-08-13 14:03:56 +04:00
|
|
|
|
call_user_func_array('self::aggr_update_'.$cell_aggr, array(&$dv, &$r['cell_field']));
|
2010-07-28 19:07:42 +04:00
|
|
|
|
else
|
2010-08-13 14:03:56 +04:00
|
|
|
|
$dv = $r['cell_field'];
|
2010-07-28 19:07:42 +04:00
|
|
|
|
}
|
2010-07-27 08:51:32 +04:00
|
|
|
|
|
2010-08-13 14:03:56 +04:00
|
|
|
|
if ($do_aggr && is_callable('self::aggr_finish_'.$cell_aggr))
|
2010-07-27 09:39:12 +04:00
|
|
|
|
{
|
2010-08-13 14:03:56 +04:00
|
|
|
|
$code = '';
|
|
|
|
|
$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
|
|
|
|
|
2010-08-13 14:03:56 +04:00
|
|
|
|
foreach ($group_fields as &$v)
|
2010-07-27 09:39:12 +04:00
|
|
|
|
{
|
2010-08-13 14:03:56 +04:00
|
|
|
|
if (strlen($v['sort_aggr']) && strlen($v['sort_field']))
|
|
|
|
|
{
|
|
|
|
|
$fields = array($v['sql'].' f');
|
|
|
|
|
$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' => $v['sql']), MS_RESULT);
|
|
|
|
|
while ($r = mysql_fetch_row($result))
|
|
|
|
|
$v['sort'][$r[0]] = $r[1];
|
|
|
|
|
}
|
|
|
|
|
$v['keys'] = array_keys($v['keys']);
|
|
|
|
|
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-13 14:03:56 +04:00
|
|
|
|
$tables = self::build_tables($group_fields, $data, $tdkeys);
|
2010-07-27 08:51:32 +04:00
|
|
|
|
|
|
|
|
|
$vars = array(
|
|
|
|
|
'build' => 1,
|
2010-08-13 14:03:56 +04:00
|
|
|
|
'tables' => $tables,
|
2010-07-27 08:51:32 +04:00
|
|
|
|
'rpt_link' => "?".http_build_query($_GET+$_POST),
|
|
|
|
|
'csv_link' => "?csv=1&".http_build_query($_GET+$_POST),
|
|
|
|
|
'memory' => file_size_string(memory_get_usage()),
|
|
|
|
|
'time_elapsed' => sprintf("%.3f", microtime(true)-$time_start),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
foreach (self::$specf as $f)
|
|
|
|
|
$vars[$f] = $$f;
|
|
|
|
|
|
|
|
|
|
$template->vars($vars);
|
|
|
|
|
if ($request['csv'])
|
|
|
|
|
{
|
|
|
|
|
self::$is_html_format = false;
|
|
|
|
|
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-08-13 14:03:56 +04:00
|
|
|
|
static function recurse_tables($fields, &$bytype, &$data, &$tdkeys)
|
|
|
|
|
{
|
|
|
|
|
if ($fields && $fields[0]['type'] == 'tables')
|
|
|
|
|
{
|
|
|
|
|
$tables = array();
|
|
|
|
|
$my = array_shift($fields);
|
|
|
|
|
foreach ($my['keys'] as $k)
|
|
|
|
|
{
|
|
|
|
|
if (array_key_exists($k, $data))
|
|
|
|
|
{
|
|
|
|
|
$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);
|
|
|
|
|
$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)
|
|
|
|
|
return self::field_format(self::$current_spec['cell_field'], self::$current_spec['cell_func'], self::$current_spec['cell_aggr'], $data);
|
|
|
|
|
$my = array_shift($fields);
|
|
|
|
|
if ($my['type'] == 'tr')
|
|
|
|
|
{
|
|
|
|
|
$rows = array();
|
|
|
|
|
foreach ($my['keys'] as $k)
|
|
|
|
|
{
|
|
|
|
|
if (array_key_exists($k, $data))
|
|
|
|
|
{
|
|
|
|
|
$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 ($my['keys'] as $k)
|
|
|
|
|
{
|
|
|
|
|
if (array_key_exists($k, $tdkeys))
|
|
|
|
|
{
|
|
|
|
|
$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 ($my['keys'] as $k)
|
|
|
|
|
{
|
|
|
|
|
if (array_key_exists($k, $data))
|
|
|
|
|
{
|
|
|
|
|
$list .= '<li>'.self::field_format($my['field'], $my['func'], '', $k).': ';
|
|
|
|
|
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 ($my['keys'] as $k)
|
|
|
|
|
{
|
|
|
|
|
if (array_key_exists($k, $data))
|
|
|
|
|
{
|
|
|
|
|
$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]);
|
|
|
|
|
$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();
|
|
|
|
|
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);
|
|
|
|
|
}
|
2010-08-13 14:03:56 +04:00
|
|
|
|
static function group_type_cmp($a, $b)
|
|
|
|
|
{
|
|
|
|
|
$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-07-28 19:07:42 +04:00
|
|
|
|
if ($o && $o[$value])
|
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))
|
2010-08-13 14:03:56 +04:00
|
|
|
|
{
|
|
|
|
|
$value = sprintf(self::$decimal, $value);
|
|
|
|
|
$value = preg_replace('/\.0+$|(\.\d*?)0+$/', '\1', $value);
|
|
|
|
|
return $value;
|
|
|
|
|
}
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}
|