Skip to content

Commit

Permalink
CTP-3990 Marks items under gradebook category as summative
Browse files Browse the repository at this point in the history
  • Loading branch information
aydevworks committed Dec 11, 2024
1 parent 1e1ee5b commit 31f397f
Show file tree
Hide file tree
Showing 7 changed files with 494 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/moodle-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

services:
postgres:
image: postgres:13
image: postgres:14
env:
POSTGRES_USER: 'postgres'
POSTGRES_HOST_AUTH_METHOD: 'trust'
Expand Down
150 changes: 134 additions & 16 deletions classes/assesstype.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,19 @@ public static function update_assess_type(int|\stdClass $mapping, string $action
return;
}

// Mapped assessment is a course module, set grdade item ID to 0.
if ($mapping->sourcetype === assessmentfactory::SOURCETYPE_MOD) {
$cmid = $mapping->sourceid;
$gradeitemid = 0;
} else {
// Mapped assessment is a gradebook item or category, set course module ID to 0.
$cmid = 0;
$assessment = assessmentfactory::get_assessment($mapping->sourcetype, $mapping->sourceid);
$gradeitems = $assessment->get_grade_items();
$gradeitemid = $gradeitems[0]->id;
}
// Set course module ID if the mapped assessment is a course module, otherwise set to 0.
$cmid = $mapping->sourcetype === assessmentfactory::SOURCETYPE_MOD ? $mapping->sourceid : 0;

// Everything that is not a course module is a grade item or category.
$gradeitemid = $cmid ? 0 : assessmentfactory::get_assessment($mapping->sourcetype, $mapping->sourceid)
->get_grade_items()[0]->id;

$lockstatus = $action === self::ACTION_LOCK ? 1 : 0;
assess_type::update_type($mapping->courseid, assess_type::ASSESS_TYPE_SUMMATIVE, $cmid, $gradeitemid, $lockstatus);

// Set assessment type and lock status.
if ($action === self::ACTION_LOCK) {
assess_type::update_type($mapping->courseid, assess_type::ASSESS_TYPE_SUMMATIVE, $cmid, $gradeitemid, 1);
} else if ($action === self::ACTION_UNLOCK) {
assess_type::update_type($mapping->courseid, assess_type::ASSESS_TYPE_SUMMATIVE, $cmid, $gradeitemid, 0);
// Update assess type for items (grade items or activities) under the grade category.
if ($mapping->sourcetype === assessmentfactory::SOURCETYPE_GRADE_CATEGORY) {
self::update_assess_type_items_under_gradecategory($action, $mapping->sourceid);
}
} catch (\Exception $e) {
logger::log('Failed to update assessment type and lock status.', null, null, $e->getMessage());
Expand All @@ -96,4 +92,126 @@ public static function is_assess_type_installed(): bool {
'local_assess_type'
);
}

/**
* Update assessment type and lock status for grade item when it is updated outside marks transfer.
*
* @param \core\event\grade_item_updated $event
* @return void
* @throws \coding_exception
* @throws \dml_exception
*/
public static function grade_item_updated(\core\event\grade_item_updated $event): void {
global $DB;

// Skip if assessment type plugin is not installed.
if (!self::is_assess_type_installed()) {
return;
}

$gradeitem = $event->get_record_snapshot('grade_items', $event->objectid);

// Determine source type and source ID based on item type.
switch ($gradeitem->itemtype) {
case 'manual':
$sourcetype = assessmentfactory::SOURCETYPE_GRADE_ITEM;
$sourceid = $gradeitem->id;
break;
case 'mod':
$sourcetype = assessmentfactory::SOURCETYPE_MOD;
$sourceid = get_coursemodule_from_instance(
$gradeitem->itemmodule,
$gradeitem->iteminstance,
$gradeitem->courseid
)->id;
break;
default:
return;
}

// Skip if grade item or activity is mapped. It will be handled by the mapping / unmapping actions.
if ($DB->record_exists(manager::TABLE_ASSESSMENT_MAPPING, ['sourcetype' => $sourcetype, 'sourceid' => $sourceid])) {
return;
}

// Depending on the grade item's category. If the category is mapped, mark it summative and lock it.
// Otherwise, unlock it.
$action = $DB->record_exists(
manager::TABLE_ASSESSMENT_MAPPING,
['sourcetype' => assessmentfactory::SOURCETYPE_GRADE_CATEGORY, 'sourceid' => $gradeitem->categoryid]
) ? self::ACTION_LOCK : self::ACTION_UNLOCK;

self::update_assess_type_items_under_gradecategory($action, $gradeitem->categoryid, $gradeitem);
}

/**
* Update assess type for grade items and activities under a grade category.
*
* @param string $action
* @param int $categoryid
* @param \stdClass|null $gradeitem
* @return void
* @throws \coding_exception
* @throws \dml_exception
*/
private static function update_assess_type_items_under_gradecategory(
string $action,
int $categoryid,
?\stdClass $gradeitem = null
): void {
$lockstatus = $action === self::ACTION_LOCK ? 1 : 0;
$gradeitems = $gradeitem ? [$gradeitem] : \grade_item::fetch_all(['categoryid' => $categoryid]);

foreach ($gradeitems as $item) {
if (!in_array($item->itemtype, ['mod', 'manual'])) {
continue;
}

// Workshop can have multiple grade items under the same category. Only unlock the workshop if it is the
// only grade item under the category.
if ($action === self::ACTION_UNLOCK && $item->itemmodule === 'workshop') {
if (!self::should_unlock_workshop($item->iteminstance)) {
continue;
}
}

$cmid = $item->itemtype === 'mod'
? get_coursemodule_from_instance($item->itemmodule, $item->iteminstance, $item->courseid)->id
: 0;
$gradeitemid = $item->itemtype === 'manual' ? $item->id : 0;

assess_type::update_type(
$item->courseid,
assess_type::ASSESS_TYPE_SUMMATIVE,
$cmid,
$gradeitemid,
$lockstatus
);
}
}

/**
* Check if the workshop should be unlocked. A workshop should be unlocked if there is no other grade item under
* a category that has been mapped.
*
* @param int $workshopinstanceid Workshop instance ID.
* @return bool
* @throws \dml_exception
*/
private static function should_unlock_workshop(int $workshopinstanceid): bool {
global $DB;
// Check if there is any grade item under the workshop that has category mapped.
$sql = "SELECT gi.id
FROM {grade_items} gi
JOIN {". manager::TABLE_ASSESSMENT_MAPPING . "} m ON gi.categoryid = m.sourceid AND m.sourcetype = :sourcetype
WHERE gi.itemmodule = :itemmodule AND gi.iteminstance = :workshopinstanceid";

$params = [
'sourcetype' => assessmentfactory::SOURCETYPE_GRADE_CATEGORY,
'itemmodule' => 'workshop',
'workshopinstanceid' => $workshopinstanceid,
];

return empty($DB->get_records_sql($sql, $params));
}
}
5 changes: 4 additions & 1 deletion classes/manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -1457,7 +1457,7 @@ public function remove_mapping(int $courseid, int $mappingid): void {
}

// Check the mapping exists.
if (!$DB->record_exists(self::TABLE_ASSESSMENT_MAPPING, ['id' => $mappingid])) {
if (!$mapping = $DB->get_record(self::TABLE_ASSESSMENT_MAPPING, ['id' => $mappingid])) {
throw new \moodle_exception('error:assessmentmapping', 'local_sitsgradepush', '', $mappingid);
}

Expand All @@ -1468,6 +1468,9 @@ public function remove_mapping(int $courseid, int $mappingid): void {

// Everything is fine, remove the mapping.
$DB->delete_records(self::TABLE_ASSESSMENT_MAPPING, ['id' => $mappingid]);

// Unlock the Moodle assessment in the local_assess_type plugin.
assesstype::update_assess_type($mapping, assesstype::ACTION_UNLOCK);
}

/**
Expand Down
12 changes: 12 additions & 0 deletions classes/observer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

use local_sitsgradepush\assessment\assessmentfactory;
use local_sitsgradepush\assesstype;
use local_sitsgradepush\cachemanager;
use local_sitsgradepush\manager;

Expand Down Expand Up @@ -92,4 +94,14 @@ public static function assessment_mapped(\local_sitsgradepush\event\assessment_m
cachemanager::purge_cache(cachemanager::CACHE_AREA_STUDENTSPR, $key);
}
}

/**
* Handle the grade item updated event.
*
* @param \core\event\grade_item_updated $event
* @return void
*/
public static function grade_item_updated(\core\event\grade_item_updated $event): void {
assesstype::grade_item_updated($event);
}
}
4 changes: 4 additions & 0 deletions db/events.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@
'callback' => 'local_sitsgradepush_observer::assessment_mapped',
'priority' => 200,
],
[
'eventname' => '\core\event\grade_item_updated',
'callback' => 'local_sitsgradepush_observer::grade_item_updated',
],
];
Loading

0 comments on commit 31f397f

Please sign in to comment.