MDL-86520 mod_scorm: Change the average attempts computation

* Make the average calculation uniform with other modules
(it is the students who attempted who are counted, not all
participants)
* Add a decimal point to the rounded value
This commit is contained in:
Laurent David
2025-09-19 16:39:16 +02:00
committed by Amaia Anabitarte
parent 38a6a4210d
commit e86dfcd285
4 changed files with 115 additions and 24 deletions
@@ -150,14 +150,15 @@ class overview extends \core_courseformat\activityoverviewbase {
return null;
}
$groups = array_map(fn($group) => $group->id, $this->get_groups_for_filtering());
$totalattempts = $this->manager->count_users_who_attempted($groups);
$userswhocanattempt = $this->manager->count_participants($groups);
$maxattempts = $this->manager->get_max_attempts();
if ($maxattempts === 0) {
$maxattemptstext = get_string('unlimited');
} else {
$maxattemptstext = (string) $maxattempts;
}
$totalattempts = $this->manager->count_all_attempts($groups);
$attemptedusers = $this->manager->count_users_who_attempted($groups);
$averageattempts = $totalattempts ? round($totalattempts / $attemptedusers, 1) : 0;
$content = new overviewdialog(
buttoncontent: $totalattempts,
@@ -171,10 +172,6 @@ class overview extends \core_courseformat\activityoverviewbase {
);
$content->add_item(get_string('allowedattemptsstudent', 'mod_scorm'), $maxattemptstext);
$averageattempts = 0;
if ($userswhocanattempt > 0) {
$averageattempts = (int) round($totalattempts / $userswhocanattempt);
}
$content->add_item(get_string('averageattemptperstudent', 'mod_scorm'), $averageattempts);
return new overviewitem(
+20
View File
@@ -160,6 +160,26 @@ class manager {
return $this->db->count_records_sql($query, $params);
}
/**
* Count the total number of attempts for the SCORM activity.
*
* @param array $groupids optional group id array, empty array means no group filtering.
* @return int the total number of attempts for the SCORM activity
*/
public function count_all_attempts(array $groupids = []): int {
$params = ['scormid' => $this->instance->id];
$joins = '';
$where = "WHERE a.scormid = :scormid";
if ($groupids) {
$sqljoin = groups_get_members_join($groupids, 'a.userid', $this->context);
$joins = $sqljoin->joins;
$where .= " AND $sqljoin->wheres";
$params += $sqljoin->params;
}
$sql = "SELECT COUNT(DISTINCT a.id) FROM {scorm_attempt} a $joins $where";
return $this->db->count_records_sql($sql, $params);
}
/**
* Get the max attempt setting.
*
@@ -261,7 +261,7 @@ final class overview_test extends \advanced_testcase {
'content' => '<strong>2</strong> of 4',
],
'totalattempts' => [
'value' => 2,
'value' => 3,
'items' => [
[
'label' => 'Grading method',
@@ -273,12 +273,10 @@ final class overview_test extends \advanced_testcase {
],
[
'label' => 'Average attempts per student',
'value' => '1',
'value' => '1.5',
],
],
],
],
];
yield 'teacher 1 - no groups without attempts' => [
@@ -320,7 +318,7 @@ final class overview_test extends \advanced_testcase {
'content' => '<strong>2</strong> of 4',
],
'totalattempts' => [
'value' => 2,
'value' => 3,
'items' => [
[
'label' => 'Grading method',
@@ -332,7 +330,7 @@ final class overview_test extends \advanced_testcase {
],
[
'label' => 'Average attempts per student',
'value' => '1',
'value' => '1.5',
],
],
],
@@ -348,7 +346,7 @@ final class overview_test extends \advanced_testcase {
'content' => '<strong>1</strong> of 2', // Teacher can also attempt, so s1 and t1 are counted.
],
'totalattempts' => [
'value' => 1, // Attempt from s1 only.
'value' => 2, // Attempt from s1 only.
'items' => [
[
'label' => 'Grading method',
@@ -360,7 +358,7 @@ final class overview_test extends \advanced_testcase {
],
[
'label' => 'Average attempts per student',
'value' => '1',
'value' => '2', // Only student 1 in this group attempted twice.
],
],
],
@@ -378,7 +376,7 @@ final class overview_test extends \advanced_testcase {
'content' => '<strong>2</strong> of 4',
],
'totalattempts' => [
'value' => 2,
'value' => 3,
'items' => [
[
'label' => 'Grading method',
@@ -390,7 +388,7 @@ final class overview_test extends \advanced_testcase {
],
[
'label' => 'Average attempts per student',
'value' => '1',
'value' => '1.5',
],
],
],
@@ -406,7 +404,7 @@ final class overview_test extends \advanced_testcase {
'content' => '<strong>2</strong> of 4',
],
'totalattempts' => [
'value' => 2,
'value' => 3,
'items' => [
[
'label' => 'Grading method',
@@ -418,7 +416,7 @@ final class overview_test extends \advanced_testcase {
],
[
'label' => 'Average attempts per student',
'value' => '1',
'value' => '1.5',
],
],
],
@@ -489,8 +487,7 @@ final class overview_test extends \advanced_testcase {
// attempt for testing purposes.
if ($createattempt && $createusers) {
$scormgenerator = $this->getDataGenerator()->get_plugin_generator('mod_scorm');
// Create attempts for the students.
// Two attempts for the first student, one for the second.
// Create an attempt for each student, and two attempts for the student s1.
foreach ($data as $username => $userinfo) {
$record = [
'userid' => $users[$username]->id,
@@ -500,8 +497,11 @@ final class overview_test extends \advanced_testcase {
$record['element'] = 'cmi.core.score.raw';
$record['value'] = $grades[$username];
}
// Create an attempt for each student.
// Create two attempts for the first student.
if ($userinfo['role'] === 'student') {
if ($username === 's1') {
$scormgenerator->create_attempt($record);
}
$scormgenerator->create_attempt($record);
}
}
+77 -3
View File
@@ -98,7 +98,7 @@ final class manager_test extends \advanced_testcase {
*/
public function test_can_view_reports(): void {
$this->resetAfterTest();
['users' => $users, 'course' => $course, 'instances' => $instances] = $this->setup_users_and_activity();
['users' => $users, 'instances' => $instances] = $this->setup_users_and_activity();
$manager = \mod_scorm\manager::create_from_instance($instances['withattempts']);
// Create an attempt for the current user.
$this->assertTrue($manager->can_view_reports($users['t1']));
@@ -132,8 +132,7 @@ final class manager_test extends \advanced_testcase {
*/
public function test_count_users_who_attempted(int $groupmode, string $activity, array $expected): void {
$this->resetAfterTest();
['users' => $users, 'course' => $course, 'instances' => $instances] =
$this->setup_users_and_activity(groupmode: $groupmode);
['users' => $users, 'instances' => $instances] = $this->setup_users_and_activity(groupmode: $groupmode);
$manager = \mod_scorm\manager::create_from_instance($instances[$activity]);
// Check the count of users who attempted.
@@ -193,6 +192,80 @@ final class manager_test extends \advanced_testcase {
];
}
/**
* Count the number of attempts for the SCORM activity.
*
* @param int $groupmode the group mode to use for the course.
* @param string $activity the activity name to test.
* @param array $currentgroups
* @param int $expectedcount
* @throws \moodle_exception
*/
#[\PHPUnit\Framework\Attributes\DataProvider('get_count_all_attempts_data')]
public function test_count_all_attempts(int $groupmode, string $activity, array $currentgroups, int $expectedcount): void {
$this->resetAfterTest();
['instances' => $instances, 'groups' => $groups] = $this->setup_users_and_activity(groupmode: $groupmode);
$manager = \mod_scorm\manager::create_from_instance($instances[$activity]);
$groupids = array_map(
fn($gname) => $groups[$gname]->id,
$currentgroups
);
$this->assertEquals(
$expectedcount,
$manager->count_all_attempts($groupids),
);
}
/**
* Data provider for participant count tests.
*
* @return \Generator
*/
public static function get_count_all_attempts_data(): \Generator {
yield 'No groups' => [
'groupmode' => NOGROUPS,
'activity' => 'withattempts',
'currentgroups' => [],
'expectedcount' => 3,
];
yield 'Separate groups, g1 selected' => [
'groupmode' => SEPARATEGROUPS,
'activity' => 'withattempts',
'currentgroups' => ['g1'],
'expectedcount' => 2,
];
yield 'Separate groups, g2 selected' => [
'groupmode' => SEPARATEGROUPS,
'activity' => 'withattempts',
'currentgroups' => ['g2'],
'expectedcount' => 1,
];
yield 'Separate groups, g1 and g2 selected' => [
'groupmode' => SEPARATEGROUPS,
'activity' => 'withattempts',
'currentgroups' => ['g1', 'g2'],
'expectedcount' => 3,
];
yield 'Separate groups, no group selected' => [
'groupmode' => SEPARATEGROUPS,
'activity' => 'withattempts',
'currentgroups' => [],
'expectedcount' => 3, // Seems counter-intuitive but with no group selected we count all attempts.
];
yield 'Visible groups' => [
'groupmode' => VISIBLEGROUPS,
'activity' => 'withattempts',
'currentgroups' => ['g1', 'g2'],
'expectedcount' => 3,
];
yield 'Visible groups g1 selected' => [
'groupmode' => VISIBLEGROUPS,
'activity' => 'withattempts',
'currentgroups' => ['g1'],
'expectedcount' => 2,
];
}
/**
* Test if the manager can view reports for a user.
*
@@ -392,6 +465,7 @@ final class manager_test extends \advanced_testcase {
'users' => $users,
'course' => $course,
'instances' => $instances,
'groups' => $groups,
];
}
}