Running progress bars, tweaks

pull/571/head
Piero Toffanin 2018-12-07 15:13:49 -05:00
rodzic 5431b88620
commit 99428c98cf
9 zmienionych plików z 125 dodań i 20 usunięć

Wyświetl plik

@ -0,0 +1,18 @@
# Generated by Django 2.0.3 on 2018-12-07 18:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0022_auto_20181205_1644'),
]
operations = [
migrations.AddField(
model_name='task',
name='running_progress',
field=models.FloatField(blank=True, default=0.0, help_text="Value between 0 and 1 indicating the running progress (estimated) of this task's."),
),
]

Wyświetl plik

@ -156,6 +156,22 @@ class Task(models.Model):
(pending_actions.RESIZE, 'RESIZE'),
)
# Not an exact science
TASK_OUTPUT_MILESTONES = {
'Running ODM Load Dataset Cell': 0.01,
'Running ODM Load Dataset Cell - Finished': 0.05,
'opensfm/bin/opensfm match_features': 0.10,
'opensfm/bin/opensfm reconstruct': 0.20,
'opensfm/bin/opensfm export_visualsfm': 0.30,
'Running ODM Meshing Cell': 0.60,
'Running MVS Texturing Cell': 0.65,
'Running ODM Georeferencing Cell': 0.70,
'Running ODM DEM Cell': 0.80,
'Running ODM Orthophoto Cell': 0.85,
'Running ODM OrthoPhoto Cell - Finished': 0.90,
'Compressing all.zip:': 0.95
}
id = models.UUIDField(primary_key=True, default=uuid_module.uuid4, unique=True, serialize=False, editable=False)
uuid = models.CharField(max_length=255, db_index=True, default='', blank=True, help_text="Identifier of the task (as returned by OpenDroneMap's REST API)")
@ -187,6 +203,9 @@ class Task(models.Model):
resize_progress = models.FloatField(default=0.0,
help_text="Value between 0 and 1 indicating the resize progress of this task's images.",
blank=True)
running_progress = models.FloatField(default=0.0,
help_text="Value between 0 and 1 indicating the running progress (estimated) of this task's.",
blank=True)
def __init__(self, *args, **kwargs):
super(Task, self).__init__(*args, **kwargs)
@ -429,12 +448,14 @@ class Task(models.Model):
# We also remove the "rerun-from" parameter if it's set
self.options = list(filter(lambda d: d['name'] != 'rerun-from', self.options))
self.upload_progress = 0
self.console_output = ""
self.processing_time = -1
self.status = None
self.last_error = None
self.pending_action = None
self.running_progress = 0
self.save()
else:
raise ProcessingError("Cannot restart a task that has no processing node")
@ -468,7 +489,14 @@ class Task(models.Model):
current_lines_count = len(self.console_output.split("\n"))
console_output = self.processing_node.get_task_console_output(self.uuid, current_lines_count)
if len(console_output) > 0:
self.console_output += console_output + '\n'
self.console_output += "\n".join(console_output) + '\n'
# Update running progress
for line in console_output:
for line_match, value in self.TASK_OUTPUT_MILESTONES.items():
if line_match in line:
self.running_progress = value
break
if "errorMessage" in info["status"]:
self.last_error = info["status"]["errorMessage"]
@ -527,6 +555,7 @@ class Task(models.Model):
logger.info("Populated extent field with {} for {}".format(raster_path, self))
self.update_available_assets_field()
self.running_progress = 1.0
self.save()
from app.plugins import signals as plugin_signals

Wyświetl plik

@ -0,0 +1,16 @@
const values = {};
export default {
getValue: function(className, property, element = 'div'){
const k = className + '|' + property;
if (values[k]) return values[k];
else{
let d = document.createElement(element);
d.style.display = "none";
d.className = className;
document.body.appendChild(d);
values[k] = getComputedStyle(d)[property];
document.body.removeChild(d);
return values[k];
}
}
}

Wyświetl plik

@ -112,7 +112,7 @@ class BasicTaskView extends React.Component {
}
this.tearDownDynamicSource();
this.setState({lines: [], currentRf: 0, loaded: false});
this.setState({lines: [], currentRf: 0, loaded: false, rf: this.state.rf});
this.setupDynamicSource();
}
@ -122,9 +122,18 @@ class BasicTaskView extends React.Component {
}
componentDidUpdate(prevProps){
let taskFailed = [StatusCodes.RUNNING, StatusCodes.QUEUED].indexOf(prevProps.taskStatus) !== -1 &&
[StatusCodes.FAILED, StatusCodes.CANCELED].indexOf(this.props.taskStatus) !== -1;
this.updateRfState(taskFailed);
let taskFailed;
let taskCompleted;
let taskRestarted;
if (prevProps.taskStatus !== this.props.taskStatus){
taskFailed = [StatusCodes.FAILED, StatusCodes.CANCELED].indexOf(this.props.taskStatus) !== -1;
taskCompleted = this.props.taskStatus === StatusCodes.COMPLETED;
taskRestarted = this.props.taskStatus === null;
}
this.updateRfState(taskFailed, taskCompleted, taskRestarted);
}
componentWillUnmount(){
@ -173,7 +182,7 @@ class BasicTaskView extends React.Component {
if (this.props.onAddLines) this.props.onAddLines(lines);
}
updateRfState(taskFailed){
updateRfState(taskFailed, taskCompleted, taskRestarted){
// If the task has just failed, update all items that were either running or in queued state
if (taskFailed){
this.state.rf.forEach(p => {
@ -181,9 +190,14 @@ class BasicTaskView extends React.Component {
});
}
// The last is always dependent on the task status
this.state.rf[this.state.rf.length - 1].state = this.getInitialStatus();
// If completed, all steps must have completed
if (taskCompleted){
this.state.rf.forEach(p => p.state = 'completed');
}
if (taskRestarted){
this.state.rf.forEach(p => p.state = 'queued');
}
}
suffixFor(state){

Wyświetl plik

@ -11,6 +11,7 @@ import PropTypes from 'prop-types';
import TaskPluginActionButtons from './TaskPluginActionButtons';
import PipelineSteps from '../classes/PipelineSteps';
import BasicTaskView from './BasicTaskView';
import Css from '../classes/Css';
class TaskListItem extends React.Component {
static propTypes = {
@ -49,6 +50,10 @@ class TaskListItem extends React.Component {
this.checkForCommonErrors = this.checkForCommonErrors.bind(this);
this.handleEditTaskSave = this.handleEditTaskSave.bind(this);
this.setView = this.setView.bind(this);
// Retrieve CSS values for status bar colors
this.backgroundSuccessColor = Css.getValue('theme-background-success', 'backgroundColor');
this.backgroundFailedColor = Css.getValue('theme-background-failed', 'backgroundColor');
}
shouldRefresh(){
@ -351,7 +356,7 @@ class TaskListItem extends React.Component {
const name = task.name !== null ? task.name : `Task #${task.id}`;
let status = statusCodes.description(task.status);
if (status === "") status = "Uploading images";
if (status === "") status = "Uploading images to processing node";
if (!task.processing_node) status = "Waiting for a node...";
if (task.pending_action !== null) status = pendingActions.description(task.pending_action);
@ -464,10 +469,6 @@ class TaskListItem extends React.Component {
<div className="labels">
<strong>Processing Node: </strong> {task.processing_node_name || "-"} ({task.auto_processing_node ? "auto" : "manual"})<br/>
</div>
{status ? <div className="labels">
<strong>Status: </strong> {status}<br/>
</div>
: ""}
{Array.isArray(task.options) ?
<div className="labels">
<strong>Options: </strong> {this.optionsToList(task.options)}<br/>
@ -482,7 +483,7 @@ class TaskListItem extends React.Component {
<div className="col-md-9">
<div className="switch-view text-right pull-right">
<i className="fa fa-list-ul"></i> <a href="javascript:void(0);" onClick={this.setView("basic")}
className={this.state.view === 'basic' ? "selected" : ""}>Basic</a>
className={this.state.view === 'basic' ? "selected" : ""}>Simple</a>
|
<i className="fa fa-desktop"></i> <a href="javascript:void(0);" onClick={this.setView("console")}
className={this.state.view === 'console' ? "selected" : ""}>Console</a>
@ -551,8 +552,15 @@ class TaskListItem extends React.Component {
}
}
const getStatusLabel = (text, classes = "") => {
return (<div className={"status-label " + classes} title={text}>{text}</div>);
// @param type {String} one of: ['neutral', 'done', 'error']
const getStatusLabel = (text, type = 'neutral', progress = 100) => {
let color = 'rgba(255, 255, 255, 0.0)';
if (type === 'done') color = this.backgroundSuccessColor;
else if (type === 'error') color = this.backgroundFailedColor;
return (<div
className={"status-label theme-border-primary " + type}
style={{background: `linear-gradient(90deg, ${color} ${progress}%, rgba(255, 255, 255, 0) ${progress}%)`}}
title={text}>{text}</div>);
}
let statusLabel = "";
@ -560,13 +568,28 @@ class TaskListItem extends React.Component {
let showEditLink = false;
if (task.last_error){
statusLabel = getStatusLabel(task.last_error, "error");
statusLabel = getStatusLabel(task.last_error, 'error');
}else if (!task.processing_node){
statusLabel = getStatusLabel("Set a processing node");
statusIcon = "fa fa-hourglass-3";
showEditLink = true;
}else{
statusLabel = getStatusLabel(status, task.status == 40 ? "done" : "");
let progress = 100;
let type = 'done';
if (task.pending_action === pendingActions.RESIZE){
progress = task.resize_progress * 100;
}else if (task.status === null){
progress = task.upload_progress * 100;
}else if (task.status === statusCodes.RUNNING){
progress = task.running_progress * 100;
}else if (task.status === statusCodes.FAILED){
type = 'error';
}else if (task.status !== statusCodes.COMPLETED){
type = 'neutral';
}
statusLabel = getStatusLabel(status, type, progress);
}
return (

Wyświetl plik

@ -41,6 +41,8 @@
padding: 4px;
width: 100%;
font-size: 90%;
border-style: solid;
border-width: 1px;
}
.clickable:hover{

Wyświetl plik

@ -153,7 +153,7 @@ class ProcessingNode(models.Model):
if isinstance(result, dict) and 'error' in result:
raise ProcessingError(result['error'])
elif isinstance(result, list):
return "\n".join(result)
return result
else:
raise ProcessingError("Unknown response for console output: {}".format(result))

Wyświetl plik

@ -121,7 +121,7 @@ class TestClientApi(TestCase):
# task_output
self.assertTrue(isinstance(api.task_output(uuid, 0), list))
self.assertTrue(isinstance(online_node.get_task_console_output(uuid, 0), str))
self.assertTrue(isinstance(online_node.get_task_console_output(uuid, 0), list))
self.assertRaises(ProcessingError, online_node.get_task_console_output, "wrong-uuid", 0)

Wyświetl plik

@ -47,6 +47,9 @@ if [ "$1" = "--setup-devenv" ] || [ "$2" = "--setup-devenv" ]; then
npm install
cd /webodm
echo Setup pip requirements...
pip install -r requirements.txt
echo Setup webpack watch...
webpack --watch &
fi