MDL-81714 grades: Display "Run now" button for admins in task indicator

This commit is contained in:
Mark Johnson
2025-03-14 16:01:05 +00:00
parent 10de329397
commit 69afa8a3ac
7 changed files with 114 additions and 47 deletions
@@ -6,7 +6,7 @@ date: 2024-08-21T00:00:00+01:00
draft: false
tags:
- MDL-81714
- 4.5
- 5.0
---
{{< mustache template="core/task_indicator" >}}
@@ -89,5 +89,34 @@ if ($taskindicator->has_task_record()) {
{{< /php >}}
When the task begins running and the progress is updated, the progress bar will automatically be displayed.
If the optional `redirecturl` parameter is set when creating the indicator, the page will automatically reload or redirect to
this URL when the progress bar completes.
While the task is still queued, admins will see a "Run now" button below the indicator. This is designed for convenience if
a user is blocked on a job and needs the task run immediately. It will run the specific instance of the task tracked by the
indicator.
{{< mustache template="core/task_indicator" >}}
{
"heading": "Regrade in progress",
"icon": {
"attributes": [
{"name": "src", "value": "/pix/i/timer.svg"},
{"name": "alt", "value": ""}
]
},
"message": "Grades are being recalculated due to recent changes.",
"progress": {
"id": "progressbar_test",
"message": "Task pending",
"idnumber": "progressbar_test",
"class": "stored-progress-bar stored-progress-notstarted",
"width": "500",
"value": "0"
},
"runurl": "http://example.com/runtask.php?id=1",
"runlabel": "Run now"
}
{{< /mustache >}}
+19 -24
View File
@@ -54,15 +54,14 @@ Feature: Asynchronous regrade on a large course
And I set the field "Maximum grade" to "50"
And I press "Save and return to course"
And I log out
And I change the viewport size to "medium"
Scenario Outline: Task indicator displays on all grade reports when a calculation is pending
Given I am on the "Test course 2" "<report>" page logged in as "<user>"
Then I should not see "Grades are being recalculated due to recent changes."
Then I should not see "The report will update automatically. You don't need to do anything."
And <element> should exist
When I am on the "Test course 1" "<report>" page logged in as "<user>"
Then I should see "Grades are being recalculated due to recent changes."
And I should see "Task pending"
And I should see "0.0%"
Then I should see "The report will update automatically. You don't need to do anything."
And <element> should not exist
Examples:
@@ -77,10 +76,10 @@ Feature: Asynchronous regrade on a large course
Scenario Outline: Gradebook settings can be accessed when a regrade is pending
Given I am on the "Test course 2" "<page>" page logged in as "teacher1"
Then I should see "<text>"
And I should not see "Grades are being recalculated due to recent changes."
And I should not see "The report will update automatically. You don't need to do anything."
Given I am on the "Test course 1" "<page>" page logged in as "teacher1"
Then I should see "<text>"
And I should not see "Grades are being recalculated due to recent changes."
And I should not see "The report will update automatically. You don't need to do anything."
Examples:
| page | text |
@@ -92,36 +91,32 @@ Feature: Asynchronous regrade on a large course
When I follow "Grades" in the user menu
And I follow "Test course 2"
Then "table.user-grade" "css_element" should exist
Then I should not see "Grades are being recalculated due to recent changes."
Then I should not see "The report will update automatically. You don't need to do anything."
When I follow "Grades" in the user menu
And I follow "Test course 1"
Then "table.user-grade" "css_element" should not exist
Then I should see "Grades are being recalculated due to recent changes."
Then I should see "The report will update automatically. You don't need to do anything."
Scenario: Task indicator progresses and redirects when the task is run.
When I am on the "Test course 1" "grades > Grader report > View" page logged in as teacher1
And I should see "Grades are being recalculated due to recent changes."
And I should see "Task pending"
And I should see "0.0%"
And I should see "The report will update automatically. You don't need to do anything."
And I should not see "Run now"
And I should not see "0.0%"
And "user-grades" "table" should not exist
When I run all adhoc tasks
And I run all adhoc tasks
# Progress bar should update.
# Manual wait is a bit of a fudge, but we need the progress bar to poll for an update.
And I wait "1" seconds
Then I should not see "Task pending"
And I should see "Recalculating grades"
And I wait until "Recalculating grades" "text" exists
And I should see "100%"
When I wait "2" seconds
# The page should reload after a short delay.
Then I should not see "Grades are being recalculated due to recent changes."
Then I wait until "Recalculating grades" "text" does not exist
And I set the field "Search users" to "Student 1"
And "user-grades" "table" should exist
And "40.00" "text" should exist in the "student1@example.com" "table_row"
Scenario: Admin should see a "Run now" button in the task indicator
When I am on the "Test course 1" "grades > Grader report > View" page logged in as admin
And I should see "Grades are being recalculated due to recent changes."
And I should see "Task pending"
And I should see "The report will update automatically. You don't need to do anything."
And I should not see "0.0%"
And I should see "Run now"
Scenario: Making changes on course with less than 100 grades performs the regrade synchronously, no indicator is shown.
@@ -131,17 +126,17 @@ Feature: Asynchronous regrade on a large course
And I set the field "Maximum grade" to "50"
And I press "Save and return to course"
When I am on the "Test course 2" "grades > Grader report > View" page
Then I should not see "Grades are being recalculated due to recent changes."
Then I should not see "The report will update automatically. You don't need to do anything."
And "user-grades" "table" should exist
Scenario: Editing weights triggers a regrade, but further edits are possible
Given I run all adhoc tasks
And I am on the "Test course 1" "grades > Grader report > View" page logged in as "teacher1"
And I should not see "Grades are being recalculated due to recent changes."
And I should not see "The report will update automatically. You don't need to do anything."
And I am on the "Test course 1" "grades > Gradebook setup" page
When I set the field "Override weight of Test assignment 1" to "1"
And I press "Save changes"
And I am on the "Test course 1" "grades > Grader report > View" page
And I should see "Grades are being recalculated due to recent changes."
And I should see "The report will update automatically. You don't need to do anything."
And I am on the "Test course 1" "grades > Gradebook setup" page
And I should not see "Grades are being recalculated due to recent changes."
And I should not see "The report will update automatically. You don't need to do anything."
+1 -1
View File
@@ -9,6 +9,6 @@ define("core/task_indicator",["exports"],(function(_exports){Object.defineProper
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class{static init(id,redirectUrl){document.getElementById(id).addEventListener("update",(event=>{100===event.detail.percent&&window.setTimeout((()=>window.location.assign(redirectUrl)),2e3)}))}},_exports.default}));
class{static init(id,redirectUrl){const bar=document.getElementById(id);bar.addEventListener("update",(event=>{var _event$detail;const percent=null==event||null===(_event$detail=event.detail)||void 0===_event$detail?void 0:_event$detail.percent;if(percent>0){bar.classList.remove("stored-progress-notstarted");const runlink=document.querySelector(".runlink[data-idnumber=".concat(id,"]"));runlink&&runlink.remove()}""!==redirectUrl&&100===percent&&window.setTimeout((()=>window.location.assign(redirectUrl)),2e3)}))}},_exports.default}));
//# sourceMappingURL=task_indicator.min.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"task_indicator.min.js","sources":["../src/task_indicator.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Task indicator\n *\n * Watches the progress bar inside the task indicator for updates, and redirects when the progress is complete.\n *\n * @module core/task_indicator\n * @copyright 2024 Catalyst IT Europe Ltd\n * @author Mark Johnson <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n /**\n * Watch the progress bar for updates.\n *\n * When the progress bar is updated to 100%, wait a couple of seconds so the user gets to see it if they are watching,\n * then redirect to the specified URL.\n *\n * @param {String} id\n * @param {String} redirectUrl\n */\n static init(id, redirectUrl) {\n document.getElementById(id).addEventListener('update', (event) => {\n if (event.detail.percent === 100) {\n window.setTimeout(() => window.location.assign(redirectUrl), 2000);\n }\n });\n }\n}\n"],"names":["id","redirectUrl","document","getElementById","addEventListener","event","detail","percent","window","setTimeout","location","assign"],"mappings":";;;;;;;;;;;kBAmCgBA,GAAIC,aACZC,SAASC,eAAeH,IAAII,iBAAiB,UAAWC,QACvB,MAAzBA,MAAMC,OAAOC,SACbC,OAAOC,YAAW,IAAMD,OAAOE,SAASC,OAAOV,cAAc"}
{"version":3,"file":"task_indicator.min.js","sources":["../src/task_indicator.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Task indicator\n *\n * Watches the progress bar inside the task indicator for updates, and redirects when the progress is complete.\n *\n * @module core/task_indicator\n * @copyright 2024 Catalyst IT Europe Ltd\n * @author Mark Johnson <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n /**\n * Watch the progress bar for updates.\n *\n * When the progress bar is updated to 100%, wait a couple of seconds so the user gets to see it if they are watching,\n * then redirect to the specified URL.\n *\n * @param {String} id The ID of the progress bar element.\n * @param {String} redirectUrl Optional URL to redirect to once the task is complete.\n */\n static init(id, redirectUrl) {\n const bar = document.getElementById(id);\n bar.addEventListener('update', (event) => {\n const percent = event?.detail?.percent;\n if (percent > 0) {\n // Once progress starts, display the progress bar and remove the run link.\n bar.classList.remove('stored-progress-notstarted');\n const runlink = document.querySelector(`.runlink[data-idnumber=${id}]`);\n if (runlink) {\n runlink.remove();\n }\n }\n // Once the progress bar completes, redirect the page.\n if (redirectUrl !== '' && percent === 100) {\n window.setTimeout(() => window.location.assign(redirectUrl), 2000);\n }\n });\n }\n}\n"],"names":["id","redirectUrl","bar","document","getElementById","addEventListener","event","percent","detail","_event$detail","classList","remove","runlink","querySelector","window","setTimeout","location","assign"],"mappings":";;;;;;;;;;;kBAmCgBA,GAAIC,mBACNC,IAAMC,SAASC,eAAeJ,IACpCE,IAAIG,iBAAiB,UAAWC,gCACtBC,QAAUD,MAAAA,6BAAAA,MAAOE,uCAAPC,cAAeF,WAC3BA,QAAU,EAAG,CAEbL,IAAIQ,UAAUC,OAAO,oCACfC,QAAUT,SAASU,+CAAwCb,SAC7DY,SACAA,QAAQD,SAII,KAAhBV,aAAkC,MAAZM,SACtBO,OAAOC,YAAW,IAAMD,OAAOE,SAASC,OAAOhB,cAAc"}
+15 -4
View File
@@ -30,12 +30,23 @@ export default class {
* When the progress bar is updated to 100%, wait a couple of seconds so the user gets to see it if they are watching,
* then redirect to the specified URL.
*
* @param {String} id
* @param {String} redirectUrl
* @param {String} id The ID of the progress bar element.
* @param {String} redirectUrl Optional URL to redirect to once the task is complete.
*/
static init(id, redirectUrl) {
document.getElementById(id).addEventListener('update', (event) => {
if (event.detail.percent === 100) {
const bar = document.getElementById(id);
bar.addEventListener('update', (event) => {
const percent = event?.detail?.percent;
if (percent > 0) {
// Once progress starts, display the progress bar and remove the run link.
bar.classList.remove('stored-progress-notstarted');
const runlink = document.querySelector(`.runlink[data-idnumber=${id}]`);
if (runlink) {
runlink.remove();
}
}
// Once the progress bar completes, redirect the page.
if (redirectUrl !== '' && percent === 100) {
window.setTimeout(() => window.location.assign(redirectUrl), 2000);
}
});
+26 -1
View File
@@ -16,14 +16,21 @@
namespace core\output;
use core\plugin_manager;
use core\task\adhoc_task;
use core\task\stored_progress_task_trait;
use core\url;
use core\context\system;
use stdClass;
/**
* Indicator for displaying status and progress of a background task
*
* This will display a section containing an icon, heading and message describing the background task being performed,
* as well as a progress bar that is updated as the task progresses. Optionally, it will redirect to a given URL (or reload
* the current one) when the task completes. If the task is still waiting in the queue, an admin viewing the indicator
* will also see a "Run now" button.
*
* @package core
* @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}
* @author Mark Johnson <[email protected]>
@@ -36,6 +43,12 @@ class task_indicator implements renderable, templatable {
/** @var ?stored_progress_bar $progressbar */
protected ?stored_progress_bar $progressbar;
/** @var ?url $runurl The URL to manually run the task. */
protected ?url $runurl = null;
/** @var string $runlabel Label for the link to run the task. */
protected string $runlabel = '';
/**
* Find the task record, and get the progress bar object.
*
@@ -80,6 +93,16 @@ class task_indicator implements renderable, templatable {
$this->task->set_id($this->taskrecord->id);
$idnumber = stored_progress_bar::convert_to_idnumber($this->task::class, $this->task->get_id());
$this->progressbar = stored_progress_bar::get_by_idnumber($idnumber);
// As long as the tool_task plugin hasn't been removed,
// allow admins to trigger the task manually if it's not running yet.
if (
array_key_exists('task', plugin_manager::instance()->get_present_plugins('tool'))
&& is_null($this->taskrecord->timestarted)
&& has_capability('moodle/site:config', system::instance())
) {
$this->runurl = new url('/admin/tool/task/run_adhoctasks.php', ['id' => $this->taskrecord->id]);
$this->runlabel = get_string('runnow', 'tool_task');
}
}
}
@@ -103,8 +126,10 @@ class task_indicator implements renderable, templatable {
$export['message'] = $this->message;
$export['progress'] = $this->progressbar->export_for_template($output);
$export['icon'] = $this->icon ? $this->icon->export_for_template($output) : '';
$export['redirecturl'] = $this->redirecturl->out();
$export['redirecturl'] = $this->redirecturl?->out();
$export['extraclasses'] = implode(' ', $this->extraclasses);
$export['runurl'] = $this->runurl?->out();
$export['runlabel'] = $this->runlabel;
$this->progressbar->init_js();
}
return $export;
+22 -15
View File
@@ -33,29 +33,36 @@
"id": "progressbar_test",
"message": "Recalculating grades",
"idnumber": "progressbar_test",
"class": "stored-progress-bar",
"width": "500",
"value": "50"
}
}
}}
<div class="task-indicator {{extraclasses}}">
{{#icon}}
<div class="text-center">
{{>core/pix_icon}}
</div>
{{/icon}}
<h3 class="text-center">{{heading}}</h3>
<p class="text-center">{{message}}</p>
<div class="text-center">
{{#icon}}
<div>
{{>core/pix_icon}}
</div>
{{/icon}}
<h2>{{heading}}</h2>
<p>{{message}}</p>
</div>
{{#runurl}}
<p class="text-center">
<a class="runlink btn btn-primary" href="{{runurl}}" data-idnumber="{{progress.idnumber}}">{{runlabel}}</a>
</p>
{{/runurl}}
{{#progress}}
{{>core/progress_bar}}
{{/progress}}
</div>
{{#redirecturl}}
{{#js}}
require(['core/task_indicator'], function(TaskIndicator) {
TaskIndicator.init('{{progress.idnumber}}', '{{redirecturl}}');
});
{{/js}}
{{/redirecturl}}
{{#js}}
require(['core/task_indicator'], function(TaskIndicator) {
TaskIndicator.init('{{progress.idnumber}}', '{{redirecturl}}');
});
{{/js}}