. /** * This is an api endpoint that sends a request to a local container with a flask-based server. * The plugin uses the ASYST grading tool * modified to work as a web endpoint. * * @package local_asystgrade * @copyright 2024 Artem Baranovskyi * @copyright based on work by 2023 Ulrike Pado , * @copyright Yunus Eryilmaz & Larissa Kirschner * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once($_SERVER['DOCUMENT_ROOT'] . '/config.php'); require_once('lib.php'); use local_asystgrade\api\client; use local_asystgrade\api\http_client; use local_asystgrade\utils; try { require_login(); require_capability('mod/assign:grade', context_system::instance()); } catch (coding_exception | moodle_exception $e) { debugging($e->getMessage()); redirect( new moodle_url('/local/asystgrade/error.php'), get_string('loginerror', 'local_asystgrade') ); } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { throw new moodle_exception('invalidmethod', 'local_asystgrade'); } $data = json_decode(file_get_contents('php://input'), true); if ($data) { // Preparing Flask API. try { $apiendpoint = utils::get_api_endpoint(); } catch (dml_exception $e) { debugging('Failed to get API endpoint setting: ' . $e->getMessage()); } $httpclient = new http_client(); $apiclient = client::getInstance($apiendpoint, $httpclient); $response = retry_api_request($apiclient, $data); $grades = json_decode($response, true); // Check JSON validity. if (json_last_error() !== JSON_ERROR_NONE) { debugging('JSON decode error: ' . json_last_error_msg()); throw new moodle_exception('invalidjson', 'local_asystgrade', '', json_last_error_msg()); } else { echo json_encode(['success' => true, 'grades' => $grades]); } } else { echo json_encode(['error' => 'No data received']); } /** * Validates the provided request payload data array. * * @param array $data The data to validate. * @return array The cleaned data. * @throws moodle_exception If the data is invalid. */ function validate_data($data): array { if (!isset($data['referenceAnswer'], $data['studentAnswers']) || !is_array($data['studentAnswers'])) { throw new moodle_exception('invalidrequest', 'local_asystgrade'); } return [ 'referenceAnswer' => clean_param($data['referenceAnswer'], PARAM_TEXT), 'studentAnswers' => array_map(fn($answer) => clean_param($answer, PARAM_TEXT), $data['studentAnswers']), ]; } /** * Retries an API request a specified number of times. * * @param object $apiclient The API client to use for the request. * @param array $payload The data to send in the request. * @param int $maxretries The maximum number of retry attempts. * @return mixed The response from the API client. * @throws moodle_exception If the API request fails after the maximum retries. */ function retry_api_request($apiclient, $payload, $maxretries = 3): mixed { for ($attempts = 0; $attempts < $maxretries; $attempts++) { try { return $apiclient->send_data(validate_data($payload)); } catch (Exception $e) { debugging('API request error: ' . $e->getMessage()); if ($attempts + 1 === $maxretries) { throw new moodle_exception('apifailure', 'local_asystgrade'); } } } }