Рисуем графики: відмінності між версіями
Sv (обговорення | внесок) Немає опису редагування |
Sv (обговорення | внесок) мНемає опису редагування |
||
(Не показано 8 проміжних версій цього користувача) | |||
Рядок 1: | Рядок 1: | ||
[[Файл:Programming graphs.png|мини]] | [[Файл:Programming graphs.png|мини]] | ||
Нарисовать график несложно. Это делается в 2 шага: | Нарисовать график несложно. Это делается в 2 шага: | ||
# Подготавливаем данные и сохраняем в базе данных | |||
# Выводим график :) | |||
Поставим задачу: вывести график количества платежей на каждый день. | Поставим задачу: вывести график количества платежей на каждый день. | ||
Количество платежей: | Количество платежей по дням: | ||
<pre> | <pre> | ||
SELECT UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm, COUNT(*) n | SELECT UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm, COUNT(*) n | ||
Рядок 13: | Рядок 13: | ||
Пояснение: | Пояснение: | ||
# Платежи хранятся в таблице pays | |||
# COUNT(*) n - считает количество записей и записывает в "переменную" n (запись в n исключительно для удобства) | |||
# UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm - довольно сложная конструкция. Проанализируем по частям: | |||
#* Поле time хранит и дату и время платежа. А нам нужна только дата. Т.е. нужно отбросить время | |||
#* Время можно отбросить командой DATE() | |||
#* Проблема в том, что NoDeny для уменьшения размера данных везде хранит время в виде называемом unixtimestamp. По сути это число (которое указывает на количество секунд прошедших с 01.01.1970). Команда DATE() принимает не unixtimestamp, а дату | |||
#* FROM_UNIXTIME(time) преобразует unixtimestamp в дату | |||
#* UNIX_TIMESTAMP() делает обратное преобразование даты в unixtimestamp | |||
#* Таким образом, SELECT UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) - преобразование в дату, отбрасывание времени, преобразование в unixtimestamp | |||
# GROUP BY tm - группирует результат по дням (дни мы записали в "переменную" tm) | |||
Кроме sql нам нужно дать имя графику. Это просто уникальная последовательность символов. Назовем график pays_stat. | |||
Теперь напишем код. Создадим файл /usr/local/nodeny/web/demo.pl и дадим ему права на чтение вебсервером. Содержимое файла: | |||
<pre> | |||
use strict; | |||
use JSON; | |||
sub go | |||
{ | |||
my($url) = @_; | |||
my $graph_name = 'pays_stat'; | |||
if( !ses::input('make') ) | |||
{ | |||
my $param = to_json({ | |||
a => 'ajGraph', | |||
domid => 'main_block', | |||
group => $graph_name, | |||
y_title => 'кол-во', | |||
type => 1 | |||
}); | |||
Doc->template('base')->{document_ready} .= "nody.ajax($param);"; | |||
return; | |||
} | |||
my $points = []; | |||
my $db = Db->sql( | |||
"SELECT UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm, COUNT(*) n ". | |||
"FROM pays GROUP BY tm ORDER BY tm" | |||
); | |||
while( my %p = $db->line ) { push @$points, [ 1000*$p{tm}, $p{n} ]; } | |||
Save_webses_data( | |||
module=>'ajGraph', data=>{ points=>$points, group=>$graph_name, descr=>'Количество платежей' } | |||
); | |||
$url->redirect(); | |||
} | |||
1; | |||
</pre> | |||
Давайте проверим работу, а потом поясним как оно работает. В браузере открываем: | |||
<pre> | |||
https://xxx.xx/cgi-bin/stat.pl?a=demo&make=1 | |||
</pre> | |||
make=1 заставит сформировать график и записать его в базу. Если дальше просто открывать | |||
<pre> | |||
https://xxx.xx/cgi-bin/stat.pl?a=demo | |||
</pre> | |||
- будет показываться уже сформированный график. | |||
Рассмотрим код. Это, как вы поняли, имя графика мы записали в переменную graph_name: | |||
<pre> | |||
my $graph_name = 'pays_stat'; | |||
</pre> | |||
<pre> | |||
if( !ses::input('make') ) | |||
{ | |||
XXX | |||
} | |||
</pre> | |||
ses::input() - это функция получения GET и POST параметров. В данном случае из url-а получаем параметр make и если он не установлен (восклицательный знак - это проверка "не установлен"), то выполняется код XXX, а именно: | |||
<pre> | |||
my $param = to_json({ | |||
a => 'ajGraph', | |||
domid => 'main_block', | |||
group => $graph_name, | |||
y_title => 'кол-во', | |||
type => 1 | |||
}); | |||
Doc->template('base')->{document_ready} .= "nody.ajax($param);"; | |||
return; | |||
</pre> | |||
Здесь все, что вы можете поменять: | |||
* group - имя графика | |||
* y_title - подпись к оси Y на графике | |||
Этот код выведет на экран все графики (их может быть несколько) с именем $graph_name. | |||
Переходим к формированию графика. Вот такая конструкция выполняет sql: | |||
<pre> | |||
my $db = Db->sql(...); | |||
</pre> | |||
Так мы проходимся по всем строкам результата и каждую строку записываем в переменную %p: | |||
<pre> | |||
while( my %p = $db->line ) { ... } | |||
</pre> | |||
Переменная $points - указатель на массив. Чтобы использовать ее как массив ставим спереди символ "@": | |||
<pre> | |||
push @$points, [ 1000*$p{tm}, $p{n} ]; | |||
</pre> | |||
Структура данных для графика: [время в миллисекундах, значение по оси Y]. Умножаем на 1000 чтобы перевести секунды в миллисекунды | |||
Запись графика в базу данных: | |||
<pre> | |||
Save_webses_data( | |||
module=>'ajGraph', data=>{ points=>$points, group=>$graph_name, descr=>'Количество платежей' } | |||
); | |||
</pre> | |||
* module=>'ajGraph' - модуль, который будет рисовать график | |||
* descr=>'Количество платежей' - заголовок графика | |||
Переход редиректом на текущую страницу, но без параметра make=1 | |||
<pre> | |||
$url->redirect(); | |||
</pre> |
Поточна версія на 18:28, 23 квітня 2021
Нарисовать график несложно. Это делается в 2 шага:
- Подготавливаем данные и сохраняем в базе данных
- Выводим график :)
Поставим задачу: вывести график количества платежей на каждый день.
Количество платежей по дням:
SELECT UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm, COUNT(*) n FROM pays GROUP BY tm ORDER BY tm
Пояснение:
- Платежи хранятся в таблице pays
- COUNT(*) n - считает количество записей и записывает в "переменную" n (запись в n исключительно для удобства)
- UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm - довольно сложная конструкция. Проанализируем по частям:
- Поле time хранит и дату и время платежа. А нам нужна только дата. Т.е. нужно отбросить время
- Время можно отбросить командой DATE()
- Проблема в том, что NoDeny для уменьшения размера данных везде хранит время в виде называемом unixtimestamp. По сути это число (которое указывает на количество секунд прошедших с 01.01.1970). Команда DATE() принимает не unixtimestamp, а дату
- FROM_UNIXTIME(time) преобразует unixtimestamp в дату
- UNIX_TIMESTAMP() делает обратное преобразование даты в unixtimestamp
- Таким образом, SELECT UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) - преобразование в дату, отбрасывание времени, преобразование в unixtimestamp
- GROUP BY tm - группирует результат по дням (дни мы записали в "переменную" tm)
Кроме sql нам нужно дать имя графику. Это просто уникальная последовательность символов. Назовем график pays_stat.
Теперь напишем код. Создадим файл /usr/local/nodeny/web/demo.pl и дадим ему права на чтение вебсервером. Содержимое файла:
use strict; use JSON; sub go { my($url) = @_; my $graph_name = 'pays_stat'; if( !ses::input('make') ) { my $param = to_json({ a => 'ajGraph', domid => 'main_block', group => $graph_name, y_title => 'кол-во', type => 1 }); Doc->template('base')->{document_ready} .= "nody.ajax($param);"; return; } my $points = []; my $db = Db->sql( "SELECT UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm, COUNT(*) n ". "FROM pays GROUP BY tm ORDER BY tm" ); while( my %p = $db->line ) { push @$points, [ 1000*$p{tm}, $p{n} ]; } Save_webses_data( module=>'ajGraph', data=>{ points=>$points, group=>$graph_name, descr=>'Количество платежей' } ); $url->redirect(); } 1;
Давайте проверим работу, а потом поясним как оно работает. В браузере открываем:
https://xxx.xx/cgi-bin/stat.pl?a=demo&make=1
make=1 заставит сформировать график и записать его в базу. Если дальше просто открывать
https://xxx.xx/cgi-bin/stat.pl?a=demo
- будет показываться уже сформированный график.
Рассмотрим код. Это, как вы поняли, имя графика мы записали в переменную graph_name:
my $graph_name = 'pays_stat';
if( !ses::input('make') ) { XXX }
ses::input() - это функция получения GET и POST параметров. В данном случае из url-а получаем параметр make и если он не установлен (восклицательный знак - это проверка "не установлен"), то выполняется код XXX, а именно:
my $param = to_json({ a => 'ajGraph', domid => 'main_block', group => $graph_name, y_title => 'кол-во', type => 1 }); Doc->template('base')->{document_ready} .= "nody.ajax($param);"; return;
Здесь все, что вы можете поменять:
- group - имя графика
- y_title - подпись к оси Y на графике
Этот код выведет на экран все графики (их может быть несколько) с именем $graph_name.
Переходим к формированию графика. Вот такая конструкция выполняет sql:
my $db = Db->sql(...);
Так мы проходимся по всем строкам результата и каждую строку записываем в переменную %p:
while( my %p = $db->line ) { ... }
Переменная $points - указатель на массив. Чтобы использовать ее как массив ставим спереди символ "@":
push @$points, [ 1000*$p{tm}, $p{n} ];
Структура данных для графика: [время в миллисекундах, значение по оси Y]. Умножаем на 1000 чтобы перевести секунды в миллисекунды
Запись графика в базу данных:
Save_webses_data( module=>'ajGraph', data=>{ points=>$points, group=>$graph_name, descr=>'Количество платежей' } );
- module=>'ajGraph' - модуль, который будет рисовать график
- descr=>'Количество платежей' - заголовок графика
Переход редиректом на текущую страницу, но без параметра make=1
$url->redirect();