kopia lustrzana https://github.com/OpenDroneMap/WebODM
Fixed processing nodes permissions display, readded processing node menu in backend via template tags
rodzic
66bae84aa9
commit
deac02c385
|
@ -1,8 +1,7 @@
|
||||||
import django_filters
|
import django_filters
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django_filters.rest_framework import FilterSet
|
from django_filters.rest_framework import FilterSet
|
||||||
from guardian.shortcuts import get_objects_for_user
|
from guardian.shortcuts import get_objects_for_user
|
||||||
from rest_framework import serializers, viewsets, exceptions
|
from rest_framework import serializers, viewsets
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ class ProjectListItem extends React.Component {
|
||||||
this.handleEditProject = this.handleEditProject.bind(this);
|
this.handleEditProject = this.handleEditProject.bind(this);
|
||||||
this.updateProject = this.updateProject.bind(this);
|
this.updateProject = this.updateProject.bind(this);
|
||||||
this.taskDeleted = this.taskDeleted.bind(this);
|
this.taskDeleted = this.taskDeleted.bind(this);
|
||||||
|
this.hasPermission = this.hasPermission.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(){
|
refresh(){
|
||||||
|
@ -87,81 +88,87 @@ class ProjectListItem extends React.Component {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasPermission(perm){
|
||||||
|
return this.state.data.permissions.indexOf(perm) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount(){
|
componentDidMount(){
|
||||||
Dropzone.autoDiscover = false;
|
Dropzone.autoDiscover = false;
|
||||||
|
|
||||||
this.dz = new Dropzone(this.dropzone, {
|
if (this.hasPermission("add")){
|
||||||
paramName: "images",
|
this.dz = new Dropzone(this.dropzone, {
|
||||||
url : `/api/projects/${this.state.data.id}/tasks/`,
|
paramName: "images",
|
||||||
parallelUploads: 9999999,
|
url : `/api/projects/${this.state.data.id}/tasks/`,
|
||||||
uploadMultiple: true,
|
parallelUploads: 9999999,
|
||||||
acceptedFiles: "image/*, .txt",
|
uploadMultiple: true,
|
||||||
autoProcessQueue: true,
|
acceptedFiles: "image/*, .txt",
|
||||||
createImageThumbnails: false,
|
autoProcessQueue: true,
|
||||||
clickable: this.uploadButton,
|
createImageThumbnails: false,
|
||||||
|
clickable: this.uploadButton,
|
||||||
headers: {
|
|
||||||
[csrf.header]: csrf.token
|
headers: {
|
||||||
}
|
[csrf.header]: csrf.token
|
||||||
});
|
|
||||||
|
|
||||||
this.dz.on("totaluploadprogress", (progress, totalBytes, totalBytesSent) => {
|
|
||||||
this.setUploadState({
|
|
||||||
progress, totalBytes, totalBytesSent
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on("addedfile", () => {
|
|
||||||
this.setUploadState({
|
|
||||||
totalCount: this.state.upload.totalCount + 1
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on("processingmultiple", () => {
|
|
||||||
this.setUploadState({
|
|
||||||
uploading: true,
|
|
||||||
showEditTask: true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.on("completemultiple", (files) => {
|
|
||||||
// Check
|
|
||||||
let success = files.length > 0 && files.filter(file => file.status !== "success").length === 0;
|
|
||||||
|
|
||||||
// All files have uploaded!
|
|
||||||
if (success){
|
|
||||||
this.setUploadState({uploading: false});
|
|
||||||
|
|
||||||
try{
|
|
||||||
let response = JSON.parse(files[0].xhr.response);
|
|
||||||
if (!response.id) throw new Error(`Expected id field, but none given (${response})`);
|
|
||||||
|
|
||||||
let taskId = response.id;
|
|
||||||
this.setUploadState({taskId});
|
|
||||||
|
|
||||||
// Update task information (if the user has completed this step)
|
|
||||||
if (this.state.upload.savedTaskInfo){
|
|
||||||
this.updateTaskInfo(taskId, this.editTaskPanel.getTaskInfo());
|
|
||||||
}else{
|
|
||||||
// Need to wait for user to confirm task options
|
|
||||||
}
|
|
||||||
}catch(e){
|
|
||||||
this.setUploadState({error: `Invalid response from server: ${e.message}`})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}else{
|
|
||||||
this.setUploadState({
|
|
||||||
uploading: false,
|
|
||||||
error: "Could not upload all files. An error occured. Please try again."
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on("reset", () => {
|
|
||||||
this.resetUploadState();
|
|
||||||
})
|
|
||||||
.on("dragenter", () => {
|
|
||||||
this.resetUploadState();
|
|
||||||
})
|
|
||||||
.on("sending", (file, xhr, formData) => {
|
|
||||||
formData.append('auto_processing_node', "false");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.dz.on("totaluploadprogress", (progress, totalBytes, totalBytesSent) => {
|
||||||
|
this.setUploadState({
|
||||||
|
progress, totalBytes, totalBytesSent
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on("addedfile", () => {
|
||||||
|
this.setUploadState({
|
||||||
|
totalCount: this.state.upload.totalCount + 1
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on("processingmultiple", () => {
|
||||||
|
this.setUploadState({
|
||||||
|
uploading: true,
|
||||||
|
showEditTask: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on("completemultiple", (files) => {
|
||||||
|
// Check
|
||||||
|
let success = files.length > 0 && files.filter(file => file.status !== "success").length === 0;
|
||||||
|
|
||||||
|
// All files have uploaded!
|
||||||
|
if (success){
|
||||||
|
this.setUploadState({uploading: false});
|
||||||
|
|
||||||
|
try{
|
||||||
|
let response = JSON.parse(files[0].xhr.response);
|
||||||
|
if (!response.id) throw new Error(`Expected id field, but none given (${response})`);
|
||||||
|
|
||||||
|
let taskId = response.id;
|
||||||
|
this.setUploadState({taskId});
|
||||||
|
|
||||||
|
// Update task information (if the user has completed this step)
|
||||||
|
if (this.state.upload.savedTaskInfo){
|
||||||
|
this.updateTaskInfo(taskId, this.editTaskPanel.getTaskInfo());
|
||||||
|
}else{
|
||||||
|
// Need to wait for user to confirm task options
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
this.setUploadState({error: `Invalid response from server: ${e.message}`})
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
this.setUploadState({
|
||||||
|
uploading: false,
|
||||||
|
error: "Could not upload all files. An error occured. Please try again."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on("reset", () => {
|
||||||
|
this.resetUploadState();
|
||||||
|
})
|
||||||
|
.on("dragenter", () => {
|
||||||
|
this.resetUploadState();
|
||||||
|
})
|
||||||
|
.on("sending", (file, xhr, formData) => {
|
||||||
|
formData.append('auto_processing_node', "false");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTaskInfo(taskId, taskInfo){
|
updateTaskInfo(taskId, taskInfo){
|
||||||
|
@ -289,19 +296,21 @@ class ProjectListItem extends React.Component {
|
||||||
projectName={data.name}
|
projectName={data.name}
|
||||||
projectDescr={data.description}
|
projectDescr={data.description}
|
||||||
saveAction={this.updateProject}
|
saveAction={this.updateProject}
|
||||||
deleteAction={this.handleDelete}
|
deleteAction={this.hasPermission("delete") ? this.handleDelete : undefined}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="row no-margin">
|
<div className="row no-margin">
|
||||||
<ErrorMessage bind={[this, 'error']} />
|
<ErrorMessage bind={[this, 'error']} />
|
||||||
<div className="btn-group pull-right">
|
<div className="btn-group pull-right">
|
||||||
<button type="button"
|
{this.hasPermission("add") ?
|
||||||
className={"btn btn-primary btn-sm " + (this.state.upload.uploading ? "hide" : "")}
|
<button type="button"
|
||||||
onClick={this.handleUpload}
|
className={"btn btn-primary btn-sm " + (this.state.upload.uploading ? "hide" : "")}
|
||||||
ref={this.setRef("uploadButton")}>
|
onClick={this.handleUpload}
|
||||||
<i className="glyphicon glyphicon-upload"></i>
|
ref={this.setRef("uploadButton")}>
|
||||||
Upload Images and GCP
|
<i className="glyphicon glyphicon-upload"></i>
|
||||||
</button>
|
Upload Images and GCP
|
||||||
|
</button>
|
||||||
|
: ""}
|
||||||
|
|
||||||
<button disabled={this.state.upload.error !== ""}
|
<button disabled={this.state.upload.error !== ""}
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
@ -241,17 +241,23 @@
|
||||||
<!--<li>
|
<!--<li>
|
||||||
<a href="#"><i class="fa fa-plane fa-fw"></i> Mission Planner</a>
|
<a href="#"><i class="fa fa-plane fa-fw"></i> Mission Planner</a>
|
||||||
</li> -->
|
</li> -->
|
||||||
{% if can_view_processingnode %}
|
{% load processingnode_extras %}
|
||||||
|
{% can_view_processing_nodes as view_nodes %}
|
||||||
|
{% can_add_processing_nodes as add_nodes %}
|
||||||
|
{% get_visible_processing_nodes as nodes %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if view_nodes %}
|
||||||
<li>
|
<li>
|
||||||
<a href="#"><i class="fa fa-wrench fa-fw"></i> Processing Nodes<span class="fa arrow"></span></a>
|
<a href="#"><i class="fa fa-wrench fa-fw"></i> Processing Nodes<span class="fa arrow"></span></a>
|
||||||
<ul class="nav nav-second-level">
|
<ul class="nav nav-second-level">
|
||||||
{% for node in processingnodes %}
|
{% for node in nodes %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'processing_node' node.id %}"><span class="fa fa-laptop"></span> {{node}}</a>
|
<a href="{% url 'processing_node' node.id %}"><span class="fa fa-laptop"></span> {{node}}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if can_add_processingnode %}
|
{% if add_nodes %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'admin:nodeodm_processingnode_add' %}"><span class="fa fa-plus-circle"></span> Add New</a>
|
<a href="{% url 'admin:nodeodm_processingnode_add' %}"><span class="fa fa-plus-circle"></span> Add New</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
from django import template
|
||||||
|
from guardian.shortcuts import get_objects_for_user
|
||||||
|
|
||||||
|
from app.models import ProcessingNode
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.assignment_tag(takes_context=True)
|
||||||
|
def get_visible_processing_nodes(context):
|
||||||
|
return get_objects_for_user(context['request'].user, "nodeodm.view_processingnode", ProcessingNode, accept_global_perms=False)
|
||||||
|
|
||||||
|
|
||||||
|
@register.assignment_tag(takes_context=True)
|
||||||
|
def can_view_processing_nodes(context):
|
||||||
|
return context['request'].user.has_perm("nodeodm.view_processingnode")
|
||||||
|
|
||||||
|
|
||||||
|
@register.assignment_tag(takes_context=True)
|
||||||
|
def can_add_processing_nodes(context):
|
||||||
|
return context['request'].user.has_perm("nodeodm.add_processingnode")
|
25
app/views.py
25
app/views.py
|
@ -33,8 +33,7 @@ def dashboard(request):
|
||||||
|
|
||||||
return render(request, 'app/dashboard.html', {'title': 'Dashboard',
|
return render(request, 'app/dashboard.html', {'title': 'Dashboard',
|
||||||
'no_processingnodes': no_processingnodes,
|
'no_processingnodes': no_processingnodes,
|
||||||
'no_tasks': no_tasks,
|
'no_tasks': no_tasks
|
||||||
**get_view_params(request),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,8 +59,7 @@ def map(request, project_pk=None, task_pk=None):
|
||||||
'title': title,
|
'title': title,
|
||||||
'params': {
|
'params': {
|
||||||
'tiles': json.dumps(tiles)
|
'tiles': json.dumps(tiles)
|
||||||
}.items(),
|
}.items()
|
||||||
**get_view_params(request),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,8 +86,7 @@ def model_display(request, project_pk=None, task_pk=None):
|
||||||
'project': project.id,
|
'project': project.id,
|
||||||
'available_assets': task.get_available_assets()
|
'available_assets': task.get_available_assets()
|
||||||
})
|
})
|
||||||
}.items(),
|
}.items()
|
||||||
**get_view_params(request),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,8 +100,7 @@ def processing_node(request, processing_node_id):
|
||||||
{
|
{
|
||||||
'title': 'Processing Node',
|
'title': 'Processing Node',
|
||||||
'processing_node': pn,
|
'processing_node': pn,
|
||||||
'available_options_json': pn.get_available_options_json(pretty=True),
|
'available_options_json': pn.get_available_options_json(pretty=True)
|
||||||
**get_view_params(request),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
class FirstUserForm(forms.ModelForm):
|
class FirstUserForm(forms.ModelForm):
|
||||||
|
@ -138,16 +134,3 @@ def welcome(request):
|
||||||
'title': 'Welcome',
|
'title': 'Welcome',
|
||||||
'firstuserform': fuf
|
'firstuserform': fuf
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def get_view_params(request):
|
|
||||||
"""
|
|
||||||
Returns common parameters to pass to a view
|
|
||||||
"""
|
|
||||||
processingnodes = get_objects_for_user(request.user, "nodeodm.view_processingnode", ProcessingNode)
|
|
||||||
|
|
||||||
return {
|
|
||||||
'can_view_processingnode': request.user.has_perm("nodeodm.view_processingnode"),
|
|
||||||
'can_add_processingnode': request.user.has_perm("nodeodm.add_processingnode"),
|
|
||||||
'processingnodes': processingnodes,
|
|
||||||
}
|
|
Ładowanie…
Reference in New Issue