OLAP interface + CSV
parent
041ec8042c
commit
71e241150e
70
olap.php
70
olap.php
|
@ -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;
|
||||
|
|
|
@ -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">Группировка: количество полей: <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 -->
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue