diff --git a/course/classes/reportbuilder/datasource/participants.php b/course/classes/reportbuilder/datasource/participants.php index 1cb1d290157..7c503495b40 100644 --- a/course/classes/reportbuilder/datasource/participants.php +++ b/course/classes/reportbuilder/datasource/participants.php @@ -113,18 +113,22 @@ class participants extends datasource { ); // Join completion entity. - $completionentity = new completion(); + $completionentity = (new completion()) + ->set_table_aliases([ + 'course' => $course, + 'user' => $user, + ]); $completion = $completionentity->get_table_alias('course_completion'); - $completionentity->add_joins($userentity->get_joins()); - $completionentity->add_join(" - LEFT JOIN {course_completions} {$completion} - ON {$completion}.course = {$course}.id AND {$completion}.userid = {$user}.id - "); - $completionentity->set_table_alias('user', $user); - $this->add_entity($completionentity); + $this->add_entity($completionentity + ->add_joins($userentity->get_joins()) + ->add_join(" + LEFT JOIN {course_completions} {$completion} + ON {$completion}.course = {$course}.id AND {$completion}.userid = {$user}.id") + ); // Join course access entity. - $accessentity = new access(); + $accessentity = (new access()) + ->set_table_alias('user', $user); $lastaccess = $accessentity->get_table_alias('user_lastaccess'); $accessentity->add_joins($userentity->get_joins()); $accessentity->add_join(" diff --git a/course/classes/reportbuilder/local/entities/completion.php b/course/classes/reportbuilder/local/entities/completion.php index 78f00305275..52dedaf820c 100644 --- a/course/classes/reportbuilder/local/entities/completion.php +++ b/course/classes/reportbuilder/local/entities/completion.php @@ -22,9 +22,13 @@ use core_reportbuilder\local\entities\base; use core_course\reportbuilder\local\formatters\completion as completion_formatter; use core_reportbuilder\local\filters\boolean_select; use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\helpers\database; use core_reportbuilder\local\helpers\format; use core_reportbuilder\local\report\column; use core_reportbuilder\local\report\filter; +use completion_criteria_completion; +use completion_info; +use html_writer; use lang_string; use stdClass; @@ -111,6 +115,49 @@ class completion extends base { return format::boolean_as_text($value); }); + // Completion criteria column. + $criterias = database::generate_alias(); + $columns[] = (new column( + 'criteria', + new lang_string('criteria', 'core_completion'), + $this->get_entity_name() + )) + ->add_joins($this->get_joins()) + // Determine whether any criteria exist for the course. We also group per course, rather than report each separately. + ->add_join("LEFT JOIN ( + SELECT DISTINCT course FROM {course_completion_criteria} + ) {$criterias} ON {$criterias}.course = {$course}.id") + ->set_type(column::TYPE_TEXT) + // Select enough fields to determine user criteria for the course. + ->add_field("{$criterias}.course", 'courseid') + ->add_field("{$course}.enablecompletion") + ->add_field("{$user}.id", 'userid') + ->set_disabled_aggregation_all() + ->add_callback(static function($id, stdClass $record): string { + if (!$record->courseid) { + return ''; + } + + $info = new completion_info((object) ['id' => $record->courseid, 'enablecompletion' => $record->enablecompletion]); + if ($info->get_aggregation_method() == COMPLETION_AGGREGATION_ALL) { + $title = get_string('criteriarequiredall', 'core_completion'); + } else { + $title = get_string('criteriarequiredany', 'core_completion'); + } + + // Map all completion data to their criteria summaries. + $items = array_map(static function(completion_criteria_completion $completion): string { + $criteria = $completion->get_criteria(); + + return get_string('criteriasummary', 'core_completion', [ + 'type' => $criteria->get_details($completion)['type'], + 'summary' => $criteria->get_title_detailed(), + ]); + }, $info->get_completions((int) $record->userid)); + + return $title . html_writer::alist($items); + }); + // Progress percentage column. $columns[] = (new column( 'progresspercent', diff --git a/course/tests/reportbuilder/datasource/participants_test.php b/course/tests/reportbuilder/datasource/participants_test.php index eb0bf7fd3f1..0eb6acde61c 100644 --- a/course/tests/reportbuilder/datasource/participants_test.php +++ b/course/tests/reportbuilder/datasource/participants_test.php @@ -19,6 +19,7 @@ declare(strict_types=1); namespace core_course\reportbuilder\datasource; use completion_completion; +use completion_criteria_self; use core_collator; use core_reportbuilder\local\filters\boolean_select; use core_reportbuilder\local\filters\date; @@ -33,7 +34,6 @@ defined('MOODLE_INTERNAL') || die(); global $CFG; require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php"); -require_once("{$CFG->libdir}/gradelib.php"); /** * Course participants datasource tests @@ -47,6 +47,16 @@ require_once("{$CFG->libdir}/gradelib.php"); */ class participants_test extends core_reportbuilder_testcase { + /** + * Load required test libraries + */ + public static function setUpBeforeClass(): void { + global $CFG; + + require_once("{$CFG->libdir}/gradelib.php"); + require_once("{$CFG->dirroot}/completion/criteria/completion_criteria_self.php"); + } + /** * Test default datasource */ @@ -107,8 +117,11 @@ class participants_test extends core_reportbuilder_testcase { $group = self::getDataGenerator()->create_group(['courseid' => $course->id]); self::getDataGenerator()->create_group_member(['groupid' => $group->id, 'userid' => $user1->id]); - // Mark course as completed for the user. - $ccompletion = new completion_completion(array('course' => $course->id, 'userid' => $user1->id)); + // Create self completion, mark as complete for the user. + $criteriaconfig = (object) ['id' => $course->id, 'criteria_self' => true]; + (new completion_criteria_self())->update_config($criteriaconfig); + + $ccompletion = new completion_completion(['course' => $course->id, 'userid' => $user1->id]); $ccompletion->mark_enrolled($timestart); $ccompletion->mark_complete($timecompleted); @@ -146,6 +159,7 @@ class participants_test extends core_reportbuilder_testcase { $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:name']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'completion:criteria']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'completion:completed']); $generator->create_column(['reportid' => $report->get('id'), @@ -197,6 +211,7 @@ class participants_test extends core_reportbuilder_testcase { 'student', // Role shortname. 'Students generally have fewer privileges within a course.', // Role description. $group->name, // Group name. + "All criteria below are required", // Completion criteria. 'Yes', // Course completed. userdate($timelastaccess), // Time last access. '100.0%', // Progress percentage. diff --git a/lang/en/completion.php b/lang/en/completion.php index 41aec434b90..37a5ab21940 100644 --- a/lang/en/completion.php +++ b/lang/en/completion.php @@ -137,6 +137,7 @@ $string['criteria'] = 'Criteria'; $string['criteriagroup'] = 'Criteria group'; $string['criteriarequiredall'] = 'All criteria below are required'; $string['criteriarequiredany'] = 'Any criteria below are required'; +$string['criteriasummary'] = '{$a->type}: {$a->summary}'; $string['csvdownload'] = 'Download in spreadsheet format (UTF-8 .csv)'; $string['datepassed'] = 'Date passed'; $string['days'] = 'Days';