Skip to content

Commit

Permalink
refactor: Merge node listings to one endpoint
Browse files Browse the repository at this point in the history
Factors out some duplicate code.

PR #57 adds a `to_json()` method that might be able to replace the `extract_node_info()` method here. TBD.
  • Loading branch information
hcat-pge committed Apr 21, 2020
1 parent 8e27268 commit 896d3e6
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 55 deletions.
1 change: 1 addition & 0 deletions pyworkflow/pyworkflow/node_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def manipulation_node(node_key, node_info):
else:
return None


def custom_node(filename, node_key, node_info):
try:
package = __import__('custom_nodes.' + filename)
Expand Down
1 change: 0 additions & 1 deletion vp/vp/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
path('admin/', admin.site.urls),
path('info/', views.info),
path('nodes/', views.retrieve_nodes_for_user),
path('custom_nodes/', views.retrieve_custom_nodes_for_user),
path('node/', include('node.urls')),
path('workflow/', include('workflow.urls'))
]
116 changes: 62 additions & 54 deletions vp/vp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from rest_framework.decorators import api_view
from drf_yasg.utils import swagger_auto_schema
from pyworkflow import Node
from modulefinder import ModuleFinder

import os
import inspect
import sys


@swagger_auto_schema(method='get', responses={200:'JSON response with data'})
Expand Down Expand Up @@ -42,78 +44,84 @@ def retrieve_nodes_for_user(request):
"""
data = dict()

# Iterate through node 'types'
# Iterate through installed Nodes
for parent in Node.__subclasses__():
key = getattr(parent, "display_name", parent.__name__)
data[key] = list()

# Iterate through node 'keys'
for child in parent.__subclasses__():
# TODO: check attribute-scope is handled correctly
child_node = {
'name': child.name,
'node_key': child.__name__,
'node_type': parent.__name__,
'num_in': child.num_in,
'num_out': child.num_out,
'color': child.color or parent.color,
'doc': child.__doc__,
'options': {k: v.get_value() for k, v in child.options.items()},
'option_types': child.option_types,
'download_result': getattr(child, "download_result", False)
}

data[key].append(child_node)
node = extract_node_info(parent, child)
data[key].append(node)

# Check for any installed Custom Nodes
# TODO: Workflow loading excluded in middleware for this route
# Should probably have a way to access the 'custom_node` dir dynamically
custom_node_path = os.path.join(os.getcwd(), '../pyworkflow/custom_nodes')
data['CustomNode'] = import_custom_node(custom_node_path)

return JsonResponse(data)


@swagger_auto_schema(method='get',
operation_summary='Retrieve a list of custom Nodes',
operation_description='Retrieves a list of custom Nodes, in JSON.',
responses={
200: 'List of installed Nodes, in JSON',
})
@api_view(['GET'])
def retrieve_custom_nodes_for_user(request):
data = dict()
def check_missing_packages(node_path):
finder = ModuleFinder(node_path)
finder.run_script(node_path)

uninstalled = list()
for missing_package in finder.badmodules.keys():
if missing_package not in sys.modules:
uninstalled.append(missing_package)

return uninstalled


def extract_node_info(parent, child):
# TODO: check attribute(s) accessing is handled correctly
return {
'name': child.name,
'node_key': child.__name__,
'node_type': str(parent),
'num_in': child.num_in,
'num_out': child.num_out,
'color': child.color or parent.color or 'black',
'doc': child.__doc__,
'options': {k: v.get_value() for k, v in child.options.items()},
'option_types': child.option_types,
'download_result': getattr(child, "download_result", False)
}

# TODO: Workflow loading excluded in middleware for this route
# Should probably have a way to access the 'custom_node` dir dynamically
custom_node_path = os.path.join(os.getcwd(), '../pyworkflow/custom_nodes')

def import_custom_node(root_path):
# Get list of files in path
try:
nodes = os.listdir(custom_node_path)
files = os.listdir(root_path)
except OSError as e:
return JsonResponse({"message": str(e)}, status=500)
return None

for node in nodes:
# Parse file type
node_name, ext = os.path.splitext(node)
data = list()
for file in files:
# Check file is not a dir
node_path = os.path.join(root_path, file)
if not os.path.isfile(node_path):
continue

node, ext = os.path.splitext(file)

try:
package = __import__('custom_nodes.' + node_name)
module = getattr(package, node_name)
except ModuleNotFoundError as e:
# TODO: This will only catch the first missing package. Can we get more?
data[node_name] = f"Please install missing packages and restart the server. Missing '{e.name}'"
package = __import__('custom_nodes.' + node)
module = getattr(package, node)
except ModuleNotFoundError:
data.append({
"name": node,
"missing_packages": check_missing_packages(node_path)
})
continue

for name, klass in inspect.getmembers(module):
if inspect.isclass(klass) and klass.__module__.startswith('custom_nodes.'):
custom_node = {
'name': klass.name,
'node_key': name,
'node_type': node_name,
'num_in': klass.num_in,
'num_out': klass.num_out,
'color': klass.color or 'black',
'doc': klass.__doc__,
'options': {k: v.get_value() for k, v in klass.options.items()},
'option_types': klass.option_types,
'download_result': getattr(klass, "download_result", False)
}

data[node_name] = custom_node

return JsonResponse(data, safe=False)
custom_node = extract_node_info(node, klass)
data.append(custom_node)

return data


0 comments on commit 896d3e6

Please sign in to comment.