-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add logic to upgrade legacy courses to the current format
- Loading branch information
1 parent
837e298
commit 22cf619
Showing
2 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
119 changes: 119 additions & 0 deletions
119
classes/local/upgrade/upgrade_3_2_0_to_4_0_0_completionlib.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
<?php | ||
|
||
namespace local_adler\local\upgrade; | ||
|
||
use cm_info; | ||
use dml_exception; | ||
use local_adler\helpers; | ||
use local_adler\local\db\moodle_core_repository; | ||
use local_adler\local\exceptions\not_an_adler_course_exception; | ||
use local_logging\logger; | ||
use moodle_exception; | ||
|
||
class upgrade_3_2_0_to_4_0_0_completionlib { | ||
private int $course_id; | ||
private logger $logger; | ||
private moodle_core_repository $moodle_core_repository; | ||
public function __construct(int $course_id) { | ||
$this->logger = new logger('local_adler', self::class); | ||
$this->course_id = $course_id; | ||
$this->moodle_core_repository = new moodle_core_repository(); | ||
} | ||
|
||
/** | ||
* @throws not_an_adler_course_exception | ||
* @throws moodle_exception | ||
*/ | ||
public function execute(): void { | ||
// Check if the course is an Adler course | ||
if (!helpers::course_is_adler_course($this->course_id)) { | ||
throw new not_an_adler_course_exception(); | ||
} | ||
|
||
$this->upgrade_modules(); | ||
$this->resetCourseCache(); | ||
} | ||
|
||
/** | ||
* @param cm_info $cm_info | ||
* @return void | ||
* @throws dml_exception | ||
*/ | ||
private function upgrade_module(cm_info $cm_info): void { | ||
if ($cm_info->completion == COMPLETION_TRACKING_MANUAL) { | ||
if ($cm_info->modname == "h5pactivity") { | ||
$this->upgrade_h5p_module($cm_info); | ||
} else { | ||
$this->upgrade_normal_module($cm_info); | ||
} | ||
} else { | ||
$this->logger->warning('Completion for cm ' . $cm_info->id . ' is already set to auto, skipping'); | ||
} | ||
} | ||
|
||
/** | ||
* @return void | ||
* @throws moodle_exception | ||
*/ | ||
private function resetCourseCache(): void { | ||
purge_caches(['courses' => $this->course_id]); | ||
get_fast_modinfo($this->course_id, 0, true); | ||
} | ||
|
||
/** | ||
* @return void | ||
* @throws moodle_exception | ||
*/ | ||
public function upgrade_modules(): void { | ||
global $DB; | ||
$cm_infos = get_fast_modinfo($this->course_id)->get_cms(); | ||
|
||
$transaction = $DB->start_delegated_transaction(); | ||
foreach ($cm_infos as $cm_info) { | ||
$this->upgrade_module($cm_info); | ||
} | ||
$transaction->allow_commit(); | ||
} | ||
|
||
/** | ||
* @param cm_info $cm_info | ||
* @return void | ||
* @throws dml_exception | ||
*/ | ||
public function upgrade_h5p_module(cm_info $cm_info): void { | ||
$grade_item = $this->moodle_core_repository->get_grade_item('h5pactivity', $cm_info->instance); | ||
if ($grade_item) { | ||
$this->logger->info('Setting completion for h5p cm ' . $cm_info->id . ' to "passing grade"'); | ||
$this->set_completion_to_auto($cm_info, false); | ||
$this->moodle_core_repository->update_grade_item_record($grade_item->id, [ | ||
'gradepass' => $grade_item->grademax | ||
]); | ||
} else { | ||
$this->logger->error('No grade item found for h5p cm ' . $cm_info->id); | ||
} | ||
} | ||
|
||
/** | ||
* @param cm_info $cm_info | ||
* @return void | ||
* @throws dml_exception | ||
*/ | ||
public function upgrade_normal_module(cm_info $cm_info): void { | ||
$this->logger->info('Setting completion for cm ' . $cm_info->id . ' to view tracking'); | ||
$this->set_completion_to_auto($cm_info, true); | ||
} | ||
|
||
/** | ||
* @param cm_info $cm_info | ||
* @param bool $view if true: complete on view, if false: complete when achieving a passing grade | ||
* @return void | ||
* @throws dml_exception | ||
*/ | ||
public function set_completion_to_auto(cm_info $cm_info, bool $view): void { | ||
$this->moodle_core_repository->update_course_module_record($cm_info->id, [ | ||
'completion' => 2, | ||
'completionpassgrade' => $view ? 0 : 1, | ||
'completionview' => $view ? 1 : 0 | ||
]); | ||
} | ||
} |
162 changes: 162 additions & 0 deletions
162
tests/local/upgrade/upgrade_3_2_0_to_4_0_0_completionlib_test.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
<?php /** @noinspection PhpIllegalPsrClassPathInspection */ | ||
|
||
global $CFG; | ||
|
||
use local_adler\lib\adler_testcase; | ||
use local_adler\local\backport\backport_cm_completion_details; | ||
use local_adler\local\exceptions\not_an_adler_course_exception; | ||
use local_adler\local\upgrade\upgrade_3_2_0_to_4_0_0_completionlib; | ||
|
||
require_once($CFG->dirroot . '/local/adler/tests/lib/adler_testcase.php'); | ||
|
||
class upgrade_3_2_0_to_4_0_0_completionlib_test extends adler_testcase { | ||
public function provide_execute_simple_learning_element_data(): array { | ||
return [ | ||
'simple LE' => [ | ||
'module' => 'url' | ||
], | ||
'h5p LE' => [ | ||
'module' => 'h5pactivity' | ||
] | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider provide_execute_simple_learning_element_data | ||
*/ | ||
public function test_execute(string $module_type) { | ||
global $DB; | ||
|
||
// create course | ||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); | ||
// make course an adler course | ||
$this->getDataGenerator()->get_plugin_generator('local_adler')->create_adler_course_object($course->id); | ||
|
||
// create module | ||
$user = $this->getDataGenerator()->create_user(); | ||
$this->setUser($user); | ||
$module = $this->create_legacy_module($module_type, $course->id); | ||
|
||
$cud = new upgrade_3_2_0_to_4_0_0_completionlib($course->id); | ||
$cud->execute(); | ||
|
||
// check if completion is set to view tracking | ||
$cm = get_fast_modinfo($course->id)->get_cm($module->cmid); | ||
$this->assertEquals(COMPLETION_TRACKING_AUTOMATIC, $cm->completion); | ||
if ($module_type == 'url') { | ||
$this->assertEquals(0, $cm->completionpassgrade); | ||
$this->assertEquals(1, $cm->completionview); | ||
} else { | ||
$this->assertEquals(1, $cm->completionpassgrade); | ||
$this->assertEquals(0, $cm->completionview); | ||
// check passing grade | ||
$grade_max = $DB->get_field('grade_items', 'grademax', ['itemmodule' => $module_type, 'iteminstance' => $module->id]); | ||
$pass_grade = $DB->get_field('grade_items', 'gradepass', ['itemmodule' => $module_type, 'iteminstance' => $module->id]); | ||
$this->assertEquals($grade_max, $pass_grade); | ||
} | ||
} | ||
|
||
public function test_completion_already_auto() { | ||
// create course | ||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); | ||
// make course an adler course | ||
$this->getDataGenerator()->get_plugin_generator('local_adler')->create_adler_course_object($course->id); | ||
|
||
// create module | ||
$module = $this->getDataGenerator()->create_module('url', [ | ||
'course' => $course->id, | ||
'completion' => COMPLETION_TRACKING_AUTOMATIC, | ||
'completeionview' => 0, | ||
'completionpassgrade' => 0 | ||
]); | ||
|
||
$cud = new upgrade_3_2_0_to_4_0_0_completionlib($course->id); | ||
$cud->execute(); | ||
|
||
// check if completion is set to view tracking | ||
$cm = get_fast_modinfo($course->id)->get_cm($module->cmid); | ||
$this->assertEquals(COMPLETION_TRACKING_AUTOMATIC, $cm->completion); | ||
$this->assertEquals(0, $cm->completionpassgrade); | ||
$this->assertEquals(0, $cm->completionview); | ||
} | ||
|
||
public function test_execute_not_adler_course() { | ||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); | ||
|
||
$this->expectException(not_an_adler_course_exception::class); | ||
|
||
$cud = new upgrade_3_2_0_to_4_0_0_completionlib($course->id); | ||
$cud->execute(); | ||
} | ||
|
||
|
||
|
||
private function create_legacy_module(string $module_type, int $course_id) { | ||
return $this->getDataGenerator()->get_plugin_generator('mod_' . $module_type)->create_instance([ | ||
'course' => $course_id, | ||
'completion' => COMPLETION_TRACKING_MANUAL, | ||
'completeionview' => 0, | ||
'completionpassgrade' => 0 | ||
]); | ||
} | ||
|
||
public function test_with_user_attempt() { | ||
// create course | ||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); | ||
// make course an adler course | ||
$this->getDataGenerator()->get_plugin_generator('local_adler')->create_adler_course_object($course->id); | ||
|
||
// create module | ||
$user = $this->getDataGenerator()->create_user(); | ||
$this->setUser($user); | ||
$module = $this->create_legacy_module('h5pactivity', $course->id); | ||
|
||
// mark as completed for user | ||
$completion = new completion_info($course); | ||
$completion->update_state(get_fast_modinfo($course)->get_cm($module->cmid), COMPLETION_COMPLETE, $user->id); | ||
// verify element is completed for user | ||
$h5p_completion_state = backport_cm_completion_details::get_instance( | ||
get_fast_modinfo($course)->get_cm($module->cmid), | ||
$user->id | ||
); | ||
$this->assertTrue($h5p_completion_state->is_overall_complete(), 'Element is not completed before test'); | ||
|
||
$cud = new upgrade_3_2_0_to_4_0_0_completionlib($course->id); | ||
$cud->execute(); | ||
|
||
// check if completion is set to view tracking | ||
$cm = get_fast_modinfo($course->id)->get_cm($module->cmid); | ||
$this->assertEquals(COMPLETION_TRACKING_AUTOMATIC, $cm->completion); | ||
$this->assertEquals(1, $cm->completionpassgrade); | ||
$this->assertEquals(0, $cm->completionview); | ||
|
||
// verify element is still completed | ||
$h5p_completion_state = backport_cm_completion_details::get_instance( // has to be recreated to reflect completion state change | ||
get_fast_modinfo($course)->get_cm($module->cmid), | ||
$user->id | ||
); | ||
$this->assertTrue($h5p_completion_state->is_overall_complete(), 'Element is not completed after test'); | ||
|
||
// create attempt | ||
$this->create_h5p_attempt($module, $course->id, $user->id); | ||
// verify element is still completed | ||
$h5p_completion_state = backport_cm_completion_details::get_instance( // has to be recreated to reflect completion state change | ||
get_fast_modinfo($course)->get_cm($module->cmid), | ||
$user->id | ||
); | ||
} | ||
|
||
private function create_h5p_attempt(stdClass $module, int $course_id, int $user_id) { | ||
global $CFG; | ||
$grade_item = grade_item::fetch([ | ||
'itemname' => $module->name, | ||
'gradetype' => GRADE_TYPE_VALUE, | ||
'courseid' => $course_id | ||
]); | ||
$grade_data_class = new stdClass(); | ||
$grade_data_class->userid = $user_id; | ||
$grade_data_class->rawgrade = $grade_item->grademax; | ||
require_once($CFG->dirroot . '/mod/h5pactivity/lib.php'); | ||
h5pactivity_grade_item_update($module, $grade_data_class); | ||
} | ||
} |