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 72578a2
Show file tree
Hide file tree
Showing 18 changed files with 264 additions and 55 deletions.
20 changes: 14 additions & 6 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 @@ -49,13 +52,18 @@ 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;
if (empty($cache) || empty($expires) || time() >= $expires) {
$cache = cache::make('local_sitsgradepush', $area);
$cachevalue = $cache->get($key);
$expires = $cache->get('expires_' . $key);

if (empty($cachevalue) || empty($expires) || time() >= $expires) {
if (time() >= $expires) {
// Cache expired, delete it.
self::purge_cache($area, $key);
}
return null;
} else {
return $cache;
return $cachevalue;
}
}

Expand All @@ -71,7 +79,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
10 changes: 5 additions & 5 deletions classes/extension/ec.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ public function get_new_deadline(): string {

/**
* Process the extension.
*
* @param array $mappings
* @throws \dml_exception
*/
public function process_extension(): void {
// Get all mappings for the SITS assessment.
// We only allow one mapping per SITS assessment for now.
$mappings = $this->get_mappings_by_mab($this->get_mab_identifier());

public function process_extension(array $mappings): void {
// Exit if empty mappings.
if (empty($mappings)) {
return;
}
Expand Down
4 changes: 2 additions & 2 deletions classes/extension/extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public static function is_module_supported(?string $module): bool {
* @throws \dml_exception
* @throws \moodle_exception
*/
protected function get_mappings_by_mab(string $mabidentifier): array {
public function get_mappings_by_mab(string $mabidentifier): array {
global $DB;

// Extract the map code and MAB sequence number from the MAB identifier.
Expand Down 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
4 changes: 3 additions & 1 deletion classes/extension/iextension.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
interface iextension {
/**
* Process the extension.
*
* @param array $mappings SITS component mappings.
*/
public function process_extension(): void;
public function process_extension(array $mappings): void;
}
18 changes: 11 additions & 7 deletions classes/extension/sora.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,20 +182,24 @@ 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 {
// Empty mappings, exit early.
if (empty($mappings)) {
return;
}

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());

// No mappings found.
if (empty($mappings)) {
// Exit if SORA extra assessment duration and rest duration are both 0.
if ($this->extraduration == 0 && $this->restduration == 0) {
return;
}

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);
}
}
73 changes: 63 additions & 10 deletions classes/extensionmanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,90 @@ 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, true);

// 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)) {
return;
}

// Process SORA extension for each student.
// Process SORA extension for each student or the specified student if user id is provided.
foreach ($students as $student) {
$sora = new sora();
$sora->set_properties_from_get_students_api($student);
$sora->process_extension();

// Process the extension for the specified user or all users.
if (!$userid || $userid === $sora->get_userid()) {
$sora->process_extension([$mapinfo]);
if ($userid) {
break; // Exit the loop after processing the specified user.
}
}
}
} 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);

$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;
}

/**
* Check if the extension is enabled.
*
* @return bool
* @throws \dml_exception
*/
public static function is_extension_enabled() {
return get_config('local_sitsgradepush', 'extension_enabled');
}
}
53 changes: 43 additions & 10 deletions classes/manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@

namespace local_sitsgradepush;

use core_component;
use core_course\customfield\course_handler;
use DirectoryIterator;
use grade_tree;
use local_sitsgradepush\api\client_factory;
use local_sitsgradepush\api\iclient;
use local_sitsgradepush\api\irequest;
Expand Down Expand Up @@ -488,7 +486,7 @@ public function save_assessment_mapping(\stdClass $data): int|bool {
}
$record->componentgradeid = $data->componentgradeid;
$record->reassessment = $data->reassessment;
$record->enableextension = (get_config('local_sitsgradepush', 'extension_enabled') &&
$record->enableextension = (extensionmanager::is_extension_enabled() &&
(isset($record->moduletype) && extension::is_module_supported($record->moduletype))) ? 1 : 0;
$record->timecreated = time();
$record->timemodified = time();
Expand Down Expand Up @@ -882,18 +880,53 @@ public function get_transfer_logs(int $assessmentmappingid, int $userid, ?string
* Get component grade details for a given assessment mapping ID.
*
* @param int $id Assessment mapping ID.
* @param bool $extensionenabledonly Only return the mapping with extension enabled.
*
* @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, bool $extensionenabledonly = false): mixed {
global $DB;
$sql = "SELECT cg.*
FROM {" . self::TABLE_COMPONENT_GRADE . "} cg
JOIN {" . self::TABLE_ASSESSMENT_MAPPING . "} am ON cg.id = am.componentgradeid
WHERE am.id = :id";

return $DB->get_record_sql($sql, ['id' => $id]);
// 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;
}

if ($extensionenabledonly) {
$extensionenabled = 'AND am.enableextension = 1';
} else {
$extensionenabled = '';
}

// 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
INNER JOIN {" . self::TABLE_ASSESSMENT_MAPPING . "} am
ON cg.id = am.componentgradeid
WHERE am.id = :id $extensionenabled";

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

/**
Expand Down
3 changes: 2 additions & 1 deletion classes/observer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

use local_sitsgradepush\cachemanager;
use local_sitsgradepush\extensionmanager;
use local_sitsgradepush\manager;
use local_sitsgradepush\taskmanager;

Expand Down Expand Up @@ -96,7 +97,7 @@ public static function assessment_mapped(\local_sitsgradepush\event\assessment_m
cachemanager::purge_cache(cachemanager::CACHE_AREA_STUDENTSPR, $key);

// Add the process extensions adhoc task if process extensions is enabled.
if (get_config('local_sitsgradepush', 'extension_enabled')) {
if (extensionmanager::is_extension_enabled()) {
taskmanager::add_process_extensions_adhoc_task($data['other']['mappingid']);
}
}
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
3 changes: 2 additions & 1 deletion classes/task/process_aws_sora_updates.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
namespace local_sitsgradepush\task;

use local_sitsgradepush\extension\sora_queue_processor;
use local_sitsgradepush\extensionmanager;

/**
* Scheduled task to process AWS SORA updates.
Expand All @@ -43,7 +44,7 @@ public function get_name() {
*/
public function execute(): void {
// Skip if extension is not enabled.
if (!get_config('local_sitsgradepush', 'extension_enabled')) {
if (!extensionmanager::is_extension_enabled()) {
mtrace('Extension processing is not enabled. Exiting...');
return;
}
Expand Down
Loading

0 comments on commit 72578a2

Please sign in to comment.