diff --git a/classes/cachemanager.php b/classes/cachemanager.php
index 41334ed..8692c30 100644
--- a/classes/cachemanager.php
+++ b/classes/cachemanager.php
@@ -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.
*
@@ -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 ($expires && time() >= $expires) {
+ // Cache expired, delete it.
+ self::purge_cache($area, $key);
+ }
return null;
} else {
- return $cache;
+ return $cachevalue;
}
}
@@ -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);
}
/**
diff --git a/classes/extension/ec.php b/classes/extension/ec.php
index edffff7..12151a2 100644
--- a/classes/extension/ec.php
+++ b/classes/extension/ec.php
@@ -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;
}
diff --git a/classes/extension/extension.php b/classes/extension/extension.php
index 56179ff..8959641 100644
--- a/classes/extension/extension.php
+++ b/classes/extension/extension.php
@@ -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.
@@ -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.
diff --git a/classes/extension/iextension.php b/classes/extension/iextension.php
index c494246..c3f6c55 100644
--- a/classes/extension/iextension.php
+++ b/classes/extension/iextension.php
@@ -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;
}
diff --git a/classes/extension/sora.php b/classes/extension/sora.php
index c44d89a..8c73370 100644
--- a/classes/extension/sora.php
+++ b/classes/extension/sora.php
@@ -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;
}
diff --git a/classes/extension/sora_queue_processor.php b/classes/extension/sora_queue_processor.php
index 3b84c6b..d725b31 100644
--- a/classes/extension/sora_queue_processor.php
+++ b/classes/extension/sora_queue_processor.php
@@ -16,6 +16,8 @@
namespace local_sitsgradepush\extension;
+use local_sitsgradepush\manager;
+
/**
* SORA queue processor.
*
@@ -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);
}
}
diff --git a/classes/extensionmanager.php b/classes/extensionmanager.php
index d1bb6ea..d775f3c 100644
--- a/classes/extensionmanager.php
+++ b/classes/extensionmanager.php
@@ -17,6 +17,7 @@
namespace local_sitsgradepush;
use local_sitsgradepush\extension\sora;
+use local_sitsgradepush\task\process_extensions_new_enrolment;
/**
* Manager class for extension related operations.
@@ -31,37 +32,77 @@ class extensionmanager {
/**
* Update SORA extension for students in a mapping.
*
- * @param int $mapid
+ * @param \stdClass $mapping Assessment component mapping ID.
+ * @param array $students Students data from the SITS get students API.
* @return void
* @throws \dml_exception
*/
- public static function update_sora_for_mapping(int $mapid): void {
+ public static function update_sora_for_mapping(\stdClass $mapping, array $students): 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);
+ if ($mapping->enableextension !== '1') {
+ throw new \moodle_exception('error:extension_not_enabled_for_mapping', 'local_sitsgradepush', '', $mapping->id);
}
- // Get students information for that assessment component.
- $students = $manager->get_students_from_sits($mab);
-
- // If no students found, nothing to do.
+ // If no students returned from SITS, 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();
+ $sora->process_extension([$mapping]);
}
} catch (\Exception $e) {
- logger::log($e->getMessage(), null, "Mapping ID: $mapid");
+ logger::log($e->getMessage(), null, "Mapping ID: $mapping->id");
}
}
+
+ /**
+ * Check if the extension is enabled.
+ *
+ * @return bool
+ * @throws \dml_exception
+ */
+ public static function is_extension_enabled(): bool {
+ return get_config('local_sitsgradepush', 'extension_enabled') == '1';
+ }
+
+ /**
+ * Check if the user is enrolling a gradable role.
+ *
+ * @param int $roleid Role ID.
+ * @return bool
+ */
+ public static function user_is_enrolling_a_gradable_role(int $roleid): bool {
+ global $CFG;
+
+ $gradebookroles = !empty($CFG->gradebookroles) ? explode(',', $CFG->gradebookroles) : [];
+
+ return in_array($roleid, $gradebookroles);
+ }
+
+ /**
+ * Get the user enrolment events stored for a course.
+ *
+ * @param int $courseid Course ID.
+ * @return array
+ * @throws \dml_exception
+ */
+ public static function get_user_enrolment_events(int $courseid): array {
+ global $DB;
+ $sql = "SELECT ue.*
+ FROM {local_sitsgradepush_enrol} ue
+ WHERE ue.courseid = :courseid AND ue.attempts < :maxattempts";
+
+ return $DB->get_records_sql(
+ $sql,
+ [
+ 'courseid' => $courseid,
+ 'maxattempts' => process_extensions_new_enrolment::MAX_ATTEMPTS,
+ ],
+ limitnum: process_extensions_new_enrolment::BATCH_LIMIT
+ );
+ }
}
diff --git a/classes/manager.php b/classes/manager.php
index 37e9511..829dbf7 100644
--- a/classes/manager.php
+++ b/classes/manager.php
@@ -17,10 +17,8 @@
namespace local_sitsgradepush;
use context_course;
-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;
@@ -181,7 +179,7 @@ public function fetch_component_grades_from_sits(array $modocc): void {
$this->check_response($response, $request);
// Set cache expiry to 1 hour.
- cachemanager::set_cache(cachemanager::CACHE_AREA_COMPONENTGRADES, $key, $response, 3600);
+ cachemanager::set_cache(cachemanager::CACHE_AREA_COMPONENTGRADES, $key, $response, HOURSECS);
// Save component grades to DB.
$this->save_component_grades($response);
@@ -215,7 +213,7 @@ public function fetch_marking_scheme_from_sits() {
$this->check_response($response, $request);
// Set cache expiry to 1 hour.
- cachemanager::set_cache(cachemanager::CACHE_AREA_MARKINGSCHEMES, $key, $response, 3600);
+ cachemanager::set_cache(cachemanager::CACHE_AREA_MARKINGSCHEMES, $key, $response, HOURSECS);
return $response;
} catch (\moodle_exception $e) {
@@ -489,7 +487,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();
@@ -609,12 +607,13 @@ public function get_student_from_sits(\stdClass $componentgrade, int $userid): m
* Get students for a grade component from SITS.
*
* @param \stdClass $componentgrade
+ * @param bool $refresh Refresh data from SITS.
* @return \cache_application|\cache_session|\cache_store|mixed
* @throws \coding_exception
* @throws \dml_exception
* @throws \moodle_exception
*/
- public function get_students_from_sits(\stdClass $componentgrade): mixed {
+ public function get_students_from_sits(\stdClass $componentgrade, bool $refresh = false): mixed {
// Stutalk Direct is not supported currently.
if ($this->apiclient->get_client_name() == 'Stutalk Direct') {
throw new \moodle_exception(
@@ -630,13 +629,16 @@ public function get_students_from_sits(\stdClass $componentgrade): mixed {
return tests_data_provider::get_behat_test_students_response($componentgrade->mapcode, $componentgrade->mabseq);
}
- // Try to get cache first.
$key = implode('_', [cachemanager::CACHE_AREA_STUDENTSPR, $componentgrade->mapcode, $componentgrade->mabseq]);
- $students = cachemanager::get_cache(cachemanager::CACHE_AREA_STUDENTSPR, $key);
-
- // Cache found, return students.
- if (!empty($students)) {
- return $students;
+ if ($refresh) {
+ // Clear cache.
+ cachemanager::purge_cache(cachemanager::CACHE_AREA_STUDENTSPR, $key);
+ } else {
+ // Try to get cache first.
+ $students = cachemanager::get_cache(cachemanager::CACHE_AREA_STUDENTSPR, $key);
+ if (!empty($students)) {
+ return $students;
+ }
}
// Build required data.
@@ -654,7 +656,7 @@ public function get_students_from_sits(\stdClass $componentgrade): mixed {
cachemanager::CACHE_AREA_STUDENTSPR,
$key,
$result,
- strtotime('+30 days'),
+ DAYSECS * 30
);
}
@@ -885,16 +887,47 @@ 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
+ am.id,
+ am.courseid,
+ am.sourceid,
+ am.sourcetype,
+ am.moduletype,
+ am.reassessment,
+ am.enableextension,
+ cg.id as mabid,
+ cg.mapcode,
+ cg.mabseq
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.
+ $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,
+ DAYSECS * 30
+ );
+ }
+ return $mapmabinfo;
}
/**
@@ -1444,6 +1477,31 @@ public function get_all_summative_grade_items(int $courseid): array {
return $results;
}
+ /**
+ * Get assessment mappings by course id.
+ *
+ * @param int $courseid
+ * @param bool $extensionenabledonly
+ * @return array
+ * @throws \dml_exception
+ */
+ public function get_assessment_mappings_by_courseid(int $courseid, bool $extensionenabledonly = false): array {
+ global $DB;
+
+ if ($extensionenabledonly) {
+ // Get mappings that are enabled for extension only.
+ $extensionenabledonlysql = 'AND am.enableextension = 1';
+ } else {
+ $extensionenabledonlysql = '';
+ }
+ $sql = "SELECT am.*, cg.mapcode, cg.mabseq
+ FROM {".self::TABLE_ASSESSMENT_MAPPING."} am
+ JOIN {".self::TABLE_COMPONENT_GRADE."} cg ON am.componentgradeid = cg.id
+ WHERE courseid = :courseid $extensionenabledonlysql";
+
+ return $DB->get_records_sql($sql, ['courseid' => $courseid]);
+ }
+
/**
* Delete assessment mapping.
*
diff --git a/classes/observer.php b/classes/observer.php
index 6bfd183..46a6758 100644
--- a/classes/observer.php
+++ b/classes/observer.php
@@ -15,6 +15,7 @@
// along with Moodle. If not, see .
use local_sitsgradepush\cachemanager;
+use local_sitsgradepush\extensionmanager;
use local_sitsgradepush\manager;
use local_sitsgradepush\taskmanager;
@@ -96,8 +97,8 @@ 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')) {
- taskmanager::add_process_extensions_adhoc_task($data['other']['mappingid']);
+ if (extensionmanager::is_extension_enabled()) {
+ taskmanager::add_process_extensions_for_new_mapping_adhoc_task($data['other']['mappingid']);
}
}
}
diff --git a/classes/output/pushrecord.php b/classes/output/pushrecord.php
index 8a1281c..ececd50 100644
--- a/classes/output/pushrecord.php
+++ b/classes/output/pushrecord.php
@@ -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