Fixed processing nodes permissions display, readded processing node menu in backend via template tags

pull/209/head
Piero Toffanin 2017-06-15 15:47:00 -04:00
rodzic 66bae84aa9
commit deac02c385
5 zmienionych plików z 122 dodań i 104 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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"

Wyświetl plik

@ -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>

Wyświetl plik

@ -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")

Wyświetl plik

@ -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,
}