kopia lustrzana https://github.com/OpenDroneMap/WebODM
Added /processingnodes/options/ API, unit tests
rodzic
2461e0701c
commit
e14fc574a6
|
|
@ -1,6 +1,10 @@
|
|||
import django_filters
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django_filters.rest_framework import FilterSet
|
||||
from rest_framework import serializers, viewsets
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework import serializers, viewsets, exceptions
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from nodeodm.models import ProcessingNode
|
||||
|
||||
|
|
@ -36,3 +40,46 @@ class ProcessingNodeViewSet(viewsets.ModelViewSet):
|
|||
pagination_class = None
|
||||
serializer_class = ProcessingNodeSerializer
|
||||
queryset = ProcessingNode.objects.all()
|
||||
|
||||
|
||||
class ProcessingNodeOptionsView(APIView):
|
||||
"""
|
||||
Display the intersection of all ProcessingNode's available_options fields.
|
||||
Each ProcessingNode has its own set of available_options. When a user relies on the
|
||||
automatic node selection feature, it's better to have a list of available_options that
|
||||
is common among all ProcessingNode.
|
||||
"""
|
||||
|
||||
queryset = ProcessingNode.objects.all()
|
||||
|
||||
def get(self, request):
|
||||
|
||||
nodes = get_objects_for_user(request.user, 'view_processingnode', ProcessingNode, accept_global_perms=False)
|
||||
|
||||
common_options = []
|
||||
|
||||
for node in nodes:
|
||||
# Skip offline nodes
|
||||
if not node.is_online():
|
||||
continue
|
||||
|
||||
# First? Just populate
|
||||
if len(common_options) == 0 and len(node.available_options) > 0:
|
||||
common_options = node.available_options
|
||||
else:
|
||||
# Remove all options that are in common_options,
|
||||
# but that are not in node.available_options
|
||||
for common_option in common_options:
|
||||
found = False
|
||||
for option in node.available_options:
|
||||
if common_option['name'] == option['name']:
|
||||
found = True
|
||||
break
|
||||
|
||||
# Mark for deletion
|
||||
if not found:
|
||||
common_option['_delete'] = True
|
||||
|
||||
common_options = [co for co in common_options if not '_delete' in co]
|
||||
|
||||
return Response(common_options)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from django.conf.urls import url, include
|
||||
from .projects import ProjectViewSet
|
||||
from .tasks import TaskViewSet, TaskTiles, TaskTilesJson, TaskDownloads, TaskAssets
|
||||
from .processingnodes import ProcessingNodeViewSet
|
||||
from .processingnodes import ProcessingNodeViewSet, ProcessingNodeOptionsView
|
||||
from rest_framework_nested import routers
|
||||
from rest_framework_jwt.views import obtain_jwt_token
|
||||
|
||||
|
|
@ -13,6 +13,8 @@ tasks_router = routers.NestedSimpleRouter(router, r'projects', lookup='project')
|
|||
tasks_router.register(r'tasks', TaskViewSet, base_name='projects-tasks')
|
||||
|
||||
urlpatterns = [
|
||||
url(r'processingnodes/options/$', ProcessingNodeOptionsView.as_view()),
|
||||
|
||||
url(r'^', include(router.urls)),
|
||||
url(r'^', include(tasks_router.urls)),
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import datetime
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from guardian.shortcuts import assign_perm
|
||||
from guardian.shortcuts import assign_perm, get_objects_for_user
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework_jwt.settings import api_settings
|
||||
|
||||
from app import pending_actions
|
||||
from app.models import Project, Task
|
||||
from nodeodm.models import ProcessingNode
|
||||
from nodeodm.models import ProcessingNode, OFFLINE_MINUTES
|
||||
from .classes import BootTestCase
|
||||
|
||||
|
||||
|
|
@ -237,6 +238,10 @@ class TestApi(BootTestCase):
|
|||
res = client.get('/api/processingnodes/{}/'.format(pnode.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# Cannot get options as guest
|
||||
res = client.get('/api/processingnodes/options/')
|
||||
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
client.login(username="testuser", password="test1234")
|
||||
|
||||
# Cannot list processing nodes, unless permissions have been granted
|
||||
|
|
@ -307,6 +312,39 @@ class TestApi(BootTestCase):
|
|||
self.assertTrue(len(res.data) == 2)
|
||||
self.assertTrue(res.data[1]["port"] == 1000)
|
||||
|
||||
# Test available_options intersection
|
||||
# (with normal user)
|
||||
client.login(username="testuser", password="test1234")
|
||||
user = User.objects.get(username="testuser")
|
||||
self.assertFalse(user.is_superuser)
|
||||
|
||||
p1 = ProcessingNode.objects.create(hostname="invalid-host", port=11223,
|
||||
last_refreshed=timezone.now(),
|
||||
available_options=[{'name': 'a'}, {'name': 'b'}])
|
||||
p2 = ProcessingNode.objects.create(hostname="invalid-host-2", port=11223,
|
||||
last_refreshed=timezone.now(),
|
||||
available_options=[{'name': 'a'}, {'name': 'c'}])
|
||||
p3 = ProcessingNode.objects.create(hostname="invalid-host-3", port=11223,
|
||||
last_refreshed=timezone.now(),
|
||||
available_options=[{'name': 'd'}])
|
||||
p4 = ProcessingNode.objects.create(hostname="invalid-host-4", port=11223,
|
||||
last_refreshed=timezone.now() - datetime.timedelta(minutes=OFFLINE_MINUTES * 2),
|
||||
available_options=[{'name': 'd'}]) # offline
|
||||
|
||||
assign_perm('view_processingnode', user, p1)
|
||||
assign_perm('view_processingnode', user, p2)
|
||||
assign_perm('view_processingnode', user, p4)
|
||||
self.assertFalse(user.has_perm('view_processingnode', p3))
|
||||
|
||||
nodes_available = get_objects_for_user(user, 'view_processingnode', ProcessingNode, accept_global_perms=False).exclude(available_options=dict())
|
||||
self.assertTrue(len(nodes_available) == 3)
|
||||
|
||||
res = client.get('/api/processingnodes/options/')
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(len(res.data) == 1)
|
||||
self.assertTrue(res.data[0]['name'] == 'a')
|
||||
|
||||
|
||||
def test_token_auth(self):
|
||||
client = APIClient()
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ from django.db.models import signals
|
|||
from datetime import datetime, timedelta
|
||||
from .exceptions import ProcessingError, ProcessingTimeout
|
||||
import simplejson
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
def api(func):
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue