MDL-68831 quiz overrides: show a summary on the quiz info page

This commit is contained in:
Tim Hunt
2020-12-16 14:24:55 +00:00
parent 27bc291f71
commit 5b59ead94f
8 changed files with 242 additions and 19 deletions
+5
View File
@@ -616,6 +616,11 @@ $string['overrides'] = 'Overrides';
$string['overridesforquiz'] = 'Settings overrides: {$a}';
$string['overridesnoneforgroups'] = 'No group settings overrides have been created for this quiz.';
$string['overridesnoneforusers'] = 'No user settings overrides have been created for this quiz.';
$string['overridessummary'] = 'Settings overrides exist ({$a})';
$string['overridessummarythisgroup'] = 'Settings overrides exist ({$a}) for this group';
$string['overridessummaryyourgroups'] = 'Settings overrides exist ({$a}) for your groups';
$string['overridessummarygroup'] = 'Groups: {$a}';
$string['overridessummaryuser'] = 'Users: {$a}';
$string['overrideuser'] = 'Override user';
$string['overrideusereventname'] = '{$a->quiz} - Override';
$string['pageshort'] = 'P';
+3 -9
View File
@@ -1645,16 +1645,10 @@ function quiz_num_attempt_summary($quiz, $cm, $returnzero = false, $currentgroup
*/
function quiz_attempt_summary_link_to_reports($quiz, $cm, $context, $returnzero = false,
$currentgroup = 0) {
global $CFG;
$summary = quiz_num_attempt_summary($quiz, $cm, $returnzero, $currentgroup);
if (!$summary) {
return '';
}
global $PAGE;
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
$url = new moodle_url('/mod/quiz/report.php', array(
'id' => $cm->id, 'mode' => quiz_report_default_report($context)));
return html_writer::link($url, $summary);
return $PAGE->get_renderer('mod_quiz')->quiz_attempt_summary_link_to_reports(
$quiz, $cm, $context, $returnzero, $currentgroup);
}
/**
+66
View File
@@ -968,6 +968,72 @@ function quiz_update_all_final_grades($quiz) {
}
}
/**
* Return summary of the number of settings override that exist.
*
* To get a nice display of this, see the quiz_override_summary_links()
* quiz renderer method.
*
* @param stdClass $quiz the quiz settings. Only $quiz->id is used at the moment.
* @param stdClass|cm_info $cm the cm object. Only $cm->course, $cm->groupmode and
* $cm->groupingid fields are used at the moment.
* @param int $currentgroup if there is a concept of current group where this method is being called
* (e.g. a report) pass it in here. Default 0 which means no current group.
* @return array like 'group' => 3, 'user' => 12] where 3 is the number of group overrides,
* and 12 is the number of user ones.
*/
function quiz_override_summary(stdClass $quiz, stdClass $cm, int $currentgroup = 0): array {
global $DB;
if ($currentgroup) {
// Currently only interested in one group.
$groupcount = $DB->count_records('quiz_overrides', ['quiz' => $quiz->id, 'groupid' => $currentgroup]);
$usercount = $DB->count_records_sql("
SELECT COUNT(1)
FROM {quiz_overrides} o
JOIN {groups_members} gm ON o.userid = gm.userid
WHERE o.quiz = ?
AND gm.groupid = ?
", [$quiz->id, $currentgroup]);
return ['group' => $groupcount, 'user' => $usercount, 'mode' => 'onegroup'];
}
$quizgroupmode = groups_get_activity_groupmode($cm);
$accessallgroups = ($quizgroupmode == NOGROUPS) ||
has_capability('moodle/site:accessallgroups', context_module::instance($cm->id));
if ($accessallgroups) {
// User can see all groups.
$groupcount = $DB->count_records_select('quiz_overrides',
'quiz = ? AND groupid IS NOT NULL', [$quiz->id]);
$usercount = $DB->count_records_select('quiz_overrides',
'quiz = ? AND userid IS NOT NULL', [$quiz->id]);
return ['group' => $groupcount, 'user' => $usercount, 'mode' => 'allgroups'];
} else {
// User can only see groups they are in.
$groups = groups_get_activity_allowed_groups($cm);
if (!$groups) {
return ['group' => 0, 'user' => 0, 'mode' => 'somegroups'];
}
list($groupidtest, $params) = $DB->get_in_or_equal(array_keys($groups));
$params[] = $quiz->id;
$groupcount = $DB->count_records_select('quiz_overrides',
"groupid $groupidtest AND quiz = ?", $params);
$usercount = $DB->count_records_sql("
SELECT COUNT(1)
FROM {quiz_overrides} o
JOIN {groups_members} gm ON o.userid = gm.userid
WHERE gm.groupid $groupidtest
AND o.quiz = ?
", $params);
return ['group' => $groupcount, 'user' => $usercount, 'mode' => 'somegroups'];
}
}
/**
* Efficiently update check state time on all open attempts
*
+1
View File
@@ -102,6 +102,7 @@ if ($groupmode) {
$overrides = $DB->get_records_sql($sql, $params);
}
} else {
// User overrides.
$colname = get_string('user');
list($sort, $params) = users_order_by_sql('u');
$params['quizid'] = $quiz->id;
+56 -7
View File
@@ -927,7 +927,7 @@ class mod_quiz_renderer extends plugin_renderer_base {
*
* @param object $quiz the quiz settings.
* @param object $cm the course_module object.
* @param object $context the quiz context.
* @param context $context the quiz context.
* @param array $messages any access messages that should be described.
* @return string HTML to output.
*/
@@ -953,6 +953,13 @@ class mod_quiz_renderer extends plugin_renderer_base {
array('class' => 'quizattemptcounts'));
}
}
if (has_any_capability(['mod/quiz:manageoverrides', 'mod/quiz:viewoverrides'], $context)) {
if ($overrideinfo = $this->quiz_override_summary_links($quiz, $cm)) {
$output .= html_writer::tag('div', $overrideinfo, ['class' => 'quizattemptcounts']);
}
}
return $output;
}
@@ -1216,15 +1223,14 @@ class mod_quiz_renderer extends plugin_renderer_base {
* Returns the same as {@link quiz_num_attempt_summary()} but wrapped in a link
* to the quiz reports.
*
* @param object $quiz the quiz object. Only $quiz->id is used at the moment.
* @param object $cm the cm object. Only $cm->course, $cm->groupmode and $cm->groupingid
* @param stdClass $quiz the quiz object. Only $quiz->id is used at the moment.
* @param stdClass $cm the cm object. Only $cm->course, $cm->groupmode and $cm->groupingid
* fields are used at the moment.
* @param object $context the quiz context.
* @param context $context the quiz context.
* @param bool $returnzero if false (default), when no attempts have been made '' is returned
* instead of 'Attempts: 0'.
* instead of 'Attempts: 0'.
* @param int $currentgroup if there is a concept of current group where this method is being
* called
* (e.g. a report) pass it in here. Default 0 which means no current group.
* called (e.g. a report) pass it in here. Default 0 which means no current group.
* @return string HTML fragment for the link.
*/
public function quiz_attempt_summary_link_to_reports($quiz, $cm, $context,
@@ -1241,6 +1247,49 @@ class mod_quiz_renderer extends plugin_renderer_base {
return html_writer::link($url, $summary);
}
/**
* Render a summary of the number of group and user overrides, with corresponding links.
*
* @param stdClass $quiz the quiz settings.
* @param stdClass|cm_info $cm the cm object.
* @param int $currentgroup currently selected group, if there is one.
* @return string HTML fragment for the link.
*/
public function quiz_override_summary_links(stdClass $quiz, stdClass $cm, $currentgroup = 0): string {
$baseurl = new moodle_url('/mod/quiz/overrides.php', ['cmid' => $cm->id]);
$counts = quiz_override_summary($quiz, $cm, $currentgroup);
$links = [];
if ($counts['group']) {
$links[] = html_writer::link(new moodle_url($baseurl, ['mode' => 'group']),
get_string('overridessummarygroup', 'quiz', $counts['group']));
}
if ($counts['user']) {
$links[] = html_writer::link(new moodle_url($baseurl, ['mode' => 'user']),
get_string('overridessummaryuser', 'quiz', $counts['user']));
}
if (!$links) {
return '';
}
$links = implode(', ', $links);
switch ($counts['mode']) {
case 'onegroup':
return get_string('overridessummarythisgroup', 'quiz', $links);
case 'somegroups':
return get_string('overridessummaryyourgroups', 'quiz', $links);
case 'allgroups':
return get_string('overridessummary', 'quiz', $links);
default:
throw new coding_exception('Unexpected mode ' . $counts['mode']);
}
}
/**
* Outputs a chart.
*
@@ -82,8 +82,10 @@ Feature: Quiz group override
| quiz | group | attempts |
| Test quiz | G1 | 2 |
| Test quiz | G2 | 2 |
When I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "teacher1"
Then "Group 1" "table_row" should exist
When I am on the "Test quiz" "mod_quiz > View" page logged in as "teacher1"
Then I should see "Settings overrides exist (Groups: 2)"
And I follow "Groups: 2"
And "Group 1" "table_row" should exist
And "Group 2" "table_row" should exist
Scenario: A teacher without accessallgroups permission should only see the group overrides within his/her groups, when the activity's group mode is "separate groups"
@@ -94,7 +96,9 @@ Feature: Quiz group override
| quiz | group | attempts |
| Test quiz | G1 | 2 |
| Test quiz | G2 | 2 |
When I am on the "Test quiz" "mod_quiz > Group overrides" page logged in as "teacher1"
When I am on the "Test quiz" "mod_quiz > View" page logged in as "teacher1"
Then I should see "Settings overrides exist (Groups: 1) for your groups"
And I follow "Groups: 1"
Then "Group 1" "table_row" should exist
And "Group 2" "table_row" should not exist
@@ -121,3 +121,5 @@ Feature: Quiz user override
And "Edit" "link" should not exist in the "Student One" "table_row"
And "Copy" "link" should not exist in the "Student One" "table_row"
And "Delete" "link" should not exist in the "Student One" "table_row"
And I am on the "Test quiz" "mod_quiz > View" page
And I should see "Settings overrides exist (Users: 2)"
+102
View File
@@ -839,4 +839,106 @@ class mod_quiz_locallib_testcase extends advanced_testcase {
$this->assertEquals($formattedexptected, $actual);
}
public function test_quiz_override_summary() {
global $DB, $PAGE;
$this->resetAfterTest();
$generator = $this->getDataGenerator();
/** @var mod_quiz_generator $quizgenerator */
$quizgenerator = $generator->get_plugin_generator('mod_quiz');
/** @var mod_quiz_renderer $renderer */
$renderer = $PAGE->get_renderer('mod_quiz');
// Course with quiz and a group - plus some others, to verify they don't get counted.
$course = $generator->create_course();
$quiz = $quizgenerator->create_instance(['course' => $course->id, 'groupmode' => SEPARATEGROUPS]);
$cm = get_coursemodule_from_id('quiz', $quiz->cmid, $course->id);
$group = $generator->create_group(['courseid' => $course->id]);
$othergroup = $generator->create_group(['courseid' => $course->id]);
$otherquiz = $quizgenerator->create_instance(['course' => $course->id]);
// Initial test (as admin) with no data.
$this->setAdminUser();
$this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'allgroups'],
quiz_override_summary($quiz, $cm));
$this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'onegroup'],
quiz_override_summary($quiz, $cm, $group->id));
// Editing teacher.
$teacher = $generator->create_user();
$generator->enrol_user($teacher->id, $course->id, 'editingteacher');
// Non-editing teacher.
$tutor = $generator->create_user();
$generator->enrol_user($tutor->id, $course->id, 'teacher');
$generator->create_group_member(['userid' => $tutor->id, 'groupid' => $group->id]);
// Three students.
$student1 = $generator->create_user();
$generator->enrol_user($student1->id, $course->id, 'student');
$generator->create_group_member(['userid' => $student1->id, 'groupid' => $group->id]);
$student2 = $generator->create_user();
$generator->enrol_user($student2->id, $course->id, 'student');
$generator->create_group_member(['userid' => $student2->id, 'groupid' => $othergroup->id]);
$student3 = $generator->create_user();
$generator->enrol_user($student3->id, $course->id, 'student');
// Initial test now users exist, but before overrides.
// Test as teacher.
$this->setUser($teacher);
$this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'allgroups'],
quiz_override_summary($quiz, $cm));
$this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'onegroup'],
quiz_override_summary($quiz, $cm, $group->id));
// Test as tutor.
$this->setUser($tutor);
$this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'somegroups'],
quiz_override_summary($quiz, $cm));
$this->assertEquals(['group' => 0, 'user' => 0, 'mode' => 'onegroup'],
quiz_override_summary($quiz, $cm, $group->id));
$this->assertEquals('', $renderer->quiz_override_summary_links($quiz, $cm));
// Quiz setting overrides for students 1 and 3.
$quizgenerator->create_override(['quiz' => $quiz->id, 'userid' => $student1->id, 'attempts' => 2]);
$quizgenerator->create_override(['quiz' => $quiz->id, 'userid' => $student3->id, 'attempts' => 2]);
$quizgenerator->create_override(['quiz' => $quiz->id, 'groupid' => $group->id, 'attempts' => 3]);
$quizgenerator->create_override(['quiz' => $quiz->id, 'groupid' => $othergroup->id, 'attempts' => 3]);
$quizgenerator->create_override(['quiz' => $otherquiz->id, 'userid' => $student2->id, 'attempts' => 2]);
// Test as teacher.
$this->setUser($teacher);
$this->assertEquals(['group' => 2, 'user' => 2, 'mode' => 'allgroups'],
quiz_override_summary($quiz, $cm));
$this->assertEquals('Settings overrides exist (Groups: 2, Users: 2)',
// Links checked by Behat, so strip them for these tests.
html_to_text($renderer->quiz_override_summary_links($quiz, $cm), 0, false));
$this->assertEquals(['group' => 1, 'user' => 1, 'mode' => 'onegroup'],
quiz_override_summary($quiz, $cm, $group->id));
$this->assertEquals('Settings overrides exist (Groups: 1, Users: 1) for this group',
html_to_text($renderer->quiz_override_summary_links($quiz, $cm, $group->id), 0, false));
// Test as tutor.
$this->setUser($tutor);
$this->assertEquals(['group' => 1, 'user' => 1, 'mode' => 'somegroups'],
quiz_override_summary($quiz, $cm));
$this->assertEquals('Settings overrides exist (Groups: 1, Users: 1) for your groups',
html_to_text($renderer->quiz_override_summary_links($quiz, $cm), 0, false));
$this->assertEquals(['group' => 1, 'user' => 1, 'mode' => 'onegroup'],
quiz_override_summary($quiz, $cm, $group->id));
$this->assertEquals('Settings overrides exist (Groups: 1, Users: 1) for this group',
html_to_text($renderer->quiz_override_summary_links($quiz, $cm, $group->id), 0, false));
// Now set the quiz to be group mode: no groups, and re-test as tutor.
// In this case, the tutor should see all groups.
$DB->set_field('course_modules', 'groupmode', NOGROUPS, ['id' => $cm->id]);
$cm = get_coursemodule_from_id('quiz', $quiz->cmid, $course->id);
$this->assertEquals(['group' => 2, 'user' => 2, 'mode' => 'allgroups'],
quiz_override_summary($quiz, $cm));
$this->assertEquals('Settings overrides exist (Groups: 2, Users: 2)',
html_to_text($renderer->quiz_override_summary_links($quiz, $cm), 0, false));
}
}