Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CTP-3990 Marks items under gradebook category as summative #77

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
167 changes: 151 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;

// 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);
$lockstatus = $action === self::ACTION_LOCK ? 1 : 0;
assess_type::update_type($mapping->courseid, assess_type::ASSESS_TYPE_SUMMATIVE, $cmid, $gradeitemid, $lockstatus);

// 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,143 @@ 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 = $gradeitemid = $gradeitem->id;
$cmid = 0;
break;
case 'mod':
$sourcetype = assessmentfactory::SOURCETYPE_MOD;
$sourceid = $cmid = get_coursemodule_from_instance(
$gradeitem->itemmodule,
$gradeitem->iteminstance,
$gradeitem->courseid
)->id;
$gradeitemid = 0;
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;

// We only want to unlock grade items or activities that are summative and have been locked.
if ($action === self::ACTION_UNLOCK) {
$assesstyperecords = assess_type::get_assess_type_records_by_courseid(
$gradeitem->courseid,
assess_type::ASSESS_TYPE_SUMMATIVE
);

if (empty(array_filter(
$assesstyperecords,
fn($record) => $record->cmid == $cmid && $record->gradeitemid == $gradeitemid && $record->locked))
) {
return;
}
}

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
Loading