2020-03-03 20:30:42 +00:00
|
|
|
import importlib
|
|
|
|
import os
|
|
|
|
import pkgutil
|
|
|
|
|
2020-03-03 18:50:22 +00:00
|
|
|
from crc.api.common import ApiError
|
|
|
|
|
|
|
|
|
2020-04-08 17:39:42 +00:00
|
|
|
class Script(object):
|
2020-03-03 18:50:22 +00:00
|
|
|
""" Provides an abstract class that defines how scripts should work, this
|
|
|
|
must be extended in all Script Tasks."""
|
|
|
|
|
|
|
|
def get_description(self):
|
|
|
|
raise ApiError("invalid_script",
|
|
|
|
"This script does not supply a description.")
|
|
|
|
|
2020-05-24 20:13:15 +00:00
|
|
|
def do_task(self, task, study_id, workflow_id, **kwargs):
|
2020-03-03 18:50:22 +00:00
|
|
|
raise ApiError("invalid_script",
|
2020-03-27 12:29:31 +00:00
|
|
|
"This is an internal error. The script you are trying to execute '%s' " % self.__class__.__name__ +
|
2020-03-03 18:50:22 +00:00
|
|
|
"does not properly implement the do_task function.")
|
2020-03-03 20:30:42 +00:00
|
|
|
|
2020-05-24 20:13:15 +00:00
|
|
|
def do_task_validate_only(self, task, study_id, workflow_id, **kwargs):
|
2020-03-27 12:29:31 +00:00
|
|
|
raise ApiError("invalid_script",
|
|
|
|
"This is an internal error. The script you are trying to execute '%s' " % self.__class__.__name__ +
|
|
|
|
"does must provide a validate_only option that mimics the do_task, " +
|
|
|
|
"but does not make external calls or database updates." )
|
2020-07-24 18:33:24 +00:00
|
|
|
@staticmethod
|
|
|
|
def generate_augmented_list(task, study_id,workflow_id):
|
|
|
|
"""
|
|
|
|
this makes a dictionary of lambda functions that are closed over the class instance that
|
|
|
|
They represent. This is passed into PythonScriptParser as a list of helper functions that are
|
|
|
|
available for running. In general, they maintain the do_task call structure that they had, but
|
|
|
|
they always return a value rather than updating the task data.
|
|
|
|
|
|
|
|
We may be able to remove the task for each of these calls if we are not using it other than potentially
|
|
|
|
updating the task data.
|
|
|
|
"""
|
|
|
|
def make_closure(subclass,task,study_id,workflow_id):
|
|
|
|
instance = subclass()
|
|
|
|
return lambda *a : subclass.do_task(instance,task,study_id,workflow_id,*a)
|
|
|
|
execlist = {}
|
|
|
|
subclasses = Script.get_all_subclasses()
|
|
|
|
for x in range(len(subclasses)):
|
|
|
|
subclass = subclasses[x]
|
|
|
|
execlist[subclass.__module__.split('.')[-1]] = make_closure(subclass,task,study_id,
|
|
|
|
workflow_id)
|
|
|
|
return execlist
|
2020-03-27 12:29:31 +00:00
|
|
|
|
2020-03-03 20:30:42 +00:00
|
|
|
@staticmethod
|
|
|
|
def get_all_subclasses():
|
|
|
|
return Script._get_all_subclasses(Script)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _get_all_subclasses(cls):
|
|
|
|
|
|
|
|
# hackish mess to make sure we have all the modules loaded for the scripts
|
|
|
|
pkg_dir = os.path.dirname(__file__)
|
|
|
|
for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]):
|
|
|
|
importlib.import_module('.' + name, __package__)
|
|
|
|
|
|
|
|
|
|
|
|
"""Returns a list of all classes that extend this class."""
|
|
|
|
all_subclasses = []
|
|
|
|
|
|
|
|
for subclass in cls.__subclasses__():
|
|
|
|
all_subclasses.append(subclass)
|
|
|
|
all_subclasses.extend(Script._get_all_subclasses(subclass))
|
|
|
|
|
2020-03-18 21:03:36 +00:00
|
|
|
return all_subclasses
|
|
|
|
|
2020-07-24 18:33:24 +00:00
|
|
|
# def add_data_to_task(self, task, data):
|
|
|
|
# key = self.__class__.__name__
|
|
|
|
# if key in task.data:
|
|
|
|
# task.data[key].update(data)
|
|
|
|
# else:
|
|
|
|
# task.data[key] = data
|
2020-03-18 21:03:36 +00:00
|
|
|
|
|
|
|
class ScriptValidationError:
|
|
|
|
|
|
|
|
def __init__(self, code, message):
|
|
|
|
self.code = code
|
|
|
|
self.message = message
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_api_error(cls, api_error: ApiError):
|
|
|
|
return cls(api_error.code, api_error.message)
|