MDL-70583 cli: Allow progress bars to be rendered in cli scripts

This commit is contained in:
Brendan Heywood 2021-01-09 14:32:19 +11:00
parent 9dabd071fe
commit ded82b7f5b
2 changed files with 130 additions and 13 deletions

View File

@ -4916,6 +4916,14 @@ class progress_bar implements renderable, templatable {
}
}
/**
* Getter for ID
* @return string id
*/
public function get_id() : string {
return $this->html_id;
}
/**
* Create a new progress bar, this function will output html.
*
@ -4925,9 +4933,6 @@ class progress_bar implements renderable, templatable {
global $OUTPUT;
$this->time_start = microtime(true);
if (CLI_SCRIPT) {
return; // Temporary solution for cli scripts.
}
flush();
echo $OUTPUT->render($this);
@ -4943,15 +4948,13 @@ class progress_bar implements renderable, templatable {
* @throws coding_exception
*/
private function _update($percent, $msg) {
global $OUTPUT;
if (empty($this->time_start)) {
throw new coding_exception('You must call create() (or use the $autostart ' .
'argument to the constructor) before you try updating the progress bar.');
}
if (CLI_SCRIPT) {
return; // Temporary solution for cli scripts.
}
$estimate = $this->estimate($percent);
if ($estimate === null) {
@ -4965,16 +4968,15 @@ class progress_bar implements renderable, templatable {
return;
}
$estimatemsg = null;
if (is_numeric($estimate)) {
$estimatemsg = get_string('secondsleft', 'moodle', round($estimate, 2));
$estimatemsg = '';
if ($estimate != 0 && is_numeric($estimate)) {
$estimatemsg = format_time(round($estimate));
}
$this->percent = round($percent, 2);
$this->percent = $percent;
$this->lastupdate = microtime(true);
echo html_writer::script(js_writer::function_call('updateProgressBar',
array($this->html_id, $this->percent, $msg, $estimatemsg)));
echo $OUTPUT->render_progress_bar_update($this->html_id, sprintf("%.1f", $this->percent), $msg, $estimatemsg);
flush();
}

View File

@ -4776,6 +4776,22 @@ EOD;
return $this->render_from_template('core/progress_bar', $data);
}
/**
* Renders an update to a progress bar.
*
* Note: This does not cleanly map to a renderable class and should
* never be used directly.
*
* @param string $id
* @param float $percent
* @param string $msg Message
* @param string $estimate time remaining message
* @return string ascii fragment
*/
public function render_progress_bar_update(string $id, float $percent, string $msg, string $estimate) : string {
return html_writer::script(js_writer::function_call('updateProgressBar', [$id, $percent, $msg, $estimate]));
}
/**
* Renders element for a toggle-all checkbox.
*
@ -4800,6 +4816,12 @@ EOD;
*/
class core_renderer_cli extends core_renderer {
/**
* @var array $progressmaximums stores the largest percentage for a progress bar.
* @return string ascii fragment
*/
private $progressmaximums = [];
/**
* Returns the page header.
*
@ -4844,6 +4866,99 @@ class core_renderer_cli extends core_renderer {
return $this->render_check_result($result);
}
/**
* Renders a progress bar.
*
* Do not use $OUTPUT->render($bar), instead use progress_bar::create().
*
* @param progress_bar $bar The bar.
* @return string ascii fragment
*/
public function render_progress_bar(progress_bar $bar) {
global $CFG;
$size = 55; // The width of the progress bar in chars.
$ascii = "\n";
if (stream_isatty(STDOUT)) {
require_once($CFG->libdir.'/clilib.php');
$ascii .= "[" . str_repeat(' ', $size) . "] 0% \n";
return cli_ansi_format($ascii);
}
$this->progressmaximums[$bar->get_id()] = 0;
$ascii .= '[';
return $ascii;
}
/**
* Renders an update to a progress bar.
*
* Note: This does not cleanly map to a renderable class and should
* never be used directly.
*
* @param string $id
* @param float $percent
* @param string $msg Message
* @param string $estimate time remaining message
* @return string ascii fragment
*/
public function render_progress_bar_update(string $id, float $percent, string $msg, string $estimate) : string {
$size = 55; // The width of the progress bar in chars.
$ascii = '';
// If we are rendering to a terminal then we can safely use ansii codes
// to move the cursor and redraw the complete progress bar each time
// it is updated.
if (stream_isatty(STDOUT)) {
$colour = $percent == 100 ? 'green' : 'blue';
$done = $percent * $size * 0.01;
$whole = floor($done);
$bar = "<colour:$colour>";
$bar .= str_repeat('█', $whole);
if ($whole < $size) {
// By using unicode chars for partial blocks we can have higher
// precision progress bar.
$fraction = floor(($done - $whole) * 8);
$bar .= core_text::substr(' ▏▎▍▌▋▊▉', $fraction, 1);
// Fill the rest of the empty bar.
$bar .= str_repeat(' ', $size - $whole - 1);
}
$bar .= '<colour:normal>';
if ($estimate) {
$estimate = "- $estimate";
}
$ascii .= '<cursor:up>';
$ascii .= '<cursor:up>';
$ascii .= sprintf("[$bar] %3.1f%% %-22s\n", $percent, $estimate);
$ascii .= sprintf("%-80s\n", $msg);
if ($percent == 100) {
$ascii .= "\n";
}
return cli_ansi_format($ascii);
}
// If we are not rendering to a tty, ie when piped to another command
// or on windows we need to progressively render the progress bar
// which can only ever go forwards.
$done = round($percent * $size * 0.01);
$delta = max(0, $done - $this->progressmaximums[$id]);
$this->progressmaximums[$id] += $delta;
$ascii .= str_repeat('#', $delta);
if ($percent >= 100) {
$ascii .= sprintf("] %3.1f%%\n$msg\n", $percent);
}
return $ascii;
}
/**
* Returns a template fragment representing a Heading.
*