From 15f8c92d9eb10d0feca4e2d424e9a7c049237d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikel=20Mart=C3=ADn?= Date: Wed, 18 Jun 2025 14:52:40 +0200 Subject: [PATCH] MDL-83896 mod_lesson: Add new methods to lesson class Add new methods to the lesson class that will be needed by the overview page. --- .upgradenotes/MDL-83896-2025061812521463.yml | 7 ++ mod/lesson/locallib.php | 60 ++++++++++- mod/lesson/tests/locallib_test.php | 101 +++++++++++++++++++ 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 .upgradenotes/MDL-83896-2025061812521463.yml diff --git a/.upgradenotes/MDL-83896-2025061812521463.yml b/.upgradenotes/MDL-83896-2025061812521463.yml new file mode 100644 index 00000000000..ad275defe4d --- /dev/null +++ b/.upgradenotes/MDL-83896-2025061812521463.yml @@ -0,0 +1,7 @@ +issueNumber: MDL-83896 +notes: + mod_lesson: + - message: >- + Added new 'count_all_submissions', 'count_submitted_participants' and + 'count_all_participants' functions needed by the overview page. + type: improved diff --git a/mod/lesson/locallib.php b/mod/lesson/locallib.php index 5d428e2b593..189e2e3bb2f 100644 --- a/mod/lesson/locallib.php +++ b/mod/lesson/locallib.php @@ -1963,7 +1963,6 @@ class lesson extends lesson_base { return $DB->get_records('lesson_attempts', $params, 'timeseen ASC'); } - /** * Get a list of content pages (formerly known as branch tables) viewed in the lesson for the given user during an attempt. * @@ -3034,6 +3033,65 @@ class lesson extends lesson_base { return $DB->count_records('lesson_grades', array("lessonid" => $this->properties->id, "userid" => $userid)); } + /** + * Count all submissions by all users in the lesson. + * + * @return int the number of submissions (grades table) by all users in the lesson + */ + public function count_all_submissions(): int { + $db = \core\di::get(\moodle_database::class); + + [$esql, $eparams] = get_enrolled_sql($this->get_context(), 'mod/lesson:view'); + $sql = "SELECT COUNT(lg.id) + FROM {lesson_grades} lg + JOIN ($esql) e ON e.id = lg.userid + WHERE lg.lessonid = :lessonid"; + + return $db->count_records_sql($sql, ["lessonid" => $this->properties->id] + $eparams); + } + + /** + * Count the number of participants that have attempted the lesson. + * + * @return int the number of users that have attempted the lesson + */ + public function count_submitted_participants(): int { + $db = \core\di::get(\moodle_database::class); + [$esql, $eparams] = get_enrolled_sql($this->get_context(), 'mod/lesson:view'); + $sql = "SELECT COUNT(DISTINCT lg.userid) + FROM {lesson_grades} lg + JOIN ($esql) e ON e.id = lg.userid + WHERE lg.lessonid = :lessonid"; + + return $db->count_records_sql($sql, ["lessonid" => $this->properties->id] + $eparams); + } + + /** + * Count the number of participants that have access to the lesson. + * + * @return int the number of users that have access to view the lesson + */ + public function count_all_participants(): int { + $db = \core\di::get(\moodle_database::class); + + $join = get_enrolled_with_capabilities_join($this->get_context(), '', 'mod/lesson:view'); + $managersjoin = get_with_capability_join($this->get_context(), 'mod/lesson:manage', 'u.id'); + if (!$managersjoin->cannotmatchanyrows) { + $join = new \core\dml\sql_join( + $join->joins . "\n LEFT " . str_replace('ra', 'manager', $managersjoin->joins), + $join->wheres . " AND manager.userid IS NULL", + $join->params + ); + } + + $sql = "SELECT COUNT(DISTINCT u.id) + FROM {user} u + $join->joins + WHERE $join->wheres"; + + return $db->count_records_sql($sql, $join->params); + } + /** * Check if a user left a timed session. * diff --git a/mod/lesson/tests/locallib_test.php b/mod/lesson/tests/locallib_test.php index ce8c3cbdd9f..01670076ac0 100644 --- a/mod/lesson/tests/locallib_test.php +++ b/mod/lesson/tests/locallib_test.php @@ -25,6 +25,7 @@ namespace mod_lesson; use lesson; +use core\context\module as context_module; defined('MOODLE_INTERNAL') || die(); @@ -286,4 +287,104 @@ final class locallib_test extends \advanced_testcase { $lesson = new lesson($lesson); $this->assertEquals($expected, $lesson->get_last_attempt($attempts)); } + + /** + * Helper function to create attempts for a lesson. + * + * @param lesson $lesson The lesson object. + * @param int $userid The user ID for whom the attempts are created. + * @param int $count The number of attempts to create. + */ + private function create_user_submissions(lesson $lesson, int $userid, int $count): void { + /** @var \mod_lesson_generator $lessongenerator */ + $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); + + for ($i = 0; $i < $count; $i++) { + $lessongenerator->create_submission([ + 'lessonid' => $lesson->id, + 'userid' => $userid, + 'grade' => 100, + ]); + } + } + + /** + * Helper function to create lesson pages with multichoice questions. + * + * @param lesson $lesson The lesson object. + * @param int $count The number of multichoice questions to create. + */ + private function create_lesson_pages(lesson $lesson, int $count): void { + /** @var \mod_lesson_generator $lessongenerator */ + $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); + + for ($i = 0; $i < $count; $i++) { + $lessongenerator->create_page([ + 'title' => 'Multichoice question' . ($i + 1), + 'content' => 'Question content', + 'qtype' => 'multichoice', + 'lessonid' => $lesson->id, + ]); + $lessongenerator->create_answer(['page' => 'Multichoice question' . ($i + 1), 'answer' => 'A', 'score' => 1]); + $lessongenerator->create_answer(['page' => 'Multichoice question' . ($i + 1), 'answer' => 'B']); + } + $lessongenerator->finish_generate_answer(); + } + + /** + * Test the count_all_attempts, count_attempted_participants and count_all_participants methods. + * + * @covers \lesson::count_all_submissions + * @covers \lesson::count_submitted_participants + * @covers \lesson::count_all_participants + */ + public function test_count_attempts_and_participants(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $student4 = $this->getDataGenerator()->create_and_enrol($course, 'student'); + + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); + + $lessonrecord = $this->getDataGenerator()->create_module( + 'lesson', + ['course' => $course, 'retake' => 1] + ); + + $lesson = new lesson($lessonrecord); + $this->create_lesson_pages($lesson, 2); + $this->create_user_submissions($lesson, $student1->id, 1); + $this->create_user_submissions($lesson, $student2->id, 2); + $this->create_user_submissions($lesson, $student3->id, 2); + + $this->setUser($teacher->id); + + $this->assertEquals(5, $lesson->count_all_submissions()); + $this->assertEquals(3, $lesson->count_submitted_participants()); + $this->assertEquals(4, $lesson->count_all_participants()); + + // Check that the lesson is not counting teachers as participants. + $teacher2 = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); + $this->assertEquals(4, $lesson->count_all_participants()); + $student5 = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $this->assertEquals(5, $lesson->count_all_participants()); + + // Prohibit mod/lesson:view capability on student role to ensure it does not count students as participants/submissions. + $studentrole = $DB->get_record('role', ['shortname' => 'student']); + assign_capability( + 'mod/lesson:view', + CAP_PROHIBIT, + $studentrole->id, + context_module::instance($lesson->get_cm()->id) + ); + $this->assertEquals(0, $lesson->count_all_submissions()); + $this->assertEquals(0, $lesson->count_submitted_participants()); + $this->assertEquals(0, $lesson->count_all_participants()); + } }