ПОЛЬЗОВАТЕЛЬСКИЕ ОТЧЕТЫ
Модуль пользовательских отчетов предназначен для получения пользователями информации из OPAC-Global в форме списков, таблиц или иных выходных форм. Модуль представляет собой отдельное приложение, автоматически запускающееся вместе с сервером OPAC-Global, и взаимодействующее с пользователями через веб-интерфейс. Все пользовательские отчеты являются программами, написанными на языке JavaScript, и использующими для работы сервер приложений Node.js. Отчеты могут выполняться как через веб-интерфейс, так и автономно из командной строки на сервере.
Модуль пользовательских отчетов был создан в качестве замены ранее использовавшегося модуля пользовательских запросов в связи с необходимостью расширения функциональных возможностей и повышения производительности.
Данный подпункт меню предназначен для формирования пользовательских отчетов в системе OPAC-Global . При выборе подпункта Пользовательские отчеты появится окно вида (см. рисунок 1):

Рисунок 1
В рабочей области окна представлен перечень коллекций и содержащихся в них пользовательских отчетов. Каждый отчет может быть выбран для последующего выполнения.
Внимание! Отчеты, содержащиеся в коллекции Стандартные отчеты, могут быть обновлены с обновлением системы OPAC-Global. В связи с этим категорически не рекомендуется вносить изменения в эти отчеты.
При выборе одного из отчетов на экран будет выведен список аргументов: База данных, Начальная дата, Конечная дата, Максимальное число строк. Следует указать необходимые данные и нажать на кнопку Запустить.

Рисунок 2
В ходе выполнения отчета на экран будет выводиться его состояние с обновлением каждые пять секунд (или при нажатии на кнопку Обновить). После завершения выполнения окно примет следующий вид (см. рисунок 3). После успешного завершения задания можно выбрать один из вариантов представления результатов работы: HTML, Excel или Word для вывода результатов, соответственно, в браузере или в одном из редакторов в виде таблицы или документа. Результаты выполнения задания также могут быть представлены в других форматах.

Рисунок 3
В каталоге, указанном в параметре dir.data файла конфигурации (как правило, в каталоге "integration/nodejs/reports/data/tasks"), будет создан подкаталог задания с именем, указанным в поле Идентификатор задания. В этом каталоге будут созданы следующие файлы:
Примечание:
Задание выполняется в фоновом режиме. Выполнение сложных отчетов может занять длительное время. В этом случае можно не дожидаться их завершения, а просто закрыть браузер и впоследствии вернуться к результатам выполнения задания. Можно запускать несколько заданий одновременно.
Список выполняемых и завершенных заданий можно просмотреть, выбрав меню Задания.

Рисунок 4
Для создания собственного отчета нужно выполнить приведенные ниже действия.
1. В каталоге "integration/nodejs/reports/library" выбрать один из существующих каталогов коллекций отчетов или создать новый каталог.
2. В выбранном каталоге коллекции создать подкаталог с именем нового отчета. Лучше всего скопировать целиком один из каталогов с наиболее близким существующим отчетом – он будет служить в качестве образца.
3. В каталоге отчета создать или отредактировать файл index.conf следующего вида.
{ "title": "Название отчета", "index": "Имя файла результата", "args": [ { "name": "аргумент_1", "title": "Название аргумента_1", "type": "date", "attributes": {перечень атрибутов элемента HTML "input"}, "value": "значение по умолчанию" }, { "name": "аргумент_2", "title": "Название аргумента_2", "type": "integer", "attributes": {перечень атрибутов элемента HTML "input"}, "value": "значение по умолчанию" } ] } |
в данном файле
title |
название отчета, отображаемое в списке |
index |
имя файла результата, может быть строкой или массивом (при наличии нескольких файлов) |
args |
перечень аргументов, выводимый при запуске отчета |
args[i].name |
имя аргумента для использования в элементе HTML "input" |
args[i].title |
название аргумента, выводится в перечне аргументов запуска отчета |
args[i].type |
тип аргумента (integer, date, string, select) |
args[i].attributes |
перечень атрибутов элемента HTML "input" |
args[i].value |
значение аргумента по умолчанию |
Ниже приведен реальный пример файла конфигурации отчета.
{ "title": "Рейтинг часто выдаваемых книг", "index": "index.html", "args": [ { "name": "dateBegin", "title": "Начальная дата", "type": "date", "attributes": {"size": "10", "maxlength": "10"}, "value": "01.01." + (new Date ()).getFullYear() }, { "name": "dateEnd", "title": "Конечная дата", "type": "date", "attributes": {"size": "10", "maxlength": "10"}, "value": global.tools.dateToString (new Date ()) }, { "name": "tableSize", "title": "Максимальное число строк", "type": "integer", "attributes": {"size": "4", "maxlength": "4"}, "value": "50" } ] } |
4. Создать файл index.js с программой на JavaScript. Программе передаются в командной строке аргументы, указанные в файле index.conf.
5. Добавить созданный отчет в файл конфигурации отчетов, после чего выполнить в OPAC-Global пункт меню Обновить настройки.
6. Файл конфигурации отчетов "integration/nodejs/reports/library/index.conf" представлен в формате JSON и выглядит следующим образом.
{ "код_коллекции_1": { "title": "Название коллекции 1", "reports": { "код_отчета_1": { "title": "Название отчета 1", "access": { "allow": ["код_группы_1", "код_группы_2", "код_группы_3"] } }, "код_отчета_2": { "title": "Название отчета 2", } } }, "код_коллекции_2": { "title": "Название коллекции 2", "reports": { "код_отчета_3": { "title": "Название отчета 3", } } } } |
Ниже приведен реальный пример файла конфигурации.
{ "standard": { "title": "Стандартные отчеты", "reports": { "book_history": { "title": "История книги", "access": { "allow": ["001"] } }, "book_rating": { "title": "Рейтинг часто выдаваемых книг", } } }, "customize": { "title": "Пользовательские отчеты", "reports": { "test1": { "title": "Тестовый отчет", } } } } |
В приведенном примере файла конфигурации определены две коллекции – "standard" и "customize". В коллекции "standard" в свою очередь определены два отчета – "book_history" и "book_rating", а в коллекции "customize" – один отчет "test1". Для отчета "book_history" разрешение на выполнение дано только группе с кодом "001" (обычно это группа "АДМИНИСТРАТОР").
Рассмотрим в качестве примера пользовательского отчета задачу по формированию списка логинов пользователей, создавших новые библиографические записи в заданный промежуток времени.
На первом этапе необходимо создать структуру каталогов и файлов отчета. Предположим, что на данный момент в модуле пользовательских отчетов определена только одна коллекция "Стандартные отчеты". В этом случае необходимо сначала создать новую коллекцию для хранения пользовательских отчетов (в коллекции "Стандартные отчеты" не рекомендуется добавлять свои собственные отчеты или же менять существующие отчеты).
Создадим новый подкаталог с именем "customize" для новой коллекции в каталоге библиотеки отчетов "integration/nodejs/reports/library". После этого добавим в файл конфигурации отчетов "integration/nodejs/reports/library/index.conf" описание новой коллекции:
"customize": { "title": "Пользовательские отчеты", "reports": { } } |
Если коллекций несколько, то их описания нужно разделять запятыми (см. полный пример файла конфигурации отчетов выше).
Создадим каталог для нового отчета "integration/nodejs/reports/library/customize/test1" и в файле конфигурации добавим в коллекцию описание отчета (описания нескольких отчетов также разделяются запятыми):
"customize": { "title": "Пользовательские отчеты", "reports": { "test1": { "title": "Тестовый отчет" } } } |
На следующем этапе нужно создать файл конфигурации самого отчета. Создадим в текстовом редакторе файл "integration/nodejs/reports/library/customize/test1/index.conf" со следующим содержимым:
{ "title": "Тестовый отчет 1", "index": "index.html", "args": [ { "name": "dateBegin", "title": "Начальная дата", "type": "date", "attributes": {"size": "10", "maxlength": "10"}, "value": "" }, { "name": "dateEnd", "title": "Конечная дата", "type": "date", "attributes": {"size": "10", "maxlength": "10"}, "value": "" } ] } |
В этом файле было задано имя отчета "Тестовый отчет 1", указано имя файла с результатом работы "index.html" и определены два аргумента: "dateBegin" и "dateEnd".
Для каждого аргумента указаны следующие параметры:
Теперь необходимо создать файл "integration/nodejs/reports/library/customize/test1/index.js" – основной модуль отчета. Этот модуль представляет собой обычную программу на языке JavaScript для Node.js.
Единственная особенность модуля заключается в способе получения аргументов от web-интерфейса пользовательских отчетов – аргументы ему передаются в виде строки JSON. В остальном это обычная программа для Node.js.
Итак, текст отчета сначала будет следующим:
/* * Главная функция приложения. */ function main() { }
/* * Тело приложения. */
// Вызываем главную функцию приложения. main(); |
На данном этапе программа ничего не выполняет. В ней определяется пустая функция main(), которая затем вызывается из тела приложения.
Добавим в программу обработку аргументов, получаемых при запуске из командной строки или же из web-интерфейса.
var opts = {};
/* * Разбор аргументов командной строки. */ function parseArgs() { var argv = process.argv.slice(2); if (argv.length === 1 && argv[0].indexOf('JSON:') === 0) { argv = JSON.parse(argv[0].slice(5)); }
if (argv.length < 2) { console.error('Указано неверное число аргументов.'); process.exit(1); }
opts.dateBegin = argv[0]; opts.dateEnd = argv[1]; }
/* * Главная функция приложения. */ function main() { // Разбираем аргументы командной строки. parseArgs(); }
/* * Тело приложения. */
// Вызываем главную функцию приложения. main(); |
Как видно, в программе была добавлена еще одна функция – parseArgs(). Эта функция получает аргументы командной строки и проверяет первые 5 символов первого аргумента. Если в начале первого аргумента стоят символы "JSON:", то программа считает, что ей передан один аргумент в виде строки JSON. В этом случае используется функция JSON.parse() для получения массива аргументов из JSON-строки. В противном случае программа использует стандартный массив аргументов командной строки. Далее в функции parseArgs() идет проверка количества переданных аргументов и получение начальной и конечной даты. Полученные аргументы сохраняются в глобальной переменной opts (глобальная переменная использована для упрощения передачи аргументов между функциями).
Теперь добавим в программу вызов функции создания отчета.
var opacglobal = require('opacglobal')
var opts = {}; var opac = null;
/* * Создание отчета. */ function createReport() { }
/* * Разбор аргументов командной строки. */ function parseArgs() { var argv = process.argv.slice(2); if (argv.length === 1 && argv[0].indexOf('JSON:') === 0) { argv = JSON.parse(argv[0].slice(5)); }
if (argv.length < 2) { console.error('Указано неверное число аргументов.'); process.exit(1); }
opts.dateBegin = argv[0]; opts.dateEnd = argv[1]; }
/* * Главная функция приложения. */ function main() { // Разбираем аргументы командной строки. parseArgs();
// Инициализируем объект OPAC-Global. opac = new opacglobal.OpacGlobal();
// Создаем отчет. try { createReport(); console.error('Создание отчета завершено.'); } catch (err) { if (err) { console.error(err.message); } }
// Завершаем работу с объектом OPAC-Global. opac.close(); }
/* * Тело приложения. */
// Вызываем главную функцию приложения. main(); |
В функции main() добавлись инициализация и завершение работы модуля "opacglobal". Описание этого модуля приведено в разделе 3.9.1.1. "Модуль Node.js "opacglobal" в документе "Руководство по работе с системой OPAC-Global". Также добавились глобальные переменные "opacglobal" (подключаемый модуль "opacglobal") и "opac" – экземпляр класса OpacGlobal(). Также в main() добавился вызов функции createReport() с обработкой возможных исключений.
Теперь в функции createReport() реализуем получение нужной нам статистики.
function createReport() { // Формируем поисковый запрос по файлу статистики. var query = new opacglobal.AdabasXFindBuffer('CO', '0001') .and('DA', opts.dateBegin).to('DA', opts.dateEnd);
// Выполняем поиск в файле статистики. var cursor = new opacglobal.StatCursor(opac); var numRecordsFound = cursor.search(query);
// Выводим количество найденных записей статистики. console.error('Найдено записей: %d', numRecordsFound);
// Инициализируем объект для сбора статистики по логинам. var logins = {};
// Читаем из курсора все найденные записи статистики. while (record = cursor.read()) { // Проверяем, встречался ли уже логин. if (logins.hasOwnProperty(record.userId)) { // Если логин встречался, то увеличиваем счечик записей. logins[record.userId]++; } else { // Если логин еще не встречался, то инициализируем счетчик. logins[record.userId] = 1; } }
// Закрываем курсор записей статистики. cursor.close();
// Выводим на экран найденные логины с количеством созданных записей. console.error(JSON.stringify(logins, null, 2)); } |
Сначала мы ищем в базе данных статистики все записи, в которых код операции равен '0001' (создание БЗ), а дата создания записи статистики находится в указанном при запуске отчета диапазоне. Затем мы перебираем все найденные записи статистики и подсчитываем количество каждого встретившегося в них логина пользователя.
В конце мы просто выводим на экран полученную статистику в текстовом виде.
В принципе, мы уже получили отчет, который выводит нужную нам информацию. Однако, формат вывода неудобен для конечного пользователя. Оформим результат в виде HTML-файла. Для этого нужно сначала создать шаблон на языке "Jade" (см. http://jade-lang.com/).
Итак, создаем новый файл "integration/nodejs/reports/library/customize/test1/document.jade" в каталоге отчета.
h3 Статистика создания записей
li Начальная дата: #{opts.dateBegin} li Конечная дата: #{opts.dateEnd}
table(style='table-layout:fixed;') col(style='width:30mm;') col(style='width:30mm;') thead tr th Логин th Кол-во записей tbody each numRecords, login in logins tr td= login td= numRecords |
По существу шаблон представляет собой разметку HTML выполненную в более компактном виде, с добавлением конструкций JavaScript для вывода данных. Заменяем в программе вывод результатов в текстовом виде на генерацию HTML-файла.
В окончательном виде наша программа будет выглядеть так:
var fs = require('fs'); var opacglobal = require('opacglobal'); var path = require('path');
var opts = {}; var opac = null;
/* * Создание отчета. */ function createReport() { // Формируем поисковый запрос по файлу статистики. var query = new opacglobal.AdabasXFindBuffer('CO', '0001') .and('DA', opts.dateBegin).to('DA', opts.dateEnd);
// Выполняем поиск в файле статистики. var cursor = new opacglobal.StatCursor(opac); var numRecordsFound = cursor.search(query);
// Выводим количество найденных записей статистики. console.error('Найдено записей: %d', numRecordsFound);
// Инициализируем объект для сбора статистики по логинам. var logins = {};
// Читаем из курсора все найденные записи статистики. while (record = cursor.read()) { // Проверяем, встречался ли уже логин. if (logins.hasOwnProperty(record.userId)) { // Если логин встречался, то увеличиваем счетчик записей. logins[record.userId]++; } else { // Если логин еще не встречался, то инициализируем счетчик. logins[record.userId] = 1; } }
// Закрываем курсор записей статистики. cursor.close();
// Генерируем файл отчета в формате HTML. var templateArgs = {opts: opts, logins: logins}; var templateData = fs.readFileSync( path.resolve(__dirname, 'document.jade'), 'utf-8'); var template = require('jade').compile(templateData.toString()); var report = template(templateArgs); fs.writeFileSync('index.html', report); )
/* * Разбор аргументов командной строки */ function parseArgs() { var argv = process.argv.slice(2); if (argv.length === 1 && argv[0].indexOf('JSON:') === 0) { argv = JSON.parse(argv[0].slice(5)); }
if (argv.length < 2) { console.error('Указано неверное число аргументов.'); process.exit(1); }
opts.dateBegin = argv[0]; opts.dateEnd = argv[1]; }
/* * Главная функция приложения. */ function main() { // Разбираем аргументы командной строки. parseArgs();
// Инициализируем объект OPAC-Global. opac = new opacglobal.OpacGlobal();
// Создаем отчет. try { createReport(); console.error('Создание отчета завершено.'); } catch (err) { if (err) { console.error(err.message); } }
// Завершаем работу с объектом OPAC-Global. opac.close(); }
/* * Тело приложения. */
// Вызываем главную функцию приложения. main(); |
Теперь обновим конфигурацию OPAC-Global в модуле Администрирование и настройка, меню Обновить настройки и выполним наш отчет:

Рисунок 5