olap/olap.php

292 lines
11 KiB
PHP
Raw Normal View History

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;
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-27 09:39:12 +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($)'),
2010-07-27 08:51:32 +04:00
'distinct' => array('name' => 'Значения', 'cell_only' => true),
2010-07-27 09:39:12 +04:00
'n_uniq' => array('name' => 'Количество уникальных', 'sql' => 'COUNT(DISTINCT $)'),
2010-07-27 08:51:32 +04:00
);
static $specf = array('v_field', 'v_func', 'h_field', 'h_func', 'cell_field', 'cell_aggr', 'cell_func', 'v_sort_dir', 'v_sort_field', 'v_sort_aggr', 'v_sort_func', 'h_sort_dir', 'h_sort_field', 'h_sort_aggr', 'h_sort_func');
static function execute($request)
{
global $template;
set_time_limit(300);
$time_start = microtime(true);
$template->compiletime_functions['fformat'] = "OLAP::tpl_field_format";
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;
}
foreach (self::$current_src['fielddescs'] as $k => &$v)
{
$v['id'] = $k;
if ($v['options'])
foreach ($v['options'] as &$o)
$v['options_hash'][$o['id']] = $o['name'];
}
foreach (self::$functions as $k => &$v)
$v['id'] = $k;
foreach (self::$aggregates as $k => &$v)
$v['id'] = $k;
unset($v);
$wh = array();
$where = array();
foreach ($request as $k => $t)
{
if (substr($k, 0, 5) == 'type-')
{
$i = substr($k, 5);
$f = $request["field-$i"];
$v = $request["value-$i"];
$fd = self::$current_src['fielddescs'][$f];
if (!strlen($v))
continue;
$wh["type-$i"] = $t;
$wh["field-$i"] = $f;
$wh["value-$i"] = $v;
if ($fd['is_time'])
$v = Template::timestamp($v, self::$current_src['fielddefs'][$f]['format']);
$dn = $fd['dbname'];
if (!$dn)
$dn = $f;
if ($t == 'eq')
$where[$dn] = $v;
elseif ($t == 'ge')
$where[] = "$dn >= ".mysql_ecranize($v);
elseif ($t == 'le')
$where[] = "$dn <= ".mysql_ecranize($v);
}
}
$template->vars(array(
'wh' => $wh,
'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;
}
foreach(self::$specf as $x)
$$x = $request[$x];
2010-07-27 09:39:12 +04:00
$fields = array();
$options = array('GROUP BY' => array());
2010-07-27 08:51:32 +04:00
if ($v_field !== '')
{
2010-07-27 09:39:12 +04:00
$v_sql = self::sql_trans_field($v_field, $v_func);
$fields[] = $v_sql.' v_field';
$options['GROUP BY'][] = $v_sql;
2010-07-27 08:51:32 +04:00
}
if ($h_field !== '')
{
2010-07-27 09:39:12 +04:00
$h_sql = self::sql_trans_field($h_field, $h_func);
$fields[] = $h_sql.' h_field';
$options['GROUP BY'][] = $h_sql;
2010-07-27 08:51:32 +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);
$group = array();
$hkeys = array();
while ($r = mysql_fetch_assoc($result))
2010-07-27 08:51:32 +04:00
{
2010-07-27 09:39:12 +04:00
if ($do_aggr)
call_user_func_array('self::aggr_update_'.$cell_aggr, array(&$group[$r['v_field']][$r['h_field']], &$r['cell_field']));
2010-07-27 08:51:32 +04:00
else
2010-07-27 09:39:12 +04:00
$group[$r['v_field']][$r['h_field']] = $r['cell_field'];
$hkeys[$r['h_field']] = 1;
2010-07-27 08:51:32 +04:00
}
2010-07-27 09:39:12 +04:00
if ($do_aggr && is_callable('self::aggr_finish_'.$cell_aggr))
2010-07-27 08:51:32 +04:00
foreach ($group as $i => &$r)
foreach ($r as $j => &$c)
2010-07-27 09:39:12 +04:00
call_user_func_array('self::aggr_finish_'.$cell_aggr, array(&$c));
2010-07-27 08:51:32 +04:00
2010-07-27 09:39:12 +04:00
if ($v_field !== '' && $v_sort_aggr !== '' && $v_sort_field !== '')
{
$fields = array($v_sql.' v_field');
$vs_sql = self::sql_trans_field($v_sort_field, $v_sort_func);
$fields[] = str_replace('$', $vs_sql, self::$aggregates[$v_sort_aggr]['sql']).' v_sort_field';
$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))
$vsort[$r[0]] = $r[1];
}
2010-07-27 08:51:32 +04:00
2010-07-27 09:39:12 +04:00
if ($h_field !== '' && $h_sort_aggr !== '' && $h_sort_field !== '')
{
$fields = array($h_sql.' v_field');
$hs_sql = self::sql_trans_field($h_sort_field, $h_sort_func);
$fields[] = str_replace('$', $hs_sql, self::$aggregates[$h_sort_aggr]['sql']).' h_sort_field';
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), array('GROUP BY' => $h_sql), MS_RESULT);
while ($r = mysql_fetch_row($result))
$hsort[$r[0]] = $r[1];
}
2010-07-27 08:51:32 +04:00
$hkeys = array_keys($hkeys);
self::do_sort($hkeys, $hsort, $h_sort_dir);
$vkeys = array_keys($group);
self::do_sort($vkeys, $vsort, $v_sort_dir);
$vars = array(
'build' => 1,
'hkeys' => $hkeys,
'vkeys' => $vkeys,
'hsort' => $hsort,
'vsort' => $vsort,
'data' => $group,
'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-07-27 09:39:12 +04:00
static function sql_trans_field($field, $func)
{
$sql = $field;
if (self::$current_src['fielddescs'][$field]['is_time'] &&
self::$current_src['fielddescs'][$field]['format'] == TS_UNIX &&
(!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 $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'];
if ($aggr == 'distinct')
{
$r = '';
2010-07-27 09:39:12 +04:00
if ($value)
foreach($value as $k => $n)
$r .= self::field_format($field, $func, false, $k).': '.$n.(self::$is_html_format ? "<br />" : "")."\n";
2010-07-27 08:51:32 +04:00
return $r;
}
elseif ($o && $o[$value])
return $o[$value];
2010-07-27 09:39:12 +04:00
elseif (preg_match('/^-?\d+\.\d+$/s', $value))
return sprintf(self::$decimal, $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]);
}
/* Агрегатные функции и преобразования */
static function aggr_update_distinct(&$d, &$v) { $d[$v]++; }
}