Skip to content

Commit d581376

Browse files
committed
MDL-81714 grades: Add regrading progress indicator to grade reports
1 parent 4efbe68 commit d581376

File tree

18 files changed

+576
-36
lines changed

18 files changed

+576
-36
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
layout: docs
3+
title: "Task indicator"
4+
description: "A progress indicator for background tasks"
5+
date: 2024-08-21T00:00:00+01:00
6+
draft: false
7+
tags:
8+
- MDL-81714
9+
- 4.5
10+
---
11+
12+
{{< mustache template="core/task_indicator" >}}
13+
{{< /mustache >}}
14+
15+
## How to use
16+
17+
The task indicator component is used to display on any page the status and progress of an ad-hoc or scheduled task running in the
18+
background. If a task is running that will update the content on the page, it can be displayed in place of the content to inform
19+
the user that the current content is out-of-date, and will be updated when the task is complete.
20+
21+
## Source files
22+
23+
* lib/amd/src/task_indicator.js
24+
* lib/classes/output/task_indicator.php
25+
* lib/templates/task_indicator.mustache
26+
27+
## Usage
28+
29+
The task indicator can only be used to display the progress of a task if its class uses `core\task\stored_progress_task_trait`.
30+
31+
When the task is queued, you must call the `initialise_stored_progress()` method to store the progress record in a pending state,
32+
for the indicator to display while the task is queued.
33+
34+
{{< php >}}
35+
$task = new \core\task\mytask($id);
36+
$taskid = \core\task\manager::queue_adhoc_task($task, true);
37+
if ($taskid) {
38+
$task->set_id($taskid);
39+
$task->initialise_stored_progress();
40+
}
41+
{{< /php >}}
42+
43+
When the task runs, it must start, progress and complete its stored progress bar.
44+
See `core_course\task\regrade_final_grades` for a real-life example.
45+
46+
{{< php >}}
47+
48+
class mytask extends adhoc_task {
49+
use \core\task\stored_progress_task_trait;
50+
51+
public function execute(): void {
52+
$this->start_stored_progress();
53+
$storedprogress = $this->get_progress();
54+
foreach ($this->get_records() as $record) {
55+
$this->process_record($record);
56+
$storedprogress->progress();
57+
}
58+
$storedprogress->end_progress();
59+
}
60+
}
61+
62+
{{< /php >}}
63+
64+
Any page that wishes to display the status of the task must create an instance of the task object with the same parameters,
65+
and pass it to a `task_indicator`.
66+
67+
{{< php >}}
68+
69+
$task = new mytask($id);
70+
$taskindicator = new \core\output\task_indicator(
71+
task: $task,
72+
heading: 'Task processing',
73+
message: get_string('recalculatinggradesadhoc', 'grades'),
74+
icon: new \core\output\pix_icon('i/grades', ''),
75+
redirecturl: $PAGE->url,
76+
extraclasses: ['mytask'],
77+
);
78+
79+
{{< /php >}}
80+
81+
If there is currently a queued instance of the task, `$taskindicator->has_task_record()` will return true. We can use this to
82+
decide whether we display the indicator. See `grade/report/summary/index.php` for a real-life example.
83+
84+
{{< php >}}
85+
86+
if ($taskindicator->has_task_record()) {
87+
echo $OUTPUT->render($taskindicator);
88+
}
89+
90+
{{< /php >}}
91+
92+
If the optional `redirecturl` parameter is set when creating the indicator, the page will automatically reload or redirect to
93+
this URL when the progress bar completes.

grade/report/grader/index.php

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,6 @@
4949

5050
$PAGE->set_url(new moodle_url('/grade/report/grader/index.php', array('id'=>$courseid)));
5151
$PAGE->set_pagelayout('report');
52-
$PAGE->requires->js_call_amd('gradereport_grader/stickycolspan', 'init');
53-
$PAGE->requires->js_call_amd('gradereport_grader/user', 'init', [$baseurl->out(false)]);
54-
$PAGE->requires->js_call_amd('gradereport_grader/feedback_modal', 'init');
55-
$PAGE->requires->js_call_amd('core_grades/gradebooksetup_forms', 'init');
5652

5753
// basic access checks
5854
if (!$course = $DB->get_record('course', array('id' => $courseid))) {
@@ -125,9 +121,16 @@
125121
}
126122

127123
$reportname = get_string('pluginname', 'gradereport_grader');
128-
129-
// Do this check just before printing the grade header (and only do it once).
130-
grade_regrade_final_grades_if_required($course);
124+
$regradetask = \core_course\task\regrade_final_grades::create($courseid);
125+
$indicatorheader = get_string('recalculatinggrades', 'grades');
126+
$indicatormessage = get_string('recalculatinggradesadhoc', 'grades');
127+
$indictoricon = new pix_icon('i/grades', '');
128+
$taskindicator = new \core\output\task_indicator($regradetask, $indicatorheader, $indicatormessage, $indictoricon, $PAGE->url);
129+
if (!$taskindicator->has_task_record()) {
130+
// Do this check just before printing the grade header (and only do it once).
131+
grade_regrade_final_grades_if_required($course);
132+
$taskindicator = new \core\output\task_indicator($regradetask, $indicatorheader, $indicatormessage, $indictoricon, $PAGE->url);
133+
}
131134

132135
//Initialise the grader report object that produces the table
133136
//the class grade_report_grader_ajax was removed as part of MDL-21562
@@ -139,13 +142,6 @@
139142

140143
$report = new grade_report_grader($courseid, $gpr, $context, $page, $sortitemid, $sort);
141144

142-
// We call this a little later since we need some info from the grader report.
143-
$PAGE->requires->js_call_amd('gradereport_grader/collapse', 'init', [
144-
'userID' => $USER->id,
145-
'courseID' => $courseid,
146-
'defaultSort' => $report->get_default_sortable()
147-
]);
148-
149145
$numusers = $report->get_numusers(true, true);
150146

151147
$actionbar = new \gradereport_grader\output\action_bar($context, $report, $numusers);
@@ -166,6 +162,23 @@
166162
$warnings = $report->process_data($data);
167163
}
168164

165+
if ($taskindicator->has_task_record()) {
166+
echo $OUTPUT->render($taskindicator);
167+
echo $OUTPUT->footer();
168+
exit;
169+
}
170+
171+
// We call this a little later since we need some info from the grader report.
172+
$PAGE->requires->js_call_amd('gradereport_grader/collapse', 'init', [
173+
'userID' => $USER->id,
174+
'courseID' => $courseid,
175+
'defaultSort' => $report->get_default_sortable(),
176+
]);
177+
$PAGE->requires->js_call_amd('gradereport_grader/stickycolspan', 'init');
178+
$PAGE->requires->js_call_amd('gradereport_grader/user', 'init', [$baseurl->out(false)]);
179+
$PAGE->requires->js_call_amd('gradereport_grader/feedback_modal', 'init');
180+
$PAGE->requires->js_call_amd('core_grades/gradebooksetup_forms', 'init');
181+
169182
// Final grades MUST be loaded after the processing.
170183
$report->load_users();
171184
$report->load_final_grades();

grade/report/outcomes/index.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@
4444
}
4545

4646
// First make sure we have proper final grades.
47-
grade_regrade_final_grades_if_required($course);
47+
$regradetask = \core_course\task\regrade_final_grades::create($courseid);
48+
$indicatormessage = get_string('recalculatinggradesadhoc', 'grades');
49+
$taskindicator = new \core\output\task_indicator($regradetask, $indicatormessage);
50+
if (!$taskindicator->has_task_record()) {
51+
grade_regrade_final_grades_if_required($course);
52+
$taskindicator = new \core\output\task_indicator($regradetask, $indicatormessage);
53+
}
4854

4955
// Grab all outcomes used in course.
5056
$report_info = array();
@@ -96,6 +102,14 @@
96102
}
97103
}
98104

105+
print_grade_page_head($courseid, 'report', 'outcomes');
106+
107+
if ($taskindicator->has_task_record()) {
108+
echo $OUTPUT->render($taskindicator);
109+
echo $OUTPUT->footer();
110+
exit;
111+
}
112+
99113
$html = '<table class="generaltable boxaligncenter" width="90%" cellspacing="1" cellpadding="5" summary="Outcomes Report">' . "\n";
100114
$html .= '<tr><th class="header c0" scope="col">' . get_string('outcomeshortname', 'grades') . '</th>';
101115
$html .= '<th class="header c1" scope="col">' . get_string('courseavg', 'grades') . '</th>';
@@ -176,8 +190,6 @@
176190

177191
$html .= '</table>';
178192

179-
print_grade_page_head($courseid, 'report', 'outcomes');
180-
181193
echo $html;
182194

183195
$event = \gradereport_outcomes\event\grade_report_viewed::create(

grade/report/overview/index.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@
9090
$actionbar = new \core_grades\output\general_action_bar($context,
9191
new moodle_url('/grade/report/overview/index.php', ['id' => $courseid]), 'report', 'overview');
9292

93+
$taskindicator = new \core\output\task_indicator(
94+
\core_course\task\regrade_final_grades::create($courseid),
95+
get_string('recalculatinggrades', 'grades'),
96+
get_string('recalculatinggradesadhoc', 'grades'),
97+
new \core\output\pix_icon('i/grades', ''),
98+
$PAGE->url,
99+
);
100+
93101
if (has_capability('moodle/grade:viewall', $context) && $courseid != SITEID) {
94102
// Please note this would be extremely slow if we wanted to implement this properly for all teachers.
95103
$groupmode = groups_get_course_groupmode($course); // Groups are being used
@@ -115,6 +123,12 @@
115123

116124
groups_print_course_menu($course, $gpr->get_return_url('index.php?id='.$courseid, array('userid'=>0)));
117125

126+
if ($taskindicator->has_task_record()) {
127+
echo $OUTPUT->render($taskindicator);
128+
echo $OUTPUT->footer();
129+
exit;
130+
}
131+
118132
if ($user_selector) {
119133
$renderer = $PAGE->get_renderer('gradereport_overview');
120134
echo $renderer->graded_users_selector('overview', $course, $userid, $currentgroup, false);
@@ -131,6 +145,12 @@
131145
$report->user, $actionbar);
132146
groups_print_course_menu($course, $gpr->get_return_url('index.php?id='.$courseid, array('userid'=>0)));
133147

148+
if ($taskindicator->has_task_record()) {
149+
echo $OUTPUT->render($taskindicator);
150+
echo $OUTPUT->footer();
151+
exit;
152+
}
153+
134154
if ($user_selector) {
135155
$renderer = $PAGE->get_renderer('gradereport_overview');
136156
echo $renderer->graded_users_selector('overview', $course, $userid, $currentgroup, false);
@@ -168,6 +188,11 @@
168188
}
169189

170190
echo $OUTPUT->header();
191+
if ($taskindicator->has_task_record()) {
192+
echo $OUTPUT->render($taskindicator);
193+
echo $OUTPUT->footer();
194+
exit;
195+
}
171196
if ($report->fill_table(true, true)) {
172197
echo html_writer::tag('h3', get_string('coursesiamtaking', 'grades'));
173198
echo '<br />' . $report->print_table(true);
@@ -176,6 +201,11 @@
176201
print_grade_page_head($courseid, 'report', 'overview', get_string('pluginname', 'gradereport_overview')
177202
. ' - ' . fullname($report->user), false, false, true, null, null,
178203
$report->user, $actionbar);
204+
if ($taskindicator->has_task_record()) {
205+
echo $OUTPUT->render($taskindicator);
206+
echo $OUTPUT->footer();
207+
exit;
208+
}
179209
if ($report->fill_table()) {
180210
echo '<br />' . $report->print_table(true);
181211
}
@@ -189,6 +219,11 @@
189219
}
190220

191221
if (count($report->teachercourses)) {
222+
if ($taskindicator->has_task_record()) {
223+
echo $OUTPUT->render($taskindicator);
224+
echo $OUTPUT->footer();
225+
exit;
226+
}
192227
echo html_writer::tag('h3', get_string('coursesiamteaching', 'grades'));
193228
$report->print_teacher_table();
194229
}

grade/report/singleview/index.php

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,6 @@
133133
break;
134134
}
135135

136-
$report = new gradereport_singleview\report\singleview($courseid, $gpr, $context, $itemtype, $itemid);
137-
138136
$pageparams = [
139137
'id' => $courseid,
140138
'userid' => $userid,
@@ -150,6 +148,32 @@
150148

151149
$PAGE->set_url(new moodle_url('/grade/report/singleview/index.php', $pageparams));
152150

151+
// Make sure we have proper final grades.
152+
$taskindicator = new \core\output\task_indicator(
153+
\core_course\task\regrade_final_grades::create($courseid),
154+
get_string('recalculatinggrades', 'grades'),
155+
get_string('recalculatinggradesadhoc', 'grades'),
156+
new \core\output\pix_icon('i/grades', ''),
157+
$PAGE->url,
158+
);
159+
160+
if ($taskindicator->has_task_record()) {
161+
// We need to bail out early on as the report requires recalculations to be complete, so just display a basic header
162+
// with navigation, and the indicator.
163+
$actionbar = new \core_grades\output\general_action_bar(
164+
$context,
165+
new moodle_url('/grade/report/singleview/index.php', ['id' => $courseid]),
166+
'report',
167+
'singleview'
168+
);
169+
print_grade_page_head($course->id, 'report', 'singleview', actionbar: $actionbar);
170+
echo $OUTPUT->render($taskindicator);
171+
echo $OUTPUT->footer();
172+
exit;
173+
}
174+
175+
$report = new gradereport_singleview\report\singleview($courseid, $gpr, $context, $itemtype, $itemid);
176+
153177
// Build editing on/off button for themes that need it.
154178
$button = '';
155179
if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
@@ -208,9 +232,6 @@
208232
}
209233
}
210234

211-
// Make sure we have proper final grades.
212-
grade_regrade_final_grades_if_required($course);
213-
214235
// Save the screen state in a session variable as last viewed state.
215236
$SESSION->gradereport_singleview["itemtype-{$context->id}"] = $itemtype;
216237
if ($itemid) {

grade/report/summary/index.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,21 @@
4545
require_capability('gradereport/summary:view', $context);
4646
require_capability('moodle/grade:viewall', $context);
4747

48-
print_grade_page_head($courseid, 'report', 'summary', false,
49-
false, false, true, null, null,
50-
null, null);
48+
$taskindicator = new \core\output\task_indicator(
49+
\core_course\task\regrade_final_grades::create($courseid),
50+
get_string('recalculatinggrades', 'grades'),
51+
get_string('recalculatinggradesadhoc', 'grades'),
52+
new \core\output\pix_icon('i/grades', ''),
53+
$PAGE->url,
54+
);
5155

52-
$report = system_report_factory::create(summary::class, context_course::instance($courseid));
56+
print_grade_page_head($courseid, 'report', 'summary');
57+
58+
if ($taskindicator->has_task_record()) {
59+
echo $OUTPUT->render($taskindicator);
60+
} else {
61+
$report = system_report_factory::create(summary::class, context_course::instance($courseid));
62+
echo $report->output();
63+
}
5364

54-
echo $report->output();
5565
echo $OUTPUT->footer();

grade/report/user/classes/external/user.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ protected static function get_report_data(
161161
require_once($CFG->dirroot . '/grade/report/user/lib.php');
162162

163163
// Force regrade to update items marked as 'needupdate'.
164-
grade_regrade_final_grades($course->id);
164+
grade_regrade_final_grades($course->id, async: false);
165165

166166
$gpr = new grade_plugin_return(
167167
[

0 commit comments

Comments
 (0)