OLAP interface + CSV

master
vitalif 2010-08-14 09:35:48 +00:00
parent 041ec8042c
commit 71e241150e
4 changed files with 119 additions and 104 deletions

View File

@ -17,6 +17,7 @@ class OLAP
static $sources;
static $current_srcid, $current_src;
static $current_spec;
static $add_spanned;
static $functions = array(
'year' => array('name' => 'Год', 'time_format' => 'Y', 'sql' => 'SUBSTR($,1,4)'),
'month' => array('name' => 'Месяц', 'time_format' => 'Y-m', 'sql' => 'SUBSTR($,1,7)'),
@ -36,13 +37,13 @@ class OLAP
'stddev' => array('name' => 'Дисперсия', 'sql' => 'STDDEV($)'),
'n_uniq' => array('name' => 'Количество уникальных', 'sql' => 'COUNT(DISTINCT $)'),
);
static $specf = array('cell_field', 'cell_aggr', 'cell_func');
static $group_types = array(
'tables' => 'Несколько таблиц',
'tr' => 'По вертикали',
'td' => 'По горизонтали',
'cell' => 'В ячейке',
);
static $cell = array();
static function execute($request)
{
@ -82,10 +83,10 @@ class OLAP
$v['id'] = $k;
foreach (self::$aggregates as $k => &$v)
$v['id'] = $k;
$i = 0;
foreach (self::$group_types as $k => &$v)
$v = array('id' => $k, 'num' => $i++, 'name' => $v);
unset($v);
$i = 0;
foreach (self::$group_types as $k => $v)
self::$group_types[$k] = array('id' => $k, 'num' => $i++, 'name' => $v);
$wh = array();
$where = array();
@ -129,33 +130,35 @@ class OLAP
}
usort($group_fields, 'OLAP::group_type_cmp');
$nfields = intval($request['field_count']);
if ($nfields <= 0)
$nfields = 5;
if (count($group_fields) > $nfields)
$nfields = count($group_fields);
foreach(array('field', 'func', 'aggr') as $x)
self::$cell[$x] = $request["cell_$x"];
$template->vars(array(
'wh' => $wh,
'group_fields' => $group_fields,
'cell' => &self::$cell,
'group_types' => array_values(self::$group_types),
'field_count' => $nfields,
'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),
));
$cell = &self::$cell;
if (!$request['build'])
{
$vars = array();
foreach (self::$specf as $f)
$vars[$f] = $$f;
$template->vars($vars);
print $template->parse('admin_olap.tpl');
exit;
}
self::$current_spec = array();
foreach(self::$specf as $x)
{
$$x = $request[$x];
self::$current_spec[$x] = $request[$x];
}
$fields = array();
$options = array('GROUP BY' => array());
foreach ($group_fields as $i => &$v)
@ -165,14 +168,14 @@ class OLAP
$options['GROUP BY'][] = $v['sql'];
}
$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';
$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';
else
{
$do_aggr = true;
$fields[] = $cell_sql.' cell_field';
$options['GROUP BY'][] = $cell_sql;
$fields[] = $cell['sql'].' c';
$options['GROUP BY'][] = $cell['sql'];
}
$result = mysql_select(self::$current_src['tables'], $fields, array_merge(self::$current_src['where'], $where), $options, MS_RESULT);
@ -194,15 +197,15 @@ class OLAP
}
}
if ($do_aggr)
call_user_func_array('self::aggr_update_'.$cell_aggr, array(&$dv, &$r['cell_field']));
call_user_func_array('self::aggr_update_'.$cell['aggr'], array(&$dv, &$r['c']));
else
$dv = $r['cell_field'];
$dv = $r['c'];
}
if ($do_aggr && is_callable('self::aggr_finish_'.$cell_aggr))
if ($do_aggr && is_callable('self::aggr_finish_'.$cell['aggr']))
{
$code = '';
$upd = 'self::aggr_finish_'.$cell_aggr.'(&$v'.(count($group_fields)-1);
$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.') ';
@ -227,6 +230,8 @@ class OLAP
self::do_sort($v['keys'], $v['sort'], $v['sort_dir']);
}
if ($request['csv'])
self::$add_spanned = true;
$tables = self::build_tables($group_fields, $data, $tdkeys);
$vars = array(
@ -238,13 +243,9 @@ class OLAP
'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');
@ -346,7 +347,7 @@ class OLAP
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);
return self::field_format(self::$cell['field'], self::$cell['func'], self::$cell['aggr'], $data);
$my = array_shift($fields);
if ($my['type'] == 'tr')
{
@ -430,7 +431,16 @@ class OLAP
$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;
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;

View File

@ -16,56 +16,58 @@
<form action="?" method="GET">
<input type="hidden" name="datasource" value="{s src.id}" />
<input type="hidden" name="build" value="1" />
<table>
<tr><td style="text-align: center; vertical-align: middle"><input type="submit" value=" Построить отчёт " /></td><td style="background-color: #e0e0e0; padding: 8px">
По горизонтали:<br />
<!-- SET fld = 'h' --><!-- INCLUDE admin_olap_field.tpl --><br />
Сортировка:<br />
<select style="width:100px" name="h_sort_dir">
<option value="asc">по возрастанию</option>
<option value="desc"<!-- IF eq(h_sort_dir, 'desc') --> selected<!-- END -->>по убыванию</option>
</select>
<select style="width:100px" name="h_sort_aggr">
<option value="">по значениям поля</option>
<!-- FOR f = aggregates -->
<!-- IF NOT f.cell_only -->
<option value="{s f.id}"<!-- IF eq(h_sort_aggr, f.id) --> selected<!-- END -->>{s f.name} другого поля:</option>
<!-- END -->
<!-- END -->
</select>
<!-- SET fld = 'h_sort' --><!-- INCLUDE admin_olap_field.tpl -->
</td></tr>
<tr><td style="background-color: #e0e0e0; padding: 8px">
По вертикали:<br />
<!-- SET fld = 'v' --><!-- INCLUDE admin_olap_field.tpl --><br />
Сортировка:<br />
<select style="width:100px" name="v_sort_dir">
<option value="asc">по возрастанию</option>
<option value="desc"<!-- IF eq(v_sort_dir, 'desc') --> selected<!-- END -->>по убыванию</option>
</select>
<select style="width:100px" name="v_sort_aggr">
<option value="">по значениям поля</option>
<!-- FOR f = aggregates -->
<!-- IF NOT f.cell_only -->
<option value="{s f.id}"<!-- IF eq(v_sort_aggr, f.id) --> selected<!-- END -->>{s f.name} другого поля:</option>
<!-- END -->
<!-- END -->
</select>
<!-- SET fld = 'v_sort' --><!-- INCLUDE admin_olap_field.tpl -->
</td><td style="background-color: #ffe0e0; padding: 8px">
В таблице:<br />
<p style="margin-top: 16px">Показатель:</p>
<p style="margin-left: 20px">
<select style="width:100px" name="cell_aggr">
<!-- FOR f = aggregates -->
<option value="{s f.id}"<!-- IF eq(cell_aggr, f.id) --> selected<!-- END -->>{s f.name}</option>
<option value="{s f.id}"<!-- IF eq(cell.aggr, f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<!-- SET fld = 'cell' --><!-- INCLUDE admin_olap_field.tpl --><br />
Дополнительно разбить по:<br />
<!-- SET fld = 'cell_group' --><!-- INCLUDE admin_olap_field.tpl --><br />
</td></tr>
</table>
Условия выборки данных:
<table>
<select style="width:100px" name="cell_field">
<option value="">---</option>
<!-- FOR f = fielddescs -->
<option value="{s f.id}"<!-- IF eq(cell.field, f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<select style="width:100px" name="cell_func">
<option value="">без преобразования</option>
<!-- FOR f = functions -->
<option value="{s f.id}"<!-- IF eq(cell.func, f.id) --> selected<!-- END -->><!-- IF f.time -->(время) <!-- END -->{s f.name}</option>
<!-- END -->
</select>
</p>
<p style="margin-top: 16px">Группировка: &nbsp; &nbsp; количество полей: <input type="text" name="field_count" value="{field_count}" /></p>
<ol>
<!-- FOR fld = range(0,sub(field_count,1)) -->
<li>
<!-- SET gf = get(group_fields,fld) -->
<p><select name="group-type-{fld}">
<option value="">---</option>
<!-- FOR g = group_types -->
<option value="{g.id}"<!-- IF eq(gf.type,g.id) --> selected <!-- END -->>{g.name/s}</option>
<!-- END -->
</select>
<!-- INCLUDE admin_olap_field.tpl -->
</p>
<p><select style="width:100px" name="group-sort_dir-{fld}">
<option value="asc">По возрастанию</option>
<option value="desc"<!-- IF eq(gf.sort_dir, 'desc') --> selected<!-- END -->>По убыванию</option>
</select>
<select style="width:100px" name="group-sort_aggr-{fld}">
<option value="">по значениям поля</option>
<!-- FOR f = aggregates -->
<!-- IF NOT f.cell_only -->
<option value="{s f.id}"<!-- IF eq(gf.sort_aggr, f.id) --> selected<!-- END -->>{s f.name} другого поля:</option>
<!-- END -->
<!-- END -->
</select>
<!-- SET ft = 'sort_' --><!-- INCLUDE admin_olap_field.tpl --><!-- SET ft = '' -->
</p>
</li>
<!-- END -->
</ol>
<p style="margin-top: 16px">Условия выборки данных:</p>
<table style="margin-left: 20px">
<!-- FOR f = fielddescs -->
<tr><th style="text-align: left; vertical-align: top">{s f.name}</th>
<td style="text-align: left; vertical-align: top">
@ -89,6 +91,7 @@
</td></tr>
<!-- END -->
</table>
<p style="margin-top: 16px"><input type="submit" style="width: 200px" value=" Построить отчёт " /></p>
</form>
<!-- IF build -->
<h1>Отчёт</h1>
@ -102,27 +105,27 @@
<!-- FOR d = table.desc -->
<!-- SET o = or(o, not(table#), ne(get(get(get(get(tables,sub(table#,1)),'desc'),d#),'value'),d.value)) -->
<!-- IF o -->
<p style="margin-left: {mul(d#,20)}px"><!-- IF d# -->, <!-- END -->{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}</p>
<p style="margin-left: {mul(d#,20)}px">{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}</p>
<!-- END -->
<!-- END -->
<div style="margin-left: {mul(count(table.desc),20)}px">
<!-- END -->
<table class="simpletable center">
<!-- FOR row = table.rows -->
<tr>
<!-- FOR c = row -->
<!-- IF is_array(c) -->
<th<!-- IF c.colspan --> colspan="{c.colspan}"<!-- END --><!-- IF c.rowspan --> rowspan="{c.rowspan}"<!-- END --><!-- IF c.class --> class="{c.class}"<!-- END -->>{c.text}</th>
<!-- ELSE -->
<td>{c}</td>
<table class="simpletable center">
<!-- FOR row = table.rows -->
<tr>
<!-- FOR c = row -->
<!-- IF is_array(c) -->
<th<!-- IF c.colspan --> colspan="{c.colspan}"<!-- END --><!-- IF c.rowspan --> rowspan="{c.rowspan}"<!-- END --><!-- IF c.class --> class="{c.class}"<!-- END -->>{c.text}</th>
<!-- ELSE -->
<td>{c}</td>
<!-- END -->
<!-- END -->
</tr>
<!-- END -->
</tr>
</table>
<!-- IF table.desc -->
</div>
<!-- END -->
</table>
<!-- IF table.desc -->
</div>
<!-- END -->
<!-- END -->
<p>Отчёт занял {time_elapsed} сек. Использовано {memory} памяти для работы.</p>
<!-- END -->

View File

@ -1,12 +1,14 @@
<!-- SET hascg = strlen(cell_group_field) -->
<!-- FOR k = hkeys -->;"{fformat(h_field,h_func,'',k)}"<!-- END -->
<!-- FOR v = vkeys -->
"{fformat(v_field,v_func,'',v)}"
<!-- FOR k = hkeys -->;"
<!-- SET cv = get(get(data,v),k) -->
<!-- IF hascg --><!-- FOR ck = keys(cv) -->{fformat(cell_group_field,cell_group_func,'',ck)}: {fformat(cell_field,cell_func,cell_aggr,get(cv,ck))};\n <!-- END -->"
<!-- ELSE -->{fformat(cell_field,cell_func,cell_aggr,cv)}"<!-- END -->
<!-- IF tables.0.desc -->;<!-- END -->
<!-- FOR table = tables -->
<!-- IF table.desc -->"
<!-- FOR d = table.desc -->
<!-- IF d# -->, <!-- END -->{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}
<!-- END -->"
<!-- END -->
<!-- IF vsort -->;"{get(vsort,v)}" <!-- END -->
<!-- FOR row = table.rows -->
<!-- FOR c = row --><!-- IF is_array(c) -->"{c.text}"<!-- ELSE -->"{c}"<!-- END -->;<!-- END -->
<!-- END -->;
;
<!-- END -->
<!-- IF hsort --><!-- FOR k = hkeys -->;"{get(hsort,k)}"<!-- END --><!-- END -->

View File

@ -1,12 +1,12 @@
<select style="width:100px" name="{fld}_field">
<select style="width:100px" name="group-{ft}field-{fld}">
<option value="">---</option>
<!-- FOR f = fielddescs -->
<option value="{s f.id}"<!-- IF eq(get(concat(fld,'_field')), f.id) --> selected<!-- END -->>{s f.name}</option>
<option value="{s f.id}"<!-- IF eq(get(get(group_fields,fld),concat(ft,'field')), f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<select style="width:100px" name="{fld}_func">
<select style="width:100px" name="group-{ft}func-{fld}">
<option value="">без преобразования</option>
<!-- FOR f = functions -->
<option value="{s f.id}"<!-- IF eq(get(concat(fld,'_func')), f.id) --> selected<!-- END -->><!-- IF f.time -->(время) <!-- END -->{s f.name}</option>
<option value="{s f.id}"<!-- IF eq(get(get(group_fields,fld),concat(ft,'func')), f.id) --> selected<!-- END -->><!-- IF f.time -->(время) <!-- END -->{s f.name}</option>
<!-- END -->
</select>