Skip to content

Commit

Permalink
CTP-4066 SORA extenson for new student enrolment in course
Browse files Browse the repository at this point in the history
  • Loading branch information
aydevworks committed Nov 21, 2024
1 parent 1b2dd7f commit b3a2dea
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 30 deletions.
13 changes: 10 additions & 3 deletions classes/cachemanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class cachemanager {
/** @var string Cache area for storing marking schemes.*/
const CACHE_AREA_MARKINGSCHEMES = 'markingschemes';

/** @var string Cache area for storing mapping and mab information.*/
const CACHE_AREA_MAPPING_MAB_INFO = 'mappingmabinfo';

/**
* Get cache.
*
Expand All @@ -50,9 +53,13 @@ class cachemanager {
public static function get_cache(string $area, string $key) {
// Check if cache exists or expired.
$cache = cache::make('local_sitsgradepush', $area)->get($key);
// Expire key.
$expires = 'expires_' . $key;
$expires = cache::make('local_sitsgradepush', $area)->get('expires_' . $key);

if (empty($cache) || empty($expires) || time() >= $expires) {
if (time() >= $expires) {
// Cache expired, delete it.
self::purge_cache($area, $key);
}
return null;
} else {
return $cache;
Expand All @@ -71,7 +78,7 @@ public static function get_cache(string $area, string $key) {
public static function set_cache(string $area, string $key, mixed $value, int $expiresafter): void {
$cache = cache::make('local_sitsgradepush', $area);
$cache->set($key, $value);
$cache->set('expires_' . $key, $expiresafter);
$cache->set('expires_' . $key, time() + $expiresafter);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion classes/extension/extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ protected function get_mappings_by_mab(string $mabidentifier): array {
* @return array
* @throws \dml_exception|\coding_exception
*/
protected function get_mappings_by_userid(int $userid): array {
public function get_mappings_by_userid(int $userid): array {
global $DB;

// Find all enrolled courses for the student.
Expand Down
2 changes: 1 addition & 1 deletion classes/extension/iextension.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ interface iextension {
/**
* Process the extension.
*/
public function process_extension(): void;
public function process_extension(array $mappings): void;
}
14 changes: 9 additions & 5 deletions classes/extension/sora.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,21 +182,25 @@ public function set_properties_from_get_students_api(array $student): void {
/**
* Process the extension.
*
* @param array $mappings
*
* @return void
* @throws \coding_exception
* @throws \dml_exception
* @throws \dml_exception|\moodle_exception
*/
public function process_extension(): void {
public function process_extension(array $mappings): void {
if (!$this->dataisset) {
throw new \coding_exception('error:extensiondataisnotset', 'local_sitsgradepush');
}

// Get all mappings for the student.
$mappings = $this->get_mappings_by_userid($this->get_userid());
// Exit if SORA extra assessment duration and rest duration are both 0.
if ($this->extraduration == 0 && $this->restduration == 0) {
return;
}

// No mappings found.
if (empty($mappings)) {
return;
throw new \moodle_exception('error:mappings_is_empty', 'local_sitsgradepush');
}

// Apply the extension to the assessments.
Expand Down
6 changes: 5 additions & 1 deletion classes/extension/sora_queue_processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

namespace local_sitsgradepush\extension;

use local_sitsgradepush\manager;

/**
* SORA queue processor.
*
Expand Down Expand Up @@ -47,6 +49,8 @@ protected function get_queue_url(): string {
protected function process_message(array $messagebody): void {
$sora = new sora();
$sora->set_properties_from_aws_message($messagebody['Message']);
$sora->process_extension();
// Get all mappings for the student.
$mappings = $sora->get_mappings_by_userid($sora->get_userid());
$sora->process_extension($mappings);
}
}
59 changes: 50 additions & 9 deletions classes/extensionmanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,25 @@ class extensionmanager {
/**
* Update SORA extension for students in a mapping.
*
* @param int $mapid
* @param int $mapid Assessment component mapping ID.
* @param int|null $userid Null to process all students, or user ID to process only that student.
* @return void
* @throws \dml_exception
*/
public static function update_sora_for_mapping(int $mapid): void {
public static function update_sora_for_mapping(int $mapid, ?int $userid = null): void {
try {
// Find the SITS assessment component.
$manager = manager::get_manager();
$mab = $manager->get_mab_by_mapping_id($mapid);

// Throw exception if the SITS assessment component is not found.
if (!$mab) {
throw new \moodle_exception('error:mab_not_found', 'local_sitsgradepush', '', $mapid);
// Find the SITS assessment component.
$mapinfo = $manager->get_mab_and_map_info_by_mapping_id($mapid);

// Throw exception if the SITS assessment component or mapping is not found.
if (!$mapinfo) {
throw new \moodle_exception('error:mab_or_mapping_not_found', 'local_sitsgradepush', '', $mapid);
}

// Get students information for that assessment component.
$students = $manager->get_students_from_sits($mab);
$students = $manager->get_students_from_sits($mapinfo);

// If no students found, nothing to do.
if (empty($students)) {
Expand All @@ -58,10 +60,49 @@ public static function update_sora_for_mapping(int $mapid): void {
foreach ($students as $student) {
$sora = new sora();
$sora->set_properties_from_get_students_api($student);
$sora->process_extension();
// If user ID is set, only process the extension for that user.
if ($userid && $userid !== $sora->get_userid()) {
continue;
}
// Process the extension for all students.
$sora->process_extension([$mapinfo]);
}
} catch (\Exception $e) {
logger::log($e->getMessage(), null, "Mapping ID: $mapid");
}
}

/**
* Check if the user has a gradable role in the course.
*
* @param int $userid User ID.
* @param int $courseid Course ID.
* @return bool
* @throws \coding_exception
* @throws \dml_exception
*/
public static function user_has_gradable_role(int $userid, int $courseid): bool {
global $CFG, $DB;

$gradebookroles = explode(',', $CFG->gradebookroles);
if (empty($gradebookroles)) {
return false;
}

[$insql, $inparam] = $DB->get_in_or_equal($gradebookroles, SQL_PARAMS_NAMED, 'roleid');

$sql = "SELECT COUNT(*) FROM {role_assignments} ra
JOIN {context} c ON ra.contextid = c.id
WHERE ra.userid = :userid
AND c.instanceid = :courseid
AND c.contextlevel = :contextlevel
AND ra.roleid $insql";
$params = [
'userid' => $userid,
'courseid' => $courseid,
'contextlevel' => CONTEXT_COURSE,
];

return $DB->count_records_sql($sql, array_merge($params, $inparam)) > 0;
}
}
40 changes: 35 additions & 5 deletions classes/manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

namespace local_sitsgradepush;

use cache;
use cache_store;
use core_component;
use core_course\customfield\course_handler;
use DirectoryIterator;
Expand Down Expand Up @@ -884,16 +886,44 @@ public function get_transfer_logs(int $assessmentmappingid, int $userid, ?string
* @param int $id Assessment mapping ID.
*
* @return false|mixed
* @throws \dml_exception
* @throws \dml_exception|\coding_exception
*/
public function get_mab_by_mapping_id(int $id): mixed {
public function get_mab_and_map_info_by_mapping_id(int $id): mixed {
global $DB;
$sql = "SELECT cg.*

// Try to get the cache first.
$key = 'map_mab_info_' . $id;
$cache = cachemanager::get_cache(cachemanager::CACHE_AREA_MAPPING_MAB_INFO, $key);
if (!empty($cache)) {
return $cache;
}

// Define the SQL query for retrieving the information.
$sql = "SELECT
cg.*,
am.courseid,
am.sourceid,
am.sourcetype,
am.moduletype,
am.reassessment,
am.enableextension
FROM {" . self::TABLE_COMPONENT_GRADE . "} cg
JOIN {" . self::TABLE_ASSESSMENT_MAPPING . "} am ON cg.id = am.componentgradeid
INNER JOIN {" . self::TABLE_ASSESSMENT_MAPPING . "} am
ON cg.id = am.componentgradeid
WHERE am.id = :id";

return $DB->get_record_sql($sql, ['id' => $id]);
// Fetch the record from the database.
$map_mab_info = $DB->get_record_sql($sql, ['id' => $id]);
if (!empty($map_mab_info)) {
// Set the cache.
cachemanager::set_cache(
cachemanager::CACHE_AREA_MAPPING_MAB_INFO,
$key,
$map_mab_info,
strtotime('+1 month')
);
}
return $map_mab_info;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion classes/output/pushrecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ protected function set_transfer_records(int $assessmentmappingid, int $studentid
// The Easikit Get Student API will remove the students whose marks had been transferred successfully.
// Find the assessment component <MAP CODE>-<MAB SEQ> for that transfer log,
// so that we can display the transfer status of mark transfer in the corresponding assessment component mapping.
$mab = $this->manager->get_mab_by_mapping_id($assessmentmappingid);
$mab = $this->manager->get_mab_and_map_info_by_mapping_id($assessmentmappingid);
if (!empty($mab)) {
$this->componentgrade = $mab->mapcode . '-' . $mab->mabseq;
}
Expand Down
59 changes: 59 additions & 0 deletions classes/user_enrolment_callbacks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace local_sitsgradepush;

use core_enrol\hook\after_user_enrolled;

/**
* Hook callbacks to get the enrolment information.
*
* @package local_sitsgradepush
* @copyright 2024 onwards University College London {@link https://www.ucl.ac.uk/}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Alex Yeung <[email protected]>
*/
class user_enrolment_callbacks {

/**
* Callback for the user_enrolment hook.
*
* @param after_user_enrolled $hook
* @throws \dml_exception
*/
public static function process_extensions(after_user_enrolled $hook): void {
global $DB;

$instance = $hook->get_enrolinstance();

// Check the enrolled user has a gradable role.
if (!extensionmanager::user_has_gradable_role($hook->get_userid(), $instance->courseid)) {
return; // User does not have a gradable role in the course, exit early.
}

// Fetch all mappings for the course.
$mappings = $DB->get_records('local_sitsgradepush_mapping', ['courseid' => $instance->courseid]);

if (empty($mappings)) {
return; // No mappings found, exit early.
}

// Process each mapping in course.
foreach ($mappings as $mapping) {
extensionmanager::update_sora_for_mapping($mapping->id, $hook->get_userid());
}
}
}
5 changes: 5 additions & 0 deletions db/caches.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@
'simplekeys' => true,
'simpledata' => false,
],
'mappingmabinfo' => [
'mode' => cache_store::MODE_APPLICATION,
'simplekeys' => true,
'simpledata' => false,
],
];
33 changes: 33 additions & 0 deletions db/hooks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Hook callbacks for enrol_manual
*
* @package local_sitsgradepush
* @copyright 2024 onwards University College London {@link https://www.ucl.ac.uk/}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Alex Yeung <[email protected]>
*/

defined('MOODLE_INTERNAL') || die();

$callbacks = [
[
'hook' => core_enrol\hook\after_user_enrolled::class,
'callback' => 'local_sitsgradepush\user_enrolment_callbacks::process_extensions',
],
];
2 changes: 2 additions & 0 deletions lang/en/local_sitsgradepush.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@
$string['error:mab_has_push_records'] = 'Assessment component mapping cannot be updated as marks have been transfered for {$a}';
$string['error:mab_invalid_for_mapping'] = 'This assessment component is not valid for mapping due to the following reasons: {$a}.';
$string['error:mab_not_found'] = 'Assessment component not found. ID: {$a}';
$string['error:mab_or_mapping_not_found'] = 'Mab or mapping not found. Mapping ID: {$a}';
$string['error:mapassessment'] = 'You do not have permission to map assessment.';
$string['error:mappings_is_empty'] = 'Mappings is empty';
$string['error:marks_transfer_failed'] = 'Marks transfer failed.';
$string['error:missingparams'] = 'Missing parameters.';
$string['error:missingrequiredconfigs'] = 'Missing required configs.';
Expand Down
6 changes: 3 additions & 3 deletions tests/manager_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -1269,19 +1269,19 @@ public function test_check_response(): void {
/**
* Test the get mab by mapping id method.
*
* @covers \local_sitsgradepush\manager::get_mab_by_mapping_id
* @covers \local_sitsgradepush\manager::get_mab_and_map_info_by_mapping_id
* @return void
* @throws \ReflectionException
* @throws \coding_exception
* @throws \dml_exception
* @throws \moodle_exception
*/
public function test_get_mab_by_mapping_id(): void {
public function test_get_mab_and_map_info_by_mapping_id(): void {
// Set up the test environment.
$this->setup_testing_environment(assessmentfactory::get_assessment('mod', $this->assign1->cmid));

// Test the mab is returned.
$mab = $this->manager->get_mab_by_mapping_id($this->mappingid1);
$mab = $this->manager->get_mab_and_map_info_by_mapping_id($this->mappingid1);
$this->assertEquals($this->mab1->id, $mab->id);
}

Expand Down
2 changes: 1 addition & 1 deletion version.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

$plugin->component = 'local_sitsgradepush';
$plugin->release = '0.1.0';
$plugin->version = 2024110100;
$plugin->version = 2024110101;
$plugin->requires = 2024042200;
$plugin->maturity = MATURITY_ALPHA;
$plugin->dependencies = [
Expand Down

0 comments on commit b3a2dea

Please sign in to comment.