kopia lustrzana https://github.com/OpenDroneMap/WebODM
Added console dynamic source, console output only flag in tasks API
rodzic
88fa760045
commit
d7a450521c
|
@ -48,6 +48,12 @@ class TaskViewSet(viewsets.ViewSet):
|
|||
raise exceptions.NotFound()
|
||||
return project
|
||||
|
||||
@staticmethod
|
||||
def task_output_only(request, task):
|
||||
line_num = max(0, int(request.query_params.get('line', 0)))
|
||||
output = task.console_output or ""
|
||||
return '\n'.join(output.split('\n')[line_num:])
|
||||
|
||||
def list(self, request, project_pk=None):
|
||||
project = self.get_and_check_project(request, project_pk)
|
||||
tasks = self.queryset.filter(project=project_pk)
|
||||
|
@ -61,8 +67,16 @@ class TaskViewSet(viewsets.ViewSet):
|
|||
task = self.queryset.get(pk=pk, project=project_pk)
|
||||
except ObjectDoesNotExist:
|
||||
raise exceptions.NotFound()
|
||||
serializer = TaskSerializer(task)
|
||||
return Response(serializer.data)
|
||||
|
||||
response_data = None
|
||||
|
||||
if request.query_params.get('output_only', '').lower() in ['true', '1']:
|
||||
response_data = self.task_output_only(request, task)
|
||||
else:
|
||||
serializer = TaskSerializer(task)
|
||||
response_data = serializer.data
|
||||
|
||||
return Response(response_data)
|
||||
|
||||
def create(self, request, project_pk=None):
|
||||
project = self.get_and_check_project(request, project_pk, ('change_project', ))
|
||||
|
|
|
@ -84,7 +84,7 @@ def setup():
|
|||
try:
|
||||
scheduler.start()
|
||||
scheduler.add_job(update_nodes_info, 'interval', seconds=30)
|
||||
scheduler.add_job(process_pending_tasks, 'interval', seconds=5)
|
||||
scheduler.add_job(process_pending_tasks, 'interval', seconds=15)
|
||||
except SchedulerAlreadyRunningError:
|
||||
logger.warn("Scheduler already running (this is OK while testing)")
|
||||
|
||||
|
|
|
@ -26,6 +26,36 @@ class Console extends React.Component {
|
|||
|
||||
componentDidMount(){
|
||||
this.checkAutoscroll();
|
||||
|
||||
// Dynamic source?
|
||||
if (this.props.source !== undefined){
|
||||
let currentLineNumber = 0;
|
||||
|
||||
const updateFromSource = () => {
|
||||
let sourceUrl = typeof this.props.source === 'function' ?
|
||||
this.props.source(currentLineNumber) :
|
||||
this.props.source;
|
||||
|
||||
// Fetch
|
||||
this.sourceRequest = $.get(sourceUrl, text => {
|
||||
let lines = text.split("\n");
|
||||
lines.forEach(line => this.addLine(line));
|
||||
currentLineNumber += (lines.length - 1);
|
||||
})
|
||||
.always(() => {
|
||||
if (this.props.refreshInterval !== undefined){
|
||||
this.sourceTimeout = setTimeout(updateFromSource, this.props.refreshInterval);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
updateFromSource();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
if (this.sourceTimeout) clearTimeout(this.sourceTimeout);
|
||||
if (this.sourceRequest) this.sourceRequest.abort();
|
||||
}
|
||||
|
||||
setRef(domNode){
|
||||
|
|
|
@ -11,9 +11,11 @@ class TaskListItem extends React.Component {
|
|||
}
|
||||
|
||||
this.toggleExpanded = this.toggleExpanded.bind(this);
|
||||
this.consoleOutputUrl = this.consoleOutputUrl.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
|
||||
}
|
||||
|
||||
toggleExpanded(){
|
||||
|
@ -22,17 +24,32 @@ class TaskListItem extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
consoleOutputUrl(line){
|
||||
return `/api/projects/${this.props.data.project}/tasks/${this.props.data.id}/?output_only=true&line=${line}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
let name = this.props.data.name !== null ? this.props.data.name : `Task #${this.props.data.id}`;
|
||||
|
||||
let expanded = "";
|
||||
if (this.state.expanded){
|
||||
expanded = (
|
||||
<div className="row expanded-panel">
|
||||
<div className="labels">
|
||||
<strong>Status: </strong> Running<br/>
|
||||
<div className="expanded-panel">
|
||||
<div className="row">
|
||||
<div className="col-md-4 no-padding">
|
||||
<div className="labels">
|
||||
<strong>Status: </strong> Running<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-md-8">
|
||||
<Console
|
||||
source={this.consoleOutputUrl}
|
||||
refreshInterval={3000}
|
||||
autoscroll={true}
|
||||
height={200} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<button type="button" className="btn btn-primary btn-sm">
|
||||
<i className="glyphicon glyphicon-remove-circle"></i>
|
||||
Restart
|
||||
|
@ -44,7 +61,8 @@ class TaskListItem extends React.Component {
|
|||
Delete
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
.row{
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
|
||||
.no-padding{
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.name{
|
||||
|
|
|
@ -2,7 +2,6 @@ from django.test import TestCase
|
|||
from django.contrib.auth.models import User, Group
|
||||
from app.models import Project
|
||||
from app.boot import boot
|
||||
from app import scheduler
|
||||
|
||||
class BootTestCase(TestCase):
|
||||
'''
|
||||
|
|
|
@ -101,6 +101,28 @@ class TestApi(BootTestCase):
|
|||
# images_count field exists
|
||||
self.assertTrue(res.data["images_count"] == 0)
|
||||
|
||||
# Get console output
|
||||
res = client.get('/api/projects/{}/tasks/{}/?output_only=true'.format(project.id, task.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(res.data == "")
|
||||
|
||||
task.console_output = "line1\nline2\nline3"
|
||||
task.save()
|
||||
|
||||
res = client.get('/api/projects/{}/tasks/{}/?output_only=true'.format(project.id, task.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(res.data == task.console_output)
|
||||
|
||||
# Console output with line num
|
||||
res = client.get('/api/projects/{}/tasks/{}/?output_only=true&line=2'.format(project.id, task.id))
|
||||
self.assertTrue(res.data == "line3")
|
||||
|
||||
# Console output with line num out of bounds
|
||||
res = client.get('/api/projects/{}/tasks/{}/?output_only=true&line=3'.format(project.id, task.id))
|
||||
self.assertTrue(res.data == "")
|
||||
res = client.get('/api/projects/{}/tasks/{}/?output_only=true&line=-1'.format(project.id, task.id))
|
||||
self.assertTrue(res.data == task.console_output)
|
||||
|
||||
# Cannot list task details for a task belonging to a project we don't have access to
|
||||
res = client.get('/api/projects/{}/tasks/{}/'.format(other_project.id, other_task.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
|
Ładowanie…
Reference in New Issue