Рисуем графики: відмінності між версіями

Матеріал з NoDeny
Перейти до навігації Перейти до пошуку
Немає опису редагування
мНемає опису редагування
 
(Не показано 8 проміжних версій цього користувача)
Рядок 1: Рядок 1:
[[Файл:Programming graphs.png|мини]]
[[Файл:Programming graphs.png|мини]]
Нарисовать график несложно. Это делается в 2 шага:
Нарисовать график несложно. Это делается в 2 шага:
1. Подготавливаем данные и сохраняем в базе данных
# Подготавливаем данные и сохраняем в базе данных
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:


Пояснение:
Пояснение:
1. Платежи хранятся в таблице pays
# Платежи хранятся в таблице pays
2. COUNT(*) n - считает количество записей и записывает в "переменную" n (запись в n исключительно для удобства)
# COUNT(*) n - считает количество записей и записывает в "переменную" n (запись в n исключительно для удобства)
3. UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm - довольно ссложная конструкция. Проанализируем по частям:
# UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm - довольно сложная конструкция. Проанализируем по частям:
3.1. Поле time хранит и дату и время платежа. А нам нужна только дата. Т.е. нужно отброссить время
#* Поле time хранит и дату и время платежа. А нам нужна только дата. Т.е. нужно отбросить время
3.2. Время можно отбросить командой DATE()
#* Время можно отбросить командой 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 шага:

  1. Подготавливаем данные и сохраняем в базе данных
  2. Выводим график :)

Поставим задачу: вывести график количества платежей на каждый день.

Количество платежей по дням:

SELECT UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(time))) tm, COUNT(*) n
FROM pays GROUP BY tm ORDER BY tm

Пояснение:

  1. Платежи хранятся в таблице pays
  2. COUNT(*) n - считает количество записей и записывает в "переменную" n (запись в n исключительно для удобства)
  3. 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
  4. 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();