From 26acdd7bd227e869b03cc5d338e817285722e3b8 Mon Sep 17 00:00:00 2001 From: Darko Aleksovski Date: Tue, 31 Jan 2017 16:10:05 +0100 Subject: [PATCH 1/6] Initial commit of pdmanager package and related code --- workflows/api_urls.py | 5 + workflows/pdmanager/__init__.py | 0 .../a6a59bb7-5f2c-41b7-a110-7f20a7152cd1.json | 9 + .../6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json | 56 ++++++ workflows/pdmanager/predefined_workflow.json | 46 +++++ .../icons/treeview/construction_work .png | Bin 0 -> 2375 bytes .../icons/widget/construction_work .png | Bin 0 -> 21353 bytes workflows/urls.py | 5 + workflows/views_integration.py | 169 ++++++++++++++++++ 9 files changed, 290 insertions(+) create mode 100644 workflows/pdmanager/__init__.py create mode 100644 workflows/pdmanager/package_data/categories/a6a59bb7-5f2c-41b7-a110-7f20a7152cd1.json create mode 100644 workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json create mode 100644 workflows/pdmanager/predefined_workflow.json create mode 100644 workflows/pdmanager/static/pdmanager/icons/treeview/construction_work .png create mode 100644 workflows/pdmanager/static/pdmanager/icons/widget/construction_work .png create mode 100644 workflows/views_integration.py diff --git a/workflows/api_urls.py b/workflows/api_urls.py index 17bc89a..dcf0bfc 100644 --- a/workflows/api_urls.py +++ b/workflows/api_urls.py @@ -1,6 +1,7 @@ from django.conf.urls import patterns, include, url from rest_framework import routers from workflows import api_views +from workflows import views_integration router = routers.DefaultRouter() router.register(r'workflows', api_views.WorkflowViewSet) @@ -11,6 +12,10 @@ urlpatterns = patterns('', + # ---------------------- + url(r'^register_pd_man_user[/]?$', views_integration.RegisterPdManUserRESTView.as_view(), name='register_pd_man_user'), + # ---------------------- + url(r'^', include(router.urls)), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) ) diff --git a/workflows/pdmanager/__init__.py b/workflows/pdmanager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workflows/pdmanager/package_data/categories/a6a59bb7-5f2c-41b7-a110-7f20a7152cd1.json b/workflows/pdmanager/package_data/categories/a6a59bb7-5f2c-41b7-a110-7f20a7152cd1.json new file mode 100644 index 0000000..390d667 --- /dev/null +++ b/workflows/pdmanager/package_data/categories/a6a59bb7-5f2c-41b7-a110-7f20a7152cd1.json @@ -0,0 +1,9 @@ +{ + "model": "workflows.category", + "fields": { + "name": "PD_Manager", + "parent": null, + "order": 1, + "uid": "a6a59bb7-5f2c-41b7-a110-7f20a7152cd1" + } +} \ No newline at end of file diff --git a/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json b/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json new file mode 100644 index 0000000..a069bc8 --- /dev/null +++ b/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json @@ -0,0 +1,56 @@ +[ + { + "model": "workflows.abstractwidget", + "fields": { + "category": "a6a59bb7-5f2c-41b7-a110-7f20a7152cd1", + "treeview_image": "", + "uid": "6ca99fb5-e8c4-4649-80e1-7e1872e2b819", + "windows_queue": false, + "package": "pdmanager", + "interaction_view": "", + "has_progress_bar": false, + "image": "", + "description": "Imports data from the central database of the PD_Manager project.", + "static_image": "construction_work .png", + "action": "pdmanager_import_data", + "visualization_view": "", + "streaming_visualization_view": "", + "post_interact_action": "", + "wsdl_method": "", + "wsdl": "", + "interactive": false, + "is_streaming": false, + "order": 1, + "name": "Import PD_Manager data" + } + }, + { + "model": "workflows.abstractinput", + "fields": { + "widget": "6ca99fb5-e8c4-4649-80e1-7e1872e2b819", + "name": "dataset_id", + "short_name": "id", + "default": "", + "description": "The ID of the PD_Manager dataset.", + "required": true, + "multi": false, + "parameter_type": "text", + "variable": "dataset_id", + "parameter": true, + "order": 1, + "uid": "0d9c056c-80bf-40c4-98f7-eeabb6e1fdf1" + } + }, + { + "model": "workflows.abstractoutput", + "fields": { + "widget": "6ca99fb5-e8c4-4649-80e1-7e1872e2b819", + "name": "Imported Data", + "short_name": "dat", + "description": "Imported data", + "variable": "data", + "order": 1, + "uid": "4670b2b1-7bf5-4516-8a20-759df02966ea" + } + } +] \ No newline at end of file diff --git a/workflows/pdmanager/predefined_workflow.json b/workflows/pdmanager/predefined_workflow.json new file mode 100644 index 0000000..b57f188 --- /dev/null +++ b/workflows/pdmanager/predefined_workflow.json @@ -0,0 +1,46 @@ +{ + "widgets": [ + { + "inputs": [ + { + "name": "dataset_id", + "short_name": "id", + "inner_output": null, + "multi_id": 0, + "required": true, + "value": "", + "options": [], + "parameter_type": "text", + "variable": "dataset_id", + "pk": 1, + "outer_output": null, + "parameter": true, + "order": 1, + "description": "The ID of the PD_Manager dataset." + } + ], + "name": "Import PD_Manager data", + "abstract_widget": "6ca99fb5-e8c4-4649-80e1-7e1872e2b819", + "workflow": null, + "outputs": [ + { + "name": "Imported Data", + "short_name": "dat", + "outer_input": null, + "variable": "data", + "pk": 1, + "inner_input": null, + "order": 1, + "description": "Imported data" + } + ], + "abstract_widget_package": "pdmanager", + "y": 40, + "x": 32, + "type": "regular" + } + ], + "connections": [], + "name": "PD_Manager Workflow - Data analysis - pdm_112", + "description": "" +} diff --git a/workflows/pdmanager/static/pdmanager/icons/treeview/construction_work .png b/workflows/pdmanager/static/pdmanager/icons/treeview/construction_work .png new file mode 100644 index 0000000000000000000000000000000000000000..69bd351914a95f81eb1941f6e7908474916f6116 GIT binary patch literal 2375 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 literal 0 HcmV?d00001 diff --git a/workflows/pdmanager/static/pdmanager/icons/widget/construction_work .png b/workflows/pdmanager/static/pdmanager/icons/widget/construction_work .png new file mode 100644 index 0000000000000000000000000000000000000000..bc16d376995c1545972b60487ee8cd653177b407 GIT binary patch literal 21353 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 NtzXxDU0$F6e*i35z<>Y% literal 0 HcmV?d00001 diff --git a/workflows/urls.py b/workflows/urls.py index 5865c52..679e909 100755 --- a/workflows/urls.py +++ b/workflows/urls.py @@ -75,4 +75,9 @@ def set_package_url(name, value, package): url(r'^workflow_results/(?P[0-9]+)/$', 'workflows.views.workflow_results', name='workflow_results'), + + # ---------------------- + url(r'^login_and_edit_workflow/(?P[0-9]+)/(?P[0-9A-Za-z_]+)/(?P[0-9A-Za-z_]+)/$', 'workflows.views_integration.login_and_edit_workflow', name='login_edit_workflow'), + # ---------------------- + ) diff --git a/workflows/views_integration.py b/workflows/views_integration.py new file mode 100644 index 0000000..bc23808 --- /dev/null +++ b/workflows/views_integration.py @@ -0,0 +1,169 @@ +import json + +from django.conf import settings +from django.contrib.auth.models import User, Group +from django.shortcuts import redirect +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from .models import Workflow + + +def is_pd_man_admin(user): + return user.groups.filter(name='PD_Manager_Admin').exists() + +def is_pd_man_researcher(user): + return user.groups.filter(name='PD_Manager_Researcher').exists() + + +class RegisterPdManUser(object): + """ + Helper class for registering a new PD_Manager user and creating a workflow + """ + + def __init__(self, data): + self.data = data + pass + + def add_user_to_pd_man_researcher_group(self): + g = Group.objects.get(name='PD_Manager_Researcher') + g.user_set.add(self.user) + g.save() + + def do_register(self): + # STEP 1 : Create a user + + # Check if this user exists + list_exising_users = User.objects.filter(username=self.data['username']) + + if len(list_exising_users) == 0: + self.user = User( + first_name=self.data['first_name'], + last_name=self.data['last_name'], + email=self.data['email'], + username=self.data['username'], + # optional: + # dataset_id=self.data['dataset_id'] + ) + p = str(self.data['password']) + self.user.set_password(p) + self.user.save() + + self.add_user_to_pd_man_researcher_group() + self.user.save() + else: + raise Exception("A user with username %s already exists" % self.data['username']) + + def create_workflow(self): + # STEP 2 : Create a workflow + + json_data = json.loads(open("workflows/pdmanager/predefined_workflow.json").read()) + + w = Workflow() + w.user = self.user + w.public = False + w.import_from_json(json_data,{},{}) + w.name = "PD_Manager Workflow - Data analysis - %s" % (self.user.username) + + w.save() + + return w.id + + +class RegisterPdManUserRESTView(APIView): + """ + Integration with PD_Manager Researcher's App: Registers a new PD_Manager user and creates a workflow + """ + + def post(self, request, *args, **kw): + if request.user.is_authenticated() and is_pd_man_admin(request.user): + # see custom DRF permission + + # check size of group [PD_Manager_Researcher] + n = len(Group.objects.get(name='PD_Manager_Researcher').user_set.all()) + print("Num [PD_Manager_Researcher] users: %d" % n) + if len(Group.objects.get(name='PD_Manager_Researcher').user_set.all()) > 200: + response = Response({'detail': 'Too many researchers registered'}, status=status.HTTP_303_SEE_OTHER) + return response + + data = {'username': request.POST.get('username', None), + 'password': request.POST.get('password', None), + 'first_name': request.POST.get('first_name', None), + 'last_name': request.POST.get('last_name', None), + 'email': request.POST.get('email', None)} + + registerPdManUserClass = RegisterPdManUser(data) + registerPdManUserClass.do_register() + workflow_id = registerPdManUserClass.create_workflow() + + response = Response({'workflow_id': workflow_id}, status=status.HTTP_200_OK) + else: + response = Response({'detail': 'Not authorized'}, status=status.HTTP_401_UNAUTHORIZED) + + return response + + +class DatasetCreatedPdManRESTView(APIView): + """ + Integration with PD_Manager Researcher's App: Update the id of the dataset + """ + def post(self, request, *args, **kw): + if request.user.is_authenticated() and is_pd_man_admin(request.user): + # See custom DRF permission + + # Find the workflow, find the widget [Import PD_Manager data] and update its setting + + data = {'username': request.POST.get('username', None), + 'workflow_id': request.POST.get('workflow_id', None)} + + registerPdManUserClass = RegisterPdManUser(data) + registerPdManUserClass.do_register() + workflow_id = registerPdManUserClass.create_empty_workflow() + + response = Response({'workflow_id': workflow_id}, status=status.HTTP_200_OK) + else: + response = Response({}, status=status.HTTP_401_UNAUTHORIZED) + + return response + + +def login_and_edit_workflow(request, workflow_id, username, password): + + # data = {'username': request.POST.get('username', None), + # 'password': request.POST.get('password', None), + # 'workflow_id': request.POST.get('workflow_id', None)} + + data = {'username': username, + 'password': password, + 'workflow_id': workflow_id} + + # workflow_id = request.POST.get("workflow_id") + + # expect and check username and password od [PD_Man_res] user.. + from views_integration import is_pd_man_researcher + + list_users = User.objects.filter(username=data['username']) + if not(len(list_users) == 1): + raise Exception("User not found") + user = list_users[0] + + if user.check_password(data['password']): + if is_pd_man_researcher(user): + + list_workflows = Workflow.objects.filter(id=workflow_id, user__username=data['username']) + + if not(len(list_workflows) == 1): + raise Exception("Wrong workflow ID") + + workflow = list_workflows[0] + if workflow: + user = workflow.user + + user.backend = settings.AUTHENTICATION_BACKENDS[0] + from django.contrib.auth import login + login(request, user) + + user.userprofile.active_workflow = workflow + user.userprofile.save() + return redirect('editor') From 9a1b51a6e2b03ba0f1ab2b702e1c41b517860e46 Mon Sep 17 00:00:00 2001 From: Darko Aleksovski Date: Wed, 1 Feb 2017 10:21:25 +0100 Subject: [PATCH 2/6] Modifed code for API View, which updates pdmanager dataset; Refactored code for pdmanager package --- workflows/api_urls.py | 1 + workflows/pdmanager/library.py | 16 +++++++++ workflows/urls.py | 1 - workflows/views_integration.py | 60 ++++++++++++++++++++-------------- 4 files changed, 52 insertions(+), 26 deletions(-) create mode 100644 workflows/pdmanager/library.py diff --git a/workflows/api_urls.py b/workflows/api_urls.py index dcf0bfc..86ed687 100644 --- a/workflows/api_urls.py +++ b/workflows/api_urls.py @@ -14,6 +14,7 @@ urlpatterns = patterns('', # ---------------------- url(r'^register_pd_man_user[/]?$', views_integration.RegisterPdManUserRESTView.as_view(), name='register_pd_man_user'), + url(r'^modify_pd_man_dataset[/]?$', views_integration.ModifyPdManDatasetIdRESTView.as_view(), name='modify_pd_man_dataset_id'), # ---------------------- url(r'^', include(router.urls)), diff --git a/workflows/pdmanager/library.py b/workflows/pdmanager/library.py new file mode 100644 index 0000000..65e42c9 --- /dev/null +++ b/workflows/pdmanager/library.py @@ -0,0 +1,16 @@ +import json +import requests +from django.conf import settings + +def pdmanager_import_data(input_dict): + + dataset_id = input_dict['dataset_id'] + url = settings.PD_MANAGER_DB_URL + '/oauth/token' + response = requests.post(url, data={'username': settings.PD_MANAGER_DB_USERNAME, + 'password': settings.PD_MANAGER_DB_PASSWORD, + 'grant_type': 'password'}) + + acc_token = json.loads(str(response.text))['access_token'] + acc_token = str(acc_token) + + return {'data': [acc_token]} diff --git a/workflows/urls.py b/workflows/urls.py index 679e909..ae870ba 100755 --- a/workflows/urls.py +++ b/workflows/urls.py @@ -79,5 +79,4 @@ def set_package_url(name, value, package): # ---------------------- url(r'^login_and_edit_workflow/(?P[0-9]+)/(?P[0-9A-Za-z_]+)/(?P[0-9A-Za-z_]+)/$', 'workflows.views_integration.login_and_edit_workflow', name='login_edit_workflow'), # ---------------------- - ) diff --git a/workflows/views_integration.py b/workflows/views_integration.py index bc23808..a45863b 100644 --- a/workflows/views_integration.py +++ b/workflows/views_integration.py @@ -7,7 +7,7 @@ from rest_framework.response import Response from rest_framework.views import APIView -from .models import Workflow +from .models import Workflow, Widget, AbstractWidget, Input def is_pd_man_admin(user): @@ -78,7 +78,7 @@ class RegisterPdManUserRESTView(APIView): def post(self, request, *args, **kw): if request.user.is_authenticated() and is_pd_man_admin(request.user): - # see custom DRF permission + # see custom DRF permissions # check size of group [PD_Manager_Researcher] n = len(Group.objects.get(name='PD_Manager_Researcher').user_set.all()) @@ -104,26 +104,34 @@ def post(self, request, *args, **kw): return response -class DatasetCreatedPdManRESTView(APIView): +class ModifyPdManDatasetIdRESTView(APIView): """ Integration with PD_Manager Researcher's App: Update the id of the dataset """ def post(self, request, *args, **kw): + aw = AbstractWidget.objects.filter(name='Import PD_Manager data', package='pdmanager')[0] + if request.user.is_authenticated() and is_pd_man_admin(request.user): - # See custom DRF permission + # See custom DRF permissions + + data = {'dataset_id': request.POST.get('dataset_id', None), + 'workflow_id': request.POST.get('workflow_id', None)} # Find the workflow, find the widget [Import PD_Manager data] and update its setting + wf = Workflow.objects.get(id=data['workflow_id']) + if wf: + import_data_widget = Widget.objects.filter(workflow__id=wf.id, abstract_widget=aw)[0] - data = {'username': request.POST.get('username', None), - 'workflow_id': request.POST.get('workflow_id', None)} + inp_param_dataset_id = Input.objects.filter(widget=import_data_widget)[0] + inp_param_dataset_id.value = data['dataset_id'] + inp_param_dataset_id.save() - registerPdManUserClass = RegisterPdManUser(data) - registerPdManUserClass.do_register() - workflow_id = registerPdManUserClass.create_empty_workflow() + response = Response({'detail': 'OK'}, status=status.HTTP_200_OK) + else: + response = Response({'detail': 'Wrong workflow ID'}, status=status.HTTP_401_UNAUTHORIZED) - response = Response({'workflow_id': workflow_id}, status=status.HTTP_200_OK) else: - response = Response({}, status=status.HTTP_401_UNAUTHORIZED) + response = Response({'detail': 'Authentication/authorization problem'}, status=status.HTTP_401_UNAUTHORIZED) return response @@ -138,26 +146,18 @@ def login_and_edit_workflow(request, workflow_id, username, password): 'password': password, 'workflow_id': workflow_id} - # workflow_id = request.POST.get("workflow_id") - - # expect and check username and password od [PD_Man_res] user.. + # expect and check username and password of [PD_Man_res] user.. from views_integration import is_pd_man_researcher list_users = User.objects.filter(username=data['username']) - if not(len(list_users) == 1): - raise Exception("User not found") - user = list_users[0] - - if user.check_password(data['password']): - if is_pd_man_researcher(user): + if len(list_users) >= 1: + user = list_users[0] + if user.check_password(data['password']) and is_pd_man_researcher(user): list_workflows = Workflow.objects.filter(id=workflow_id, user__username=data['username']) + if len(list_workflows) >= 1: + workflow = list_workflows[0] - if not(len(list_workflows) == 1): - raise Exception("Wrong workflow ID") - - workflow = list_workflows[0] - if workflow: user = workflow.user user.backend = settings.AUTHENTICATION_BACKENDS[0] @@ -167,3 +167,13 @@ def login_and_edit_workflow(request, workflow_id, username, password): user.userprofile.active_workflow = workflow user.userprofile.save() return redirect('editor') + else: + response = Response({'detail': 'Wrong workflow ID'}, status=status.HTTP_303_SEE_OTHER) + + else: + response = Response({'detail': 'Authentication/authorization problem'}, status=status.HTTP_303_SEE_OTHER) + + else: + response = Response({'detail': 'User not found'}, status=status.HTTP_303_SEE_OTHER) + + return response From baba92f1155fb603a27dbc3f6138c022309fea07 Mon Sep 17 00:00:00 2001 From: Darko Aleksovski Date: Thu, 2 Mar 2017 12:25:54 +0100 Subject: [PATCH 3/6] Modifications --- workflows/api_urls.py | 12 ++-- workflows/pdmanager/library.py | 23 +++++- workflows/views_integration.py | 126 ++++++++++++++++++++------------- 3 files changed, 103 insertions(+), 58 deletions(-) diff --git a/workflows/api_urls.py b/workflows/api_urls.py index 86ed687..4005955 100644 --- a/workflows/api_urls.py +++ b/workflows/api_urls.py @@ -12,11 +12,9 @@ urlpatterns = patterns('', - # ---------------------- - url(r'^register_pd_man_user[/]?$', views_integration.RegisterPdManUserRESTView.as_view(), name='register_pd_man_user'), - url(r'^modify_pd_man_dataset[/]?$', views_integration.ModifyPdManDatasetIdRESTView.as_view(), name='modify_pd_man_dataset_id'), - # ---------------------- + url(r'^register_user[/]?$', views_integration.RegisterUserAndCreateWorkflowAPIView.as_view(), name='register_user'), + url(r'^modify_widget[/]?$', views_integration.ModifyWidgetRESTView.as_view(), name='modify_widget'), - url(r'^', include(router.urls)), - url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) -) + url(r'^', include(router.urls)), + url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) + ) diff --git a/workflows/pdmanager/library.py b/workflows/pdmanager/library.py index 65e42c9..ca3e776 100644 --- a/workflows/pdmanager/library.py +++ b/workflows/pdmanager/library.py @@ -1,6 +1,7 @@ import json -import requests +import requests, urllib from django.conf import settings +import pandas as pd, numpy as np def pdmanager_import_data(input_dict): @@ -14,3 +15,23 @@ def pdmanager_import_data(input_dict): acc_token = str(acc_token) return {'data': [acc_token]} + + +def convert_date_to_unix(y_m_d_tuple): + # dti = pd.DatetimeIndex( [ pd.datetime(2016,5,5) ]) #.astype(np.int64) + + y,m,d = (y_m_d_tuple[0], y_m_d_tuple[1], y_m_d_tuple[2]) + + dti = pd.DatetimeIndex( [ pd.datetime(y,m,d) ]) + a = dti.astype(np.int64) // 10**6 + # print a + return a[0] + +def convert_from_unix_to_datetime(a): + b = pd.to_datetime(a / 1000.0, unit='s') + print b[0] + return b[0] + + + + diff --git a/workflows/views_integration.py b/workflows/views_integration.py index a45863b..f956cb2 100644 --- a/workflows/views_integration.py +++ b/workflows/views_integration.py @@ -6,6 +6,9 @@ from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework.permissions import BasePermission + +from rest_framework.decorators import permission_classes from .models import Workflow, Widget, AbstractWidget, Input @@ -16,18 +19,20 @@ def is_pd_man_admin(user): def is_pd_man_researcher(user): return user.groups.filter(name='PD_Manager_Researcher').exists() - -class RegisterPdManUser(object): +# ==================== +# Helper classes +# ==================== +class RegisterUserAndCreateWorkflow(object): """ - Helper class for registering a new PD_Manager user and creating a workflow + Helper class for registering a new user and creating a workflow """ def __init__(self, data): self.data = data pass - def add_user_to_pd_man_researcher_group(self): - g = Group.objects.get(name='PD_Manager_Researcher') + def add_user_to_group(self, group_name): + g = Group.objects.get(name=group_name) g.user_set.add(self.user) g.save() @@ -50,11 +55,13 @@ def do_register(self): self.user.set_password(p) self.user.save() - self.add_user_to_pd_man_researcher_group() + if self.data['group'] is not None: + self.add_user_to_group(self.data['group']) self.user.save() else: raise Exception("A user with username %s already exists" % self.data['username']) + def create_workflow(self): # STEP 2 : Create a workflow @@ -70,72 +77,91 @@ def create_workflow(self): return w.id +# ==================== +# Permissions +# ==================== + +class RegisterUserPermission(BasePermission): + + def has_permission(self, request, view): + + return 'auth.add_user' in request.user.get_all_permissions() + # return request.user.is_authenticated() and 'auth.add_user' in request.user.get_all_permissions() -class RegisterPdManUserRESTView(APIView): + +# ==================== +# API Views +# ==================== + +class RegisterUserAndCreateWorkflowAPIView(APIView): """ - Integration with PD_Manager Researcher's App: Registers a new PD_Manager user and creates a workflow + An API view which registers a new user, puts it in a given group, and creates its first workflow """ + permission_classes = (RegisterUserPermission, ) + def post(self, request, *args, **kw): - if request.user.is_authenticated() and is_pd_man_admin(request.user): - # see custom DRF permissions - - # check size of group [PD_Manager_Researcher] - n = len(Group.objects.get(name='PD_Manager_Researcher').user_set.all()) - print("Num [PD_Manager_Researcher] users: %d" % n) - if len(Group.objects.get(name='PD_Manager_Researcher').user_set.all()) > 200: - response = Response({'detail': 'Too many researchers registered'}, status=status.HTTP_303_SEE_OTHER) - return response - - data = {'username': request.POST.get('username', None), - 'password': request.POST.get('password', None), - 'first_name': request.POST.get('first_name', None), - 'last_name': request.POST.get('last_name', None), - 'email': request.POST.get('email', None)} - - registerPdManUserClass = RegisterPdManUser(data) - registerPdManUserClass.do_register() - workflow_id = registerPdManUserClass.create_workflow() - - response = Response({'workflow_id': workflow_id}, status=status.HTTP_200_OK) - else: - response = Response({'detail': 'Not authorized'}, status=status.HTTP_401_UNAUTHORIZED) + + data = {'username': request.POST.get('username', None), + 'password': request.POST.get('password', None), + 'first_name': request.POST.get('first_name', None), + 'last_name': request.POST.get('last_name', None), + 'email': request.POST.get('email', None), + 'group': request.POST.get('group', None)} #'PD_Manager_Researcher' + + # check size of group + n = len(Group.objects.get(name=data['group']).user_set.all()) + print("Num [%s] users: %d" % (data['group'], n)) + if n > 200: + response = Response({'detail': 'Too many users in group'}, status=status.HTTP_303_SEE_OTHER) + return response + + registerHelperClass = RegisterUserAndCreateWorkflow(data) + registerHelperClass.do_register() + workflow_id = registerHelperClass.create_workflow() + + response = Response({'workflow_id': workflow_id}, status=status.HTTP_200_OK) return response -class ModifyPdManDatasetIdRESTView(APIView): +class ModifyWidgetRESTView(APIView): """ - Integration with PD_Manager Researcher's App: Update the id of the dataset + An API view which modifies one widget of a workflow """ - def post(self, request, *args, **kw): - aw = AbstractWidget.objects.filter(name='Import PD_Manager data', package='pdmanager')[0] - if request.user.is_authenticated() and is_pd_man_admin(request.user): - # See custom DRF permissions + permission_classes = (RegisterUserPermission, ) - data = {'dataset_id': request.POST.get('dataset_id', None), - 'workflow_id': request.POST.get('workflow_id', None)} + def post(self, request, *args, **kw): - # Find the workflow, find the widget [Import PD_Manager data] and update its setting - wf = Workflow.objects.get(id=data['workflow_id']) - if wf: - import_data_widget = Widget.objects.filter(workflow__id=wf.id, abstract_widget=aw)[0] + data = {'widget_name': request.POST.get('widget_name', None), + 'workflow_id': request.POST.get('workflow_id', None), + 'dataset_id': request.POST.get('dataset_id', None)} - inp_param_dataset_id = Input.objects.filter(widget=import_data_widget)[0] - inp_param_dataset_id.value = data['dataset_id'] - inp_param_dataset_id.save() + aw = AbstractWidget.objects.filter(name='Import PD_Manager data', package='pdmanager')[0] - response = Response({'detail': 'OK'}, status=status.HTTP_200_OK) - else: - response = Response({'detail': 'Wrong workflow ID'}, status=status.HTTP_401_UNAUTHORIZED) + # Find the workflow, find the widget [Import PD_Manager data] and update its setting + wf = Workflow.objects.get(id=data['workflow_id']) + if wf: + import_data_widget = Widget.objects.filter(workflow__id=wf.id, abstract_widget=aw)[0] + + # TODO Filter input by name ? + inp_param_dataset_id = Input.objects.filter(widget=import_data_widget)[0] + inp_param_dataset_id.value = data['dataset_id'] + inp_param_dataset_id.save() + response = Response({'detail': 'OK'}, status=status.HTTP_200_OK) else: - response = Response({'detail': 'Authentication/authorization problem'}, status=status.HTTP_401_UNAUTHORIZED) + response = Response({'detail': 'Wrong workflow ID'}, status=status.HTTP_401_UNAUTHORIZED) + return response +# ==================== +# Views +# ==================== + def login_and_edit_workflow(request, workflow_id, username, password): # data = {'username': request.POST.get('username', None), From 8891fa8aef78a2b8eb1f0d28a287a7beb6097718 Mon Sep 17 00:00:00 2001 From: Darko Aleksovski Date: Mon, 14 May 2018 08:30:07 +0200 Subject: [PATCH 4/6] Modifications of integration view and url --- workflows/api_urls.py | 3 +- workflows/urls.py | 4 - workflows/views_integration.py | 180 ++++----------------------------- 3 files changed, 21 insertions(+), 166 deletions(-) diff --git a/workflows/api_urls.py b/workflows/api_urls.py index 4005955..b385706 100644 --- a/workflows/api_urls.py +++ b/workflows/api_urls.py @@ -12,8 +12,7 @@ urlpatterns = patterns('', - url(r'^register_user[/]?$', views_integration.RegisterUserAndCreateWorkflowAPIView.as_view(), name='register_user'), - url(r'^modify_widget[/]?$', views_integration.ModifyWidgetRESTView.as_view(), name='modify_widget'), + url(r'^create_workflow[/]?$', views_integration.CreateWorkflowAPIView.as_view(), name='create_workflow'), url(r'^', include(router.urls)), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) diff --git a/workflows/urls.py b/workflows/urls.py index ae870ba..5865c52 100755 --- a/workflows/urls.py +++ b/workflows/urls.py @@ -75,8 +75,4 @@ def set_package_url(name, value, package): url(r'^workflow_results/(?P[0-9]+)/$', 'workflows.views.workflow_results', name='workflow_results'), - - # ---------------------- - url(r'^login_and_edit_workflow/(?P[0-9]+)/(?P[0-9A-Za-z_]+)/(?P[0-9A-Za-z_]+)/$', 'workflows.views_integration.login_and_edit_workflow', name='login_edit_workflow'), - # ---------------------- ) diff --git a/workflows/views_integration.py b/workflows/views_integration.py index f956cb2..741d6a2 100644 --- a/workflows/views_integration.py +++ b/workflows/views_integration.py @@ -1,69 +1,29 @@ import json from django.conf import settings -from django.contrib.auth.models import User, Group +from django.contrib.auth.models import User from django.shortcuts import redirect -from rest_framework import status -from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework.permissions import BasePermission -from rest_framework.decorators import permission_classes +from .models import Workflow -from .models import Workflow, Widget, AbstractWidget, Input - - -def is_pd_man_admin(user): - return user.groups.filter(name='PD_Manager_Admin').exists() - -def is_pd_man_researcher(user): - return user.groups.filter(name='PD_Manager_Researcher').exists() # ==================== # Helper classes # ==================== -class RegisterUserAndCreateWorkflow(object): +class CreateWorkflow(object): """ - Helper class for registering a new user and creating a workflow + Helper class for creating a workflow for an existing CF user """ - def __init__(self, data): - self.data = data + def __init__(self, username): + self.username = username + u_l = User.objects.filter(username=username) + self.user = u_l[0] pass - def add_user_to_group(self, group_name): - g = Group.objects.get(name=group_name) - g.user_set.add(self.user) - g.save() - - def do_register(self): - # STEP 1 : Create a user - - # Check if this user exists - list_exising_users = User.objects.filter(username=self.data['username']) - - if len(list_exising_users) == 0: - self.user = User( - first_name=self.data['first_name'], - last_name=self.data['last_name'], - email=self.data['email'], - username=self.data['username'], - # optional: - # dataset_id=self.data['dataset_id'] - ) - p = str(self.data['password']) - self.user.set_password(p) - self.user.save() - - if self.data['group'] is not None: - self.add_user_to_group(self.data['group']) - self.user.save() - else: - raise Exception("A user with username %s already exists" % self.data['username']) - - def create_workflow(self): - # STEP 2 : Create a workflow + # Create a workflow json_data = json.loads(open("workflows/pdmanager/predefined_workflow.json").read()) @@ -77,129 +37,29 @@ def create_workflow(self): return w.id -# ==================== -# Permissions -# ==================== - -class RegisterUserPermission(BasePermission): - - def has_permission(self, request, view): - - return 'auth.add_user' in request.user.get_all_permissions() - # return request.user.is_authenticated() and 'auth.add_user' in request.user.get_all_permissions() - # ==================== # API Views # ==================== -class RegisterUserAndCreateWorkflowAPIView(APIView): +class CreateWorkflowAPIView(APIView): """ - An API view which registers a new user, puts it in a given group, and creates its first workflow + An API view which creates a predifened workflow """ - permission_classes = (RegisterUserPermission, ) - - def post(self, request, *args, **kw): - - data = {'username': request.POST.get('username', None), - 'password': request.POST.get('password', None), - 'first_name': request.POST.get('first_name', None), - 'last_name': request.POST.get('last_name', None), - 'email': request.POST.get('email', None), - 'group': request.POST.get('group', None)} #'PD_Manager_Researcher' - - # check size of group - n = len(Group.objects.get(name=data['group']).user_set.all()) - print("Num [%s] users: %d" % (data['group'], n)) - if n > 200: - response = Response({'detail': 'Too many users in group'}, status=status.HTTP_303_SEE_OTHER) - return response - - registerHelperClass = RegisterUserAndCreateWorkflow(data) - registerHelperClass.do_register() - workflow_id = registerHelperClass.create_workflow() - - response = Response({'workflow_id': workflow_id}, status=status.HTTP_200_OK) - - return response - - -class ModifyWidgetRESTView(APIView): - """ - An API view which modifies one widget of a workflow - """ - - permission_classes = (RegisterUserPermission, ) - - def post(self, request, *args, **kw): - - data = {'widget_name': request.POST.get('widget_name', None), - 'workflow_id': request.POST.get('workflow_id', None), - 'dataset_id': request.POST.get('dataset_id', None)} - - aw = AbstractWidget.objects.filter(name='Import PD_Manager data', package='pdmanager')[0] - - # Find the workflow, find the widget [Import PD_Manager data] and update its setting - wf = Workflow.objects.get(id=data['workflow_id']) - if wf: - import_data_widget = Widget.objects.filter(workflow__id=wf.id, abstract_widget=aw)[0] - - # TODO Filter input by name ? - inp_param_dataset_id = Input.objects.filter(widget=import_data_widget)[0] - inp_param_dataset_id.value = data['dataset_id'] - inp_param_dataset_id.save() - - response = Response({'detail': 'OK'}, status=status.HTTP_200_OK) - else: - response = Response({'detail': 'Wrong workflow ID'}, status=status.HTTP_401_UNAUTHORIZED) - - - return response - - -# ==================== -# Views -# ==================== - -def login_and_edit_workflow(request, workflow_id, username, password): - - # data = {'username': request.POST.get('username', None), - # 'password': request.POST.get('password', None), - # 'workflow_id': request.POST.get('workflow_id', None)} - - data = {'username': username, - 'password': password, - 'workflow_id': workflow_id} - - # expect and check username and password of [PD_Man_res] user.. - from views_integration import is_pd_man_researcher - - list_users = User.objects.filter(username=data['username']) - if len(list_users) >= 1: - user = list_users[0] - if user.check_password(data['password']) and is_pd_man_researcher(user): - list_workflows = Workflow.objects.filter(id=workflow_id, user__username=data['username']) - if len(list_workflows) >= 1: - workflow = list_workflows[0] + def get(self, request, *args, **kw): - user = workflow.user + data = {'secret': request.GET.get('secret', None)} + if data['secret'] == settings.PD_MANAGER_SECRET: + username = settings.PD_MANAGER_CF_USERNAME - user.backend = settings.AUTHENTICATION_BACKENDS[0] - from django.contrib.auth import login - login(request, user) + registerHelperClass = CreateWorkflow(username) + workflow_id = registerHelperClass.create_workflow() - user.userprofile.active_workflow = workflow - user.userprofile.save() - return redirect('editor') - else: - response = Response({'detail': 'Wrong workflow ID'}, status=status.HTTP_303_SEE_OTHER) + new_workflow = Workflow.objects.get(id=workflow_id) - else: - response = Response({'detail': 'Authentication/authorization problem'}, status=status.HTTP_303_SEE_OTHER) + # here - else: - response = Response({'detail': 'User not found'}, status=status.HTTP_303_SEE_OTHER) + return redirect(new_workflow.get_absolute_url()) - return response From 9db85058b7b4b4c41cedff16657611280cc8bf24 Mon Sep 17 00:00:00 2001 From: Darko Aleksovski Date: Wed, 16 May 2018 06:39:53 +0200 Subject: [PATCH 5/6] Modifications to dataset parsing code --- workflows/pdmanager/library.py | 191 ++++++++++++++++-- .../6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json | 27 +-- 2 files changed, 176 insertions(+), 42 deletions(-) diff --git a/workflows/pdmanager/library.py b/workflows/pdmanager/library.py index ca3e776..0aa26c5 100644 --- a/workflows/pdmanager/library.py +++ b/workflows/pdmanager/library.py @@ -1,37 +1,188 @@ +import datetime import json -import requests, urllib +import re + +import numpy as np +import pandas as pd +import requests from django.conf import settings -import pandas as pd, numpy as np -def pdmanager_import_data(input_dict): - dataset_id = input_dict['dataset_id'] - url = settings.PD_MANAGER_DB_URL + '/oauth/token' - response = requests.post(url, data={'username': settings.PD_MANAGER_DB_USERNAME, - 'password': settings.PD_MANAGER_DB_PASSWORD, - 'grant_type': 'password'}) + +def auth(): + us,pa=(settings.PD_MANAGER_DB_USERNAME, settings.PD_MANAGER_DB_PASSWORD) + PD_MANAGER_DB_URL = settings.PD_MANAGER_DB_URL + url = "%s/oauth/token" % PD_MANAGER_DB_URL + + response = requests.post(url, + data={'username':us, 'password':pa, 'grant_type':'password'}) acc_token = json.loads(str(response.text))['access_token'] acc_token = str(acc_token) - return {'data': [acc_token]} + headers_acc_token = {"Authorization": "bearer %s"%(acc_token)} + return headers_acc_token + + +def pandas_dataframe_to_arff(df): + import arff + + d = {'relation': 'aaaa', 'attributes':[]} + data_tr = [] + + # attributes and data_tr + for j,c in enumerate(df.columns): + if df[c].dtype in [np.dtype('int64'), np.dtype('float64')]: + d['attributes'].append( (c, 'NUMERIC') ) + + else: + att_values0 = list(df[c].unique()) + att_values = [v for v in att_values0 if not(pd.isnull(v))] + + d['attributes'].append( (c, att_values) ) + + # handle missing values in column + col_data = ['?' if pd.isnull(el) else el for el in list(df[c])] + data_tr.append( col_data ) + + # transpose data (and np.transpose() does not work well with strings) + d['data'] = [] + for i in range(len(df)): + row = [] + for j in range(df.shape[1]): + row.append(data_tr[j][i]) + d['data'].append(row) + + arff_str = arff.dumps(d) + + return arff_str + + +def download_dataset(ds_id, h): + url_1 = 'https://pdmanager.3dnetmedical.com/api/datasets/%s'%ds_id + + response_1 = requests.get(url_1, headers=h) + print str(response_1) + + j = json.loads(response_1.text) + + return j + +def parse_observations(l_observations, column_names=None, pat_id=None, obs_type=None): + """ Parse observations + + :param pat_id: + :param obs_type: eg. finger_tapping, or gait, ... + :param l_observations: list of strings of format "1498941988645, 59.0, 168.47, 304.0, 0.0;" + :param column_names: names of rest of the columns (besides time) + :return: + """ + + df = [] + if len(l_observations)==0: + return [] + for l in l_observations: + if len(l.strip()) == 0: + break + l = l.strip() + if l.find(";")>=0: + l = l.split(";")[0] + df.append([float(v) for v in l.split(",")]) + + if column_names is None: + columns = ["time"] + ["var_{:02d}".format(j) for j in range(len(l.split(","))-1)] + else: + if len(l.split(",")) - 1 == len(column_names): + columns = ["time"] + column_names + else: + raise Exception("Observation parsing problem: Number of column names needed is {} while provided is {}!".format(len(l.split(",")) - 1, len(column_names))) + + df = pd.DataFrame(df, columns=columns) + df['time'] = df.apply(lambda r: datetime.datetime.fromtimestamp(r["time"] / 1000.0), axis=1) + df['pat_id'] = pat_id + df['obs_type'] = obs_type + + # print len(df), "obs in ", df.time.min().date(), df.time.min().date(), "\n\n" + + # transform to long format + df = pd.melt(df, id_vars="pat_id time obs_type".split(), var_name="variable", value_name="value") + + return df + +def convert(name): + s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() + + + +def pdmanager_import_dataset(input_dict): + """Import dataset from the main database""" + + # --- dataset id, target variable and predictor variables --------------- + d_id = settings.PD_MANAGER_DATASET_ID + target, target_val_prefix = ('gait_var_00', 'Gait') + + # non_motor + l_nm = "fingerTappingSimple fingerTappingAlternate".split() + + # motor + l_m = ["gait"] + # ------------------------------------------------------------------------ + + dict_json = download_dataset(d_id, auth()) + + + num_p = len(dict_json["Values"]) + print("Parsing data for {} patients".format(num_p)) + + df = [] + if num_p >= 2: + for j, v in enumerate(dict_json["Values"]): # for each patient + pat_id = "pat_{:02d}_{}_{}".format(j, v["PatientInfo"]["gender"].replace(" ", ""), + v["PatientInfo"]["age"].replace(" ", "")) + + # non_motor + if len(v["NonMotor"].keys()) > 0: + for t in l_nm: + df_ = parse_observations(v["NonMotor"][t], pat_id=pat_id, obs_type=convert(t)) + df = pd.concat([df, df_]) if len(df) > 0 else df_ + + # motor + if len(v["Motor"].keys()) > 0: + for t in l_m: + df_ = parse_observations(v["Motor"][t], pat_id=pat_id, obs_type=convert(t)) + df = pd.concat([df, df_]) if len(df) > 0 else df_ + + df = df.sort("pat_id time obs_type variable".split()).reset_index(drop=1) + + # non_m and m data on same day + df['date'] = df['time'].dt.date + df_ = df.groupby("pat_id date obs_type variable".split()).value.max().reset_index() + df_['obs_type_var'] = df_['obs_type'] + "_" + df_['variable'] + df_['pat_date'] = df_.apply(lambda r: r['pat_id'] + "_" + str(r['date']), axis=1) -def convert_date_to_unix(y_m_d_tuple): - # dti = pd.DatetimeIndex( [ pd.datetime(2016,5,5) ]) #.astype(np.int64) + df_ = df_.pivot(index="pat_date", columns="obs_type_var", values="value") - y,m,d = (y_m_d_tuple[0], y_m_d_tuple[1], y_m_d_tuple[2]) + # target must not have nulls - drop all such rows + df_ = df_.dropna(subset=[target]) + df_.ix[df_[target] == 0.0, target] = '{}_0'.format(target_val_prefix) + df_.ix[df_[target] == 1.0, target] = '{}_1'.format(target_val_prefix) + df_.ix[df_[target] == 2.0, target] = '{}_2'.format(target_val_prefix) + df_.ix[df_[target] == 3.0, target] = '{}_3'.format(target_val_prefix) + df_.ix[df_[target] == 4.0, target] = '{}_4'.format(target_val_prefix) - dti = pd.DatetimeIndex( [ pd.datetime(y,m,d) ]) - a = dti.astype(np.int64) // 10**6 - # print a - return a[0] + num_predictors = df_.shape[1] - 1 -def convert_from_unix_to_datetime(a): - b = pd.to_datetime(a / 1000.0, unit='s') - print b[0] - return b[0] + # drop rows for which we have no non_m data + df_['num_nulls'] = df_.transpose().isnull().sum() + df_ = df_[df_.num_nulls < num_predictors] + df_ = df_.drop(['num_nulls'], axis=1) + arff_str = pandas_dataframe_to_arff(df_) + with open("pdm_sample_dataset_%3d_rows.arff"%len(df), "w") as f: + f.write(arff_str) + return {'arff_data': str(arff_str)} diff --git a/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json b/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json index a069bc8..552a5bd 100644 --- a/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json +++ b/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json @@ -10,9 +10,9 @@ "interaction_view": "", "has_progress_bar": false, "image": "", - "description": "Imports data from the central database of the PD_Manager project.", + "description": "Imports data from the central database of the PD_Manager project.", "static_image": "construction_work .png", - "action": "pdmanager_import_data", + "action": "pdmanager_import_dataset", "visualization_view": "", "streaming_visualization_view": "", "post_interact_action": "", @@ -24,31 +24,14 @@ "name": "Import PD_Manager data" } }, - { - "model": "workflows.abstractinput", - "fields": { - "widget": "6ca99fb5-e8c4-4649-80e1-7e1872e2b819", - "name": "dataset_id", - "short_name": "id", - "default": "", - "description": "The ID of the PD_Manager dataset.", - "required": true, - "multi": false, - "parameter_type": "text", - "variable": "dataset_id", - "parameter": true, - "order": 1, - "uid": "0d9c056c-80bf-40c4-98f7-eeabb6e1fdf1" - } - }, { "model": "workflows.abstractoutput", "fields": { "widget": "6ca99fb5-e8c4-4649-80e1-7e1872e2b819", "name": "Imported Data", - "short_name": "dat", - "description": "Imported data", - "variable": "data", + "short_name": "arf", + "description": "Imported data in ARFF format", + "variable": "arff_data", "order": 1, "uid": "4670b2b1-7bf5-4516-8a20-759df02966ea" } From b8c36dc9cf344a3e3f6ac6d692512e3112cfc463 Mon Sep 17 00:00:00 2001 From: Darko Aleksovski Date: Sun, 20 May 2018 12:06:45 +0200 Subject: [PATCH 6/6] Modifications to integration view and logic, as well as predefined workfload loading --- workflows/pdmanager/library.py | 4 +- .../6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json | 19 +- workflows/pdmanager/predefined_workflow.json | 243 +++++++++++++++++- .../icons/treeview/construction_work .png | Bin 2375 -> 0 bytes .../pdmanager/icons/treeview/project_logo.png | Bin 0 -> 4085 bytes .../icons/widget/construction_work .png | Bin 21353 -> 0 bytes .../pdmanager/icons/widget/project_logo.png | Bin 0 -> 4085 bytes workflows/views_integration.py | 17 +- 8 files changed, 262 insertions(+), 21 deletions(-) delete mode 100644 workflows/pdmanager/static/pdmanager/icons/treeview/construction_work .png create mode 100644 workflows/pdmanager/static/pdmanager/icons/treeview/project_logo.png delete mode 100644 workflows/pdmanager/static/pdmanager/icons/widget/construction_work .png create mode 100644 workflows/pdmanager/static/pdmanager/icons/widget/project_logo.png diff --git a/workflows/pdmanager/library.py b/workflows/pdmanager/library.py index 0aa26c5..5b45656 100644 --- a/workflows/pdmanager/library.py +++ b/workflows/pdmanager/library.py @@ -63,6 +63,8 @@ def download_dataset(ds_id, h): response_1 = requests.get(url_1, headers=h) print str(response_1) + if response_1.status_code != 200: + raise Exception("Error downloading data from central Db") j = json.loads(response_1.text) @@ -119,7 +121,7 @@ def pdmanager_import_dataset(input_dict): """Import dataset from the main database""" # --- dataset id, target variable and predictor variables --------------- - d_id = settings.PD_MANAGER_DATASET_ID + d_id = input_dict['ds_id'] target, target_val_prefix = ('gait_var_00', 'Gait') # non_motor diff --git a/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json b/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json index 552a5bd..3a3ba55 100644 --- a/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json +++ b/workflows/pdmanager/package_data/widgets/6ca99fb5-e8c4-4649-80e1-7e1872e2b819.json @@ -11,7 +11,7 @@ "has_progress_bar": false, "image": "", "description": "Imports data from the central database of the PD_Manager project.", - "static_image": "construction_work .png", + "static_image": "project_logo.png", "action": "pdmanager_import_dataset", "visualization_view": "", "streaming_visualization_view": "", @@ -24,6 +24,23 @@ "name": "Import PD_Manager data" } }, + { + "model": "workflows.abstractinput", + "fields": { + "widget": "6ca99fb5-e8c4-4649-80e1-7e1872e2b819", + "name": "dataset_id", + "short_name": "ds", + "default": "", + "description": "The ID of the dataset to be imported", + "required": true, + "multi": false, + "parameter_type": "text", + "variable": "ds_id", + "parameter": true, + "order": 1, + "uid": "91c0e707-5a95-4a5b-b061-4133d55f95fb" + } + }, { "model": "workflows.abstractoutput", "fields": { diff --git a/workflows/pdmanager/predefined_workflow.json b/workflows/pdmanager/predefined_workflow.json index b57f188..71552f6 100644 --- a/workflows/pdmanager/predefined_workflow.json +++ b/workflows/pdmanager/predefined_workflow.json @@ -1,22 +1,224 @@ { "widgets": [ + { + "inputs": [ + { + "name": "Params", + "short_name": "par", + "inner_output": null, + "multi_id": 0, + "required": false, + "value": "", + "options": [], + "parameter_type": "text", + "variable": "params", + "pk": 44, + "outer_output": null, + "parameter": true, + "order": 1, + "description": "" + } + ], + "name": "Decision Tree - J48", + "abstract_widget": "90e92fe2-5899-47da-bb5a-1f6fd5755629", + "workflow": null, + "outputs": [ + { + "name": "J48 Learner", + "short_name": "lrn", + "outer_input": null, + "variable": "J48_learner", + "pk": 36, + "inner_input": null, + "order": 1, + "description": "" + } + ], + "abstract_widget_package": "weka_local", + "y": 29, + "x": 30, + "type": "regular" + }, + { + "inputs": [ + { + "name": "Learner", + "short_name": "lea", + "inner_output": null, + "multi_id": 0, + "required": false, + "value": null, + "options": [], + "parameter_type": null, + "variable": "learner", + "pk": 45, + "outer_output": null, + "parameter": false, + "order": 1, + "description": "used to build the classifier.\r\n\r\nProvided by:\r\na Weka Classification widget" + }, + { + "name": "Instances", + "short_name": "ins", + "inner_output": null, + "multi_id": 0, + "required": false, + "value": null, + "options": [], + "parameter_type": null, + "variable": "instances", + "pk": 46, + "outer_output": null, + "parameter": false, + "order": 2, + "description": "to be used by the provided learner to create a classifier.\r\n\r\nProvided by:\r\nArff to Weka Instances" + } + ], + "name": "Build Classifier", + "abstract_widget": "4ff5974f-495a-424f-ba5c-c86afa766825", + "workflow": null, + "outputs": [ + { + "name": "Classifier", + "short_name": "cla", + "outer_input": null, + "variable": "classifier", + "pk": 37, + "inner_input": null, + "order": 1, + "description": "based on the provided data and learner.\r\n\r\nUsed by:\r\nWeka Apply Classifier" + } + ], + "abstract_widget_package": "weka_local", + "y": 53, + "x": 511, + "type": "regular" + }, + { + "inputs": [ + { + "name": "Classifier", + "short_name": "cls", + "inner_output": null, + "multi_id": 0, + "required": false, + "value": null, + "options": [], + "parameter_type": null, + "variable": "classifier", + "pk": 47, + "outer_output": null, + "parameter": false, + "order": 1, + "description": "" + }, + { + "name": "Image type", + "short_name": "imt", + "inner_output": null, + "multi_id": 0, + "required": false, + "value": "vector", + "options": [ + { + "name": "Raster image (PNG file)", + "value": "raster" + }, + { + "name": "Vector image (SVG file)", + "value": "vector" + } + ], + "parameter_type": "select", + "variable": "img_type", + "pk": 48, + "outer_output": null, + "parameter": true, + "order": 1, + "description": "Whether to create a raster (PNG) or vector (SVG) image" + } + ], + "name": "Display WEKA Decision Tree", + "abstract_widget": "3a2411b7-9e72-418d-81b1-4009e714e46b", + "workflow": null, + "outputs": [], + "abstract_widget_package": "weka_local", + "y": 52, + "x": 758, + "type": "regular" + }, + { + "inputs": [ + { + "name": "arff file", + "short_name": "arf", + "inner_output": null, + "multi_id": 0, + "required": false, + "value": null, + "options": [], + "parameter_type": null, + "variable": "arff", + "pk": 49, + "outer_output": null, + "parameter": false, + "order": 1, + "description": "" + }, + { + "name": "Class index", + "short_name": "cla", + "inner_output": null, + "multi_id": 0, + "required": false, + "value": "", + "options": [], + "parameter_type": "text", + "variable": "class_index", + "pk": 50, + "outer_output": null, + "parameter": true, + "order": 1, + "description": "" + } + ], + "name": "Arff to Weka Instances", + "abstract_widget": "50ea6b8c-c528-433b-9062-a62691902103", + "workflow": null, + "outputs": [ + { + "name": "Instances", + "short_name": "ins", + "outer_input": null, + "variable": "instances", + "pk": 38, + "inner_input": null, + "order": 1, + "description": "" + } + ], + "abstract_widget_package": "weka_local", + "y": 191, + "x": 283, + "type": "regular" + }, { "inputs": [ { "name": "dataset_id", - "short_name": "id", + "short_name": "ds", "inner_output": null, "multi_id": 0, "required": true, "value": "", "options": [], "parameter_type": "text", - "variable": "dataset_id", - "pk": 1, + "variable": "ds_id", + "pk": 63, "outer_output": null, "parameter": true, "order": 1, - "description": "The ID of the PD_Manager dataset." + "description": "The ID of the dataset to be imported" } ], "name": "Import PD_Manager data", @@ -25,22 +227,39 @@ "outputs": [ { "name": "Imported Data", - "short_name": "dat", + "short_name": "arf", "outer_input": null, - "variable": "data", - "pk": 1, + "variable": "arff_data", + "pk": 53, "inner_input": null, "order": 1, - "description": "Imported data" + "description": "Imported data in ARFF format" } ], "abstract_widget_package": "pdmanager", - "y": 40, - "x": 32, + "y": 230, + "x": 23, "type": "regular" } ], - "connections": [], - "name": "PD_Manager Workflow - Data analysis - pdm_112", + "connections": [ + { + "output_id": 38, + "input_id": 46 + }, + { + "output_id": 36, + "input_id": 45 + }, + { + "output_id": 37, + "input_id": 47 + }, + { + "output_id": 53, + "input_id": 49 + } + ], + "name": "PD_Manager Integration Workflow - user not defined", "description": "" } diff --git a/workflows/pdmanager/static/pdmanager/icons/treeview/construction_work .png b/workflows/pdmanager/static/pdmanager/icons/treeview/construction_work .png deleted file mode 100644 index 69bd351914a95f81eb1941f6e7908474916f6116..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2375 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 diff --git a/workflows/pdmanager/static/pdmanager/icons/treeview/project_logo.png b/workflows/pdmanager/static/pdmanager/icons/treeview/project_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2780f25c3da89e87c8257eee1ba4e74f2556a232 GIT binary patch literal 4085 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 literal 0 HcmV?d00001 diff --git a/workflows/pdmanager/static/pdmanager/icons/widget/construction_work .png b/workflows/pdmanager/static/pdmanager/icons/widget/construction_work .png deleted file mode 100644 index bc16d376995c1545972b60487ee8cd653177b407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21353 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 NtzXxDU0$F6e*i35z<>Y% diff --git a/workflows/pdmanager/static/pdmanager/icons/widget/project_logo.png b/workflows/pdmanager/static/pdmanager/icons/widget/project_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2780f25c3da89e87c8257eee1ba4e74f2556a232 GIT binary patch literal 4085 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 literal 0 HcmV?d00001 diff --git a/workflows/views_integration.py b/workflows/views_integration.py index 741d6a2..0ca53db 100644 --- a/workflows/views_integration.py +++ b/workflows/views_integration.py @@ -31,10 +31,14 @@ def create_workflow(self): w.user = self.user w.public = False w.import_from_json(json_data,{},{}) - w.name = "PD_Manager Workflow - Data analysis - %s" % (self.user.username) - + w.name = "PD_Manager Integration Workflow - %s" % (self.user.username) w.save() + w_import = w.widgets.filter(name__icontains="import")[0] + i_ds = w_import.inputs.all()[0] + i_ds.value = settings.PD_MANAGER_SECRET + i_ds.save() + return w.id @@ -47,19 +51,18 @@ class CreateWorkflowAPIView(APIView): An API view which creates a predifened workflow """ + authentication_classes = () + permission_classes = () def get(self, request, *args, **kw): + username = settings.PD_MANAGER_CF_USERNAME data = {'secret': request.GET.get('secret', None)} - if data['secret'] == settings.PD_MANAGER_SECRET: - username = settings.PD_MANAGER_CF_USERNAME + if data['secret'] == settings.PD_MANAGER_SECRET: registerHelperClass = CreateWorkflow(username) workflow_id = registerHelperClass.create_workflow() new_workflow = Workflow.objects.get(id=workflow_id) - # here - return redirect(new_workflow.get_absolute_url()) -