Небольшое погружение в Moodle
Система управления обучением (LMS) Moodle де-факто стала стандартом платформы электронного обучения в российских вузах. Несмотря на то что в медийном пространстве время от времени появляются сообщения о создании новых образовательных платформ и различных маркетплейсов образовательного контента, Moodle прочно заняла свою нишу в высшем образовании.
Ее можно встретить практически в любом вузе. Это либо официальные системы электронного обучения, либо локальные инициативы отдельных факультетов. Такая тенденция наблюдается не только в России: за счет открытости исходного кода и свободной лицензии сообщество Moodle непрерывно растет.
Когда необходима интеграция
По мере информатизации бизнес-процессов в вузах рано или поздно встает вопрос об интеграции систем электронного обучения на базе Moodle с существующими системами учета студентов и контроля успеваемости.
В Moodle есть ряд встроенных функций по импорту пользователей, среди которых широко используются LDAP-интеграция и авторизация через OAuth2, но с отправкой данных о результатах обучения дело обстоит сложнее.
С одной стороны, завершение курса — это процесс, состоящий из вызова нескольких методов (функций):
- подсчитываются оценки за элементы курса;
- вычисляется итоговый балл согласно выбранной методике;
- проверяются условия завершения курса.
При условии, что курс может быть пройден не с первого раза и в некоторых курсах результат прохождения разрешено улучшать, возникает вопрос, где отслеживать завершение курса, чтобы передать данные во внешнюю информационную систему.
Причем сделать это необходимо так, чтобы работало абсолютно с любыми курсами платформы. С другой стороны, если разработчик внесет правки в базовый код Moodle, система станет необновляемой, потребуется вести реестр измененных файлов и проводить обновления вручную.
Как разработать собственный плагин
К счастью, в Moodle начиная с версии 2.6 появился новый интерфейс событий Events API, который позволяет в дополнение к системным обработчикам внутренних событий создавать собственные, а также создавать свои пользовательские события. Этот функционал также создал условия для эффективного взаимодействия между ядром Moodle и пользовательскими плагинами.
Используя Events API, можно разработать собственный плагин, который, например, будет при завершении студентом электронного курса отправлять результат обучения во внешнюю информационную систему.
Список всех доступных событий, включая события плагинов, можно посмотреть в веб-интерфейсе Moodle: http://ваш_moodle_сайт/report/eventlist/index.php (тут требуются права администратора), — а также в коде.
Определения базовых системных событий находятся в каталоге lib/classes/event, а определения событий модулей (плагинов) — в каталоге /путь_к_плагину/classes/event. Событие отправки попытки теста, к примеру, будет находиться в mod/quiz/classes/event/attempt_submitted.php.
Если заглянуть в определение события, можно увидеть, что они объявляются как классы, наследуемые от класса base.
Как начать использовать Moodle Events API
Чтобы воспользоваться Moodle Events API и подписаться на системное событие, нужно создать «наблюдателя» события — observer, который при заданных ему параметрах приоритета будет включаться и вызывать метод класса — ваш код, где при наступлении события будут выполняться какие-то действия.
Наблюдатели событий объявляются в виде массива $observers в файле db/events.php в каталоге плагина Moodle:
// Список наблюдателей.
$observers = array(
array(
'eventname' => 'coreeventcourse_completed',
'callback' => 'local_resultsubmit_observer::course_completed',
'priority' => 1000,
'internal' => false,
),
);
При объявлении наблюдателя ключевые параметры — это:
- eventname — полное имя класса события, которое вы хотите отслеживать;
- callback — метод вашего класса, где содержится код, выполняющийся при наступлении события;
- priority — приоритет; если наблюдателей несколько, параметр позволяет выстроить очередность их оповещения;
- internal — параметр определяет, в какой момент будет оповещен наблюдатель: до завершения операций с базой данных (БД), или это неважно. Если internal установлен в false, то наблюдатель будет оповещен только после завершения операций с БД. Это важно, когда обработчик события планирует использовать результаты события, сохраненные в БД. После объявления наблюдателя события необходимо написать код обработчика события.
Рассмотрим Moodle Events API в деталях на примере плагина
В этом примере плагин будет отправлять данные о завершении курса и результатах обучения во внешнюю систему управления студентами, используя REST API. Назовем плагин resultsubmit.
В Moodle есть различные предопределенные типы плагинов: модули, фильтры, блоки, типы вопросов и другие. Я использую тип «локальный плагин», который подразумевает сугубо пользовательский функционал, не вписывающийся в другие предопределенные типы.
Для нового плагина создадим в подкаталоге локальных плагинов local корневого каталога Moodle файловую структуру, как изображено на рисунке ниже.
Каталоги и файлы, которые нужно создать, выделены красной рамкой. Это и есть код нашего локального плагина resultsubmit. Файлы version.php и lang/en/local_resultsubmit.php — это дань программному интерфейсу Moodle: в первом указывается версия Moodle, требуемая для запуска плагина, и версия самого плагина, а во втором содержатся языковые строки, чтобы плагин корректно отображался в списке плагинов веб-интерфейса Moodle. Листинги этих файлов приведены в конце статьи.
С точки зрения использования Events API нас интересуют файлы events.php и observer.php. Рассмотрим их листинги.
Листинг файла events.php
<?php
defined('MOODLE_INTERNAL') || die();
/**
* Объявление наблюдателя событий плагина local_resultsubmit.
*
* @package local_resultsubmit
* @category event
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Список наблюдателей.
$observers = array(
array(
'eventname' => 'coreeventcourse_completed',
'callback' => 'local_resultsubmit_observer::course_completed',
'priority' => 1000,
'internal' => false,
),
);
В файле events.php объявляется наблюдатель (observer), что уже ранее рассматривалось. В параметрах обозревателя важно указать событие — завершение курса course_completed. Наш класс и метод, которые будет вызваны для обработки события, — local_resultsubmit_observer::course_completed, а параметр internal как false, потому что далее в обработчике события мы будем запрашивать в БД данные об итоговой оценке. И если указать internal как true или опустить этот параметр, то из БД будет возвращаться некорректный результат, поскольку обработчик будет вызываться до того, как итоговый результат курса будет записан в БД.
Листинг файла observer.php
/**
* Обработчик событий
*
* @package resultsubmit
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Класс обработчика событий плагина local_resultsubmit.
*/
class local_resultsubmit_observer
{
/**
* Исполняется при наступлении события завершения курса.
*
* @param coreeventcourse_completed $event
* @throws dml_exception
*/
public static function course_completed(coreeventcourse_completed $event)
{
global $DB, $CFG;
$eventData = $event->get_data();
$user = $DB->get_record('user', array('id' => $eventData['userid']));
$course = $DB->get_record('course', array('id' => $eventData['courseid']));
$payload = [
'userId' => $user->idnumber,
'result' => 1,
'mark' => self::get_final_grade($course->id, $user->id),
];
// Отправляем данные во внешний API.
self::send_to_external_api($course->idnumber, $payload);
}
/**
* Отправляет данные о завершении курса и оценку во внешний API.
*
* @param string $course_idnumber ID курса, который завершил пользователь.
* @param array $data Данные для отправки во внешний API. Переменная должна содержать:
*
- 'userId': ID пользователя во внешней информационной системе.
*
- 'result': Результат завершения курса (1 — курс завершен, 0 — курс не завершен).
*
- 'mark': Итоговый балл пользователя за курс.
* @return void
*/
private static function send_to_external_api($course_idnumber, $data)
{
// В качестве API внешней системы используем бесплатную тестовую API-среду resultsubmit.requestcatcher.com
$url = "https://resultsubmit.requestcatcher.com/course/{$course_idnumber}/result";
// Для отправки используем модуль php_curl
$headers = [
'Content-Type: application/json',
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
if (curl_errno($ch)) {
error_log('cURL error: ' . curl_error($ch));
} else {
error_log('Response from API: ' . $response);
}
curl_close($ch);
}
/**
* Возвращает итоговый балл пользователя за курс.
*
* @param int $courseid ID курса Moodle.
* @param int $userid ID пользователя Moodle.
* @return int|null Итоговый балл пользователя за курс.
* @throws dml_exception
*/
private static function get_final_grade($courseid, $userid)
{
global $DB;
// Запрос на получение итогового балла по курсу (final grade) для конкретного пользователя.
$sql = "SELECT gg.finalgrade
FROM mdl_grade_items gi
JOIN mdl_grade_grades gg ON gi.id = gg.itemid
WHERE gi.courseid = ? AND gg.userid = ? AND gi.itemtype like ?";
$params = array($courseid, $userid, 'course');
$grade = $DB->get_records_sql($sql, $params);
$key = array_key_first($grade);
$nestedArray = $grade[$key];
return intval(round($nestedArray->finalgrade));
}
}
В файле объявляется класс обработчика события, где главный метод — course_completed — получает от ядра Moodle информацию о событии и вызывает при необходимости другие функции. В частности, вызываются методы класса get_final_grade — получение итоговой оценки за курс из базы данные и send_to_external_api — отправка собранных данных во внешний API с использованием библиотеки php_curl.
В методе send_to_external_api в качестве внешнего API используется бесплатная тестовая API-среда requestcatcher.com, для просмотра результата отправки данных достаточно зайти по адресу http://resultsubmit.requestcatcher.com.
Листинг файла version.php
<?php defined('MOODLE_INTERNAL') || die(); $plugin->version = 2024051710;
// Текущая версия плагина в формате даты YYYYMMDDXX, где XX — порядковый номер версии.
$plugin->requires = 2021051700; // Требуемая для плагина версия Moodle.
$plugin->component = 'local_resultsubmit'; // Полное имя плагина для системных операций.
Файл version.php содержит информацию о версиях плагина и требуемой версии Moodle, а также системное имя плагина local_resultsubmit.
Листинг файла lang/local_resultsubmit.php
<?php
$string['pluginname'] = 'Result submit local plugin';
Файл lang/local_resultsubmit.php содержит минимальную языковую информацию для отображения имени плагина в системе Moodle, в списке плагинов.
Для тестирования плагина, необходимо:
1. Зайти в веб-интерфейс Moodle по адресу: http://ваш_moodle_сайт/admin/index.php. Moodle обнаружит новый плагин и предложит установить его, нажав кнопку «Обновить Moodle».
2. После установки можно создать тестовый курс, состоящий из одного или (для полноты эксперимента) нескольких заданий, задать правила завершения этого курса так, чтобы при прохождении задания курс был завершен и была проставлена оценка.
3. После завершения данного курса от имени пользователя с ролью «студент» (администратор не может проходить курсы как студент) в API https://resultsubmit.requestcatcher.com будет отправлен POST-запрос, содержащий данные о прохождении. Запрос сразу же отобразится на странице https://resultsubmit.requestcatcher.com.
На этом всё. Вот так происходит интеграция с внешними информационными системами, используя интерфейс событий Moodle Events API. Если возникнут вопросы — задавайте их в комментариях, обязательно отвечу.