예제 #1
0
파일: action.py 프로젝트: silky/WALKOFF
    def set_arguments(self, new_arguments):
        """Updates the arguments for an Action object.

        Args:
            new_arguments ([Argument]): The new Arguments for the Action object.
        """
        new_arguments = {arg.name: arg for arg in new_arguments}
        validate_app_action_parameters(self._arguments_api, new_arguments,
                                       self.app_name, self.action_name)
        self.arguments = new_arguments
예제 #2
0
    def _from_xml(self, step_xml, parent_name='', ancestry=None):
        self.raw_xml = step_xml
        name = step_xml.get('id')
        ExecutionElement.__init__(self,
                                  name=name,
                                  parent_name=parent_name,
                                  ancestry=ancestry)

        self.action = step_xml.find('action').text
        self.app = step_xml.find('app').text
        self.run, self.input_api = get_app_action_api(self.app, self.action)
        is_templated_xml = step_xml.find('templated')
        self.templated = is_templated_xml is not None and bool(
            is_templated_xml.text)
        get_app_action(self.app, self.run)
        input_xml = step_xml.find('inputs')
        if input_xml is not None:
            inputs = inputs_xml_to_dict(input_xml) or {}
            if not self.templated:
                self.input = validate_app_action_parameters(
                    self.input_api, inputs, self.app, self.action)
            else:
                self.input = inputs
        else:
            self.input = validate_app_action_parameters(
                self.input_api, {}, self.app, self.action)
        device_field = step_xml.find('device')
        self.device = device_field.text if device_field is not None else ''
        risk_field = step_xml.find('risk')
        self.risk = risk_field.text if risk_field is not None else 0
        self.conditionals = [
            nextstep.NextStep(xml=next_step_element,
                              parent_name=self.name,
                              ancestry=self.ancestry)
            for next_step_element in step_xml.findall('next')
        ]
        self.widgets = [
            _Widget(widget.get('app'), widget.text)
            for widget in step_xml.findall('widgets/*')
        ]
        position = step_xml.find('position')
        if position is None:
            self.position = {}
        else:
            x_position = position.find('x')
            y_position = position.find('y')
            if x_position is not None and y_position is not None:
                self.position = {'x': x_position.text, 'y': y_position.text}
            else:
                self.position = {}
예제 #3
0
파일: step.py 프로젝트: ondrocks/WALKOFF
 def execute(self, instance, accumulator):
     """Executes a Step by calling the associated app function.
     
     Args:
         instance (App): The instance of an App object to be used to execute the associated function.
         accumulator (dict): Dict containing the results of the previous steps
         
     Returns:
         The result of the executed function.
     """
     callbacks.StepInputValidated.send(self)
     try:
         args = dereference_step_routing(self.input, accumulator, 'In step {0}'.format(self.name))
         args = validate_app_action_parameters(self.input_api, args, self.app, self.action)
         action = get_app_action(self.app, self.run)
         result = action(instance, **args)
         callbacks.FunctionExecutionSuccess.send(self, data=json.dumps({"result": result}))
     except InvalidInput as e:
         logger.error('Error calling step {0}. Error: {1}'.format(self.name, str(e)))
         callbacks.StepInputInvalid.send(self)
         self.output = 'error: {0}'.format(str(e))
         raise
     except Exception as e:
         logger.error('Error calling step {0}. Error: {1}'.format(self.name, str(e)))
         self.output = 'error: {0}'.format(str(e))
         raise
     else:
         self.output = result
         for widget in self.widgets:
             get_widget_signal(widget.app, widget.widget).send(self, data=json.dumps({"result": result}))
         logger.debug('Step {0} executed successfully'.format(self.ancestry))
         return result
예제 #4
0
파일: step.py 프로젝트: ondrocks/WALKOFF
 def __init__(self,
              xml=None,
              name='',
              action='',
              app='',
              device='',
              inputs=None,
              next_steps=None,
              errors=None,
              parent_name='',
              position=None,
              ancestry=None,
              widgets=None,
              risk=0):
     """Initializes a new Step object. A Workflow has many steps that it executes.
     
     Args:
         xml (ElementTree, optional): The XML element tree object. Defaults to None.
         name (str, optional): The name of the Step object. Defaults to an empty string.
         action (str, optional): The name of the action associated with a Step. Defaults to an empty string.
         app (str, optional): The name of the app associated with the Step. Defaults to an empty string.
         device (str, optional): The name of the device associated with the app associated with the Step. Defaults
             to an empty string.
         inputs (dict, optional): A dictionary of Argument objects that are input to the step execution. Defaults
             to None.
         next_steps (list[NextStep], optional): A list of NextStep objects for the Step object. Defaults to None.
         errors (list[NextStep], optional): A list of NextStep error objects for the Step object. Defaults to None.
         parent_name (str, optional): The name of the parent for ancestry purposes. Defaults to an empty string.
         position (dict, optional): A dictionary with the x and y coordinates of the Step object. This is used
             for UI display purposes. Defaults to None.
         ancestry (list[str], optional): The ancestry for the Step object. Defaults to None.
         widgets (list[tuple(str, str)], optional): A list of widget tuples, which holds the app and the 
             corresponding widget. Defaults to None.
         risk (int, optional): The risk associated with the Step. Defaults to 0.
     """
     ExecutionElement.__init__(self, name=name, parent_name=parent_name, ancestry=ancestry)
     if xml is not None:
         self._from_xml(xml, parent_name=parent_name, ancestry=ancestry)
     else:
         if action == '' or app == '':
             raise InvalidElementConstructed('Either both action and app or xml must be '
                                             'specified in step constructor')
         self.action = action
         self.app = app
         self.run, self.input_api = get_app_action_api(self.app, self.action)
         get_app_action(self.app, self.run)
         inputs = inputs if inputs is not None else {}
         self.input = validate_app_action_parameters(self.input_api, inputs, self.app, self.action)
         self.device = device
         self.risk = risk
         self.conditionals = next_steps if next_steps is not None else []
         self.errors = errors if errors is not None else []
         self.position = position if position is not None else {}
         self.widgets = [_Widget(widget_app, widget_name)
                         for (widget_app, widget_name) in widgets] if widgets is not None else []
         self.raw_xml = self.to_xml()
         self.templated = False
     self.output = None
     self.next_up = None
예제 #5
0
파일: step.py 프로젝트: davehenton/WALKOFF
    def set_input(self, new_input):
        """Updates the input for a Step object.

        Args:
            new_input (dict): The new inputs for the Step object.
        """
        self.inputs = validate_app_action_parameters(self._input_api,
                                                     new_input, self.app,
                                                     self.action)
예제 #6
0
파일: action.py 프로젝트: silky/WALKOFF
 def _update_json(self, updated_json):
     self.action_name = updated_json['action_name']
     self.app_name = updated_json['app_name']
     self.device_id = updated_json[
         'device_id'] if 'device_id' in updated_json else None
     arguments = {}
     if 'arguments' in updated_json:
         for argument_json in updated_json['arguments']:
             argument = Argument(**argument_json)
             arguments[argument.name] = argument
     if arguments is not None:
         if not self.templated:
             validate_app_action_parameters(self._arguments_api, arguments,
                                            self.app_name, self.action_name)
     else:
         validate_app_action_parameters(self._arguments_api, [],
                                        self.app_name, self.action_name)
     self.arguments = arguments
예제 #7
0
파일: step.py 프로젝트: ondrocks/WALKOFF
 def _update_xml(self, step_xml):
     self.action = step_xml.find('action').text
     self.app = step_xml.find('app').text
     device_field = step_xml.find('device')
     self.device = device_field.text if device_field is not None else ''
     risk_field = step_xml.find('risk')
     self.risk = risk_field.text if risk_field is not None else 0
     input_xml = step_xml.find('inputs')
     if input_xml is not None:
         inputs = inputs_xml_to_dict(input_xml) or {}
         if not self.templated:
             self.input = validate_app_action_parameters(self.input_api, inputs, self.app, self.action)
         else:
             self.input = inputs
     else:
         self.input = validate_app_action_parameters(self.input_api, {}, self.app, self.action)
     self.conditionals = [nextstep.NextStep(xml=next_step_element, parent_name=self.name, ancestry=self.ancestry)
                          for next_step_element in step_xml.findall('next')]
     self.errors = [nextstep.NextStep(xml=error_step_element, parent_name=self.name, ancestry=self.ancestry)
                    for error_step_element in step_xml.findall('error')]
예제 #8
0
파일: step.py 프로젝트: davehenton/WALKOFF
 def _update_json(self, updated_json):
     self.action = updated_json['action']
     self.app = updated_json['app']
     self.device = updated_json['device'] if 'device' in updated_json else ''
     self.risk = updated_json['risk'] if 'risk' in updated_json else 0
     inputs = {arg['name']: arg['value']
               for arg in updated_json['inputs']
               } if 'inputs' in updated_json else {}
     if inputs is not None:
         if not self.templated:
             self.inputs = validate_app_action_parameters(
                 self._input_api, inputs, self.app, self.action)
         else:
             self.inputs = inputs
     else:
         self.inputs = validate_app_action_parameters(
             self._input_api, {}, self.app, self.action)
     self.next_steps = [
         NextStep.create(cond_json)
         for cond_json in updated_json['next_steps']
     ]
예제 #9
0
파일: step.py 프로젝트: davehenton/WALKOFF
    def __init__(self,
                 app,
                 action,
                 name='',
                 device='',
                 inputs=None,
                 triggers=None,
                 next_steps=None,
                 position=None,
                 widgets=None,
                 risk=0,
                 uid=None,
                 templated=False,
                 raw_representation=None):
        """Initializes a new Step object. A Workflow has many steps that it executes.

        Args:
            app (str): The name of the app associated with the Step
            action (str): The name of the action associated with a Step
            name (str, optional): The name of the Step object. Defaults to an empty string.
            device (str, optional): The name of the device associated with the app associated with the Step. Defaults
                to an empty string.
            inputs (dict, optional): A dictionary of Argument objects that are input to the step execution. Defaults
                to None.
            triggers (list[Flag], optional): A list of Flag objects for the Step. If a Step should wait for data
                before continuing, then include these Trigger objects in the Step init. Defaults to None.
            next_steps (list[NextStep], optional): A list of NextStep objects for the Step object. Defaults to None.
            position (dict, optional): A dictionary with the x and y coordinates of the Step object. This is used
                for UI display purposes. Defaults to None.
            widgets (list[tuple(str, str)], optional): A list of widget tuples, which holds the app and the
                corresponding widget. Defaults to None.
            risk (int, optional): The risk associated with the Step. Defaults to 0.
            uid (str, optional): A universally unique identifier for this object.
                Created from uuid.uuid4().hex in Python
            templated (bool, optional): Whether or not the Step is templated. Used for Jinja templating.
            raw_representation (dict, optional): JSON representation of this object. Used for Jinja templating.
        """
        ExecutionElement.__init__(self, uid)

        self.triggers = triggers if triggers is not None else []
        self._incoming_data = AsyncResult()

        self.name = name
        self.app = app
        self.action = action
        self._run, self._input_api = get_app_action_api(self.app, self.action)
        get_app_action(self.app, self._run)
        if isinstance(inputs, list):
            inputs = {arg['name']: arg['value'] for arg in inputs}
        elif isinstance(inputs, dict):
            inputs = inputs
        else:
            inputs = {}
        self.templated = templated
        if not self.templated:
            self.inputs = validate_app_action_parameters(
                self._input_api, inputs, self.app, self.action)
        else:
            self.inputs = inputs
        self.device = device if (device is not None
                                 and device != 'None') else ''
        self.risk = risk
        self.next_steps = next_steps if next_steps is not None else []
        self.position = position if position is not None else {}
        self.widgets = [
            widget if isinstance(widget, Widget) else Widget(**widget)
            for widget in widgets
        ] if widgets is not None else []

        self._output = None
        self._next_up = None
        self._raw_representation = raw_representation if raw_representation is not None else {}
        self._execution_uid = 'default'
예제 #10
0
파일: step.py 프로젝트: davehenton/WALKOFF
    def execute(self, instance, accumulator):
        """Executes a Step by calling the associated app function.

        Args:
            instance (App): The instance of an App object to be used to execute the associated function.
            accumulator (dict): Dict containing the results of the previous steps

        Returns:
            The result of the executed function.
        """
        self._execution_uid = uuid.uuid4().hex
        data_sent.send(self, callback_name="Step Started", object_type="Step")

        if self.triggers:
            data_sent.send(self,
                           callback_name="Trigger Step Awaiting Data",
                           object_type="Step")
            logger.debug('Trigger Step {} is awaiting data'.format(self.name))

            while True:
                try:
                    data = self._incoming_data.get(timeout=1)
                    self._incoming_data = AsyncResult()
                except gevent.Timeout:
                    gevent.sleep(0.1)
                    continue
                data_in = data['data_in']
                inputs = data['inputs'] if 'inputs' in data else {}

                if all(
                        flag.execute(data_in=data_in, accumulator=accumulator)
                        for flag in self.triggers):
                    data_sent.send(self,
                                   callback_name="Trigger Step Taken",
                                   object_type="Step")
                    logger.debug(
                        'Trigger is valid for input {0}'.format(data_in))
                    accumulator[self.name] = data_in

                    if inputs:
                        self.inputs.update(inputs)
                    break
                else:
                    logger.debug(
                        'Trigger is not valid for input {0}'.format(data_in))
                    data_sent.send(self,
                                   callback_name="Trigger Step Not Taken",
                                   object_type="Step")

                gevent.sleep(0.1)

        try:
            args = dereference_step_routing(self.inputs, accumulator,
                                            'In step {0}'.format(self.name))
            args = validate_app_action_parameters(self._input_api, args,
                                                  self.app, self.action)
            action = get_app_action(self.app, self._run)
            if is_app_action_bound(self.app, self._run):
                result = action(instance, **args)
            else:
                result = action(**args)

            data_sent.send(self,
                           callback_name="Function Execution Success",
                           object_type="Step",
                           data=json.dumps({"result": result.as_json()}))
        except InvalidInput as e:
            formatted_error = format_exception_message(e)
            logger.error('Error calling step {0}. Error: {1}'.format(
                self.name, formatted_error))
            data_sent.send(self,
                           callback_name="Step Input Invalid",
                           object_type="Step")
            self._output = ActionResult('error: {0}'.format(formatted_error),
                                        'InvalidInput')
            raise
        except Exception as e:
            formatted_error = format_exception_message(e)
            logger.error('Error calling step {0}. Error: {1}'.format(
                self.name, formatted_error))
            self._output = ActionResult('error: {0}'.format(formatted_error),
                                        'UnhandledException')
            raise
        else:
            self._output = result
            for widget in self.widgets:
                get_widget_signal(widget.app, widget.name).send(
                    self, data=json.dumps({"result": result.as_json()}))
            logger.debug('Step {0}-{1} (uid {2}) executed successfully'.format(
                self.app, self.action, self.uid))
            return result
예제 #11
0
 def set_input(self, new_input):
     self.input = validate_app_action_parameters(self.input_api, new_input,
                                                 self.app, self.action)
예제 #12
0
파일: action.py 프로젝트: silky/WALKOFF
    def __init__(self,
                 app_name,
                 action_name,
                 name='',
                 device_id=None,
                 arguments=None,
                 triggers=None,
                 position=None,
                 uid=None,
                 templated=False,
                 raw_representation=None):
        """Initializes a new Action object. A Workflow has many actions that it executes.

        Args:
            app_name (str): The name of the app associated with the Action
            action_name (str): The name of the action associated with a Action
            name (str, optional): The name of the Action object. Defaults to an empty string.
            device_id (int, optional): The id of the device associated with the app associated with the Action. Defaults
                to None.
            arguments ([Argument], optional): A list of Argument objects that are parameters to the action.
                Defaults to None.
            triggers (list[Flag], optional): A list of Flag objects for the Action. If a Action should wait for data
                before continuing, then include these Trigger objects in the Action init. Defaults to None.
            position (dict, optional): A dictionary with the x and y coordinates of the Action object. This is used
                for UI display purposes. Defaults to None.
            uid (str, optional): A universally unique identifier for this object.
                Created from uuid.uuid4().hex in Python
            templated (bool, optional): Whether or not the Action is templated. Used for Jinja templating.
            raw_representation (dict, optional): JSON representation of this object. Used for Jinja templating.
        """
        ExecutionElement.__init__(self, uid)

        self.triggers = triggers if triggers is not None else []
        self._incoming_data = None
        self._event = threading.Event()

        self.name = name
        self.device_id = device_id
        self.app_name = app_name
        self.action_name = action_name
        self._run, self._arguments_api = get_app_action_api(
            self.app_name, self.action_name)

        if is_app_action_bound(self.app_name,
                               self._run) and not self.device_id:
            raise InvalidArgument(
                "Cannot initialize Action {}. App action is bound but no device ID was provided."
                .format(self.name))

        self._action_executable = get_app_action(self.app_name, self._run)

        arguments = {argument.name: argument
                     for argument in arguments
                     } if arguments is not None else {}

        self.templated = templated
        if not self.templated:
            validate_app_action_parameters(self._arguments_api, arguments,
                                           self.app_name, self.action_name)
        self.arguments = arguments
        self.position = position if position is not None else {}

        self._output = None
        self._raw_representation = raw_representation if raw_representation is not None else {}
        self._execution_uid = 'default'
예제 #13
0
파일: action.py 프로젝트: silky/WALKOFF
    def execute(self, instance, accumulator):
        """Executes an Action by calling the associated app function.

        Args:
            instance (App): The instance of an App object to be used to execute the associated function.
            accumulator (dict): Dict containing the results of the previous actions

        Returns:
            The result of the executed function.
        """
        self._execution_uid = str(uuid.uuid4())

        WalkoffEvent.CommonWorkflowSignal.send(
            self, event=WalkoffEvent.ActionStarted)

        if self.triggers:
            WalkoffEvent.CommonWorkflowSignal.send(
                self, event=WalkoffEvent.TriggerActionAwaitingData)
            logger.debug('Trigger Action {} is awaiting data'.format(
                self.name))
            self._wait_for_trigger(accumulator)

        try:
            args = validate_app_action_parameters(self._arguments_api,
                                                  self.arguments,
                                                  self.app_name,
                                                  self.action_name,
                                                  accumulator=accumulator)
            if is_app_action_bound(self.app_name, self._run):
                result = self._action_executable(instance, **args)
            else:
                result = self._action_executable(**args)

            WalkoffEvent.CommonWorkflowSignal.send(
                self,
                event=WalkoffEvent.ActionExecutionSuccess,
                data=result.as_json())
        except InvalidArgument as e:
            formatted_error = format_exception_message(e)
            logger.error('Error calling action {0}. Error: {1}'.format(
                self.name, formatted_error))
            # TODO: Should this event return the error?
            WalkoffEvent.CommonWorkflowSignal.send(
                self, event=WalkoffEvent.ActionArgumentsInvalid)
            self._output = ActionResult('error: {0}'.format(formatted_error),
                                        'InvalidArguments')
        except Exception as e:
            formatted_error = format_exception_message(e)
            logger.exception('Error calling action {0}. Error: {1}'.format(
                self.name, formatted_error))
            self._output = ActionResult('error: {0}'.format(formatted_error),
                                        'UnhandledException')
            WalkoffEvent.CommonWorkflowSignal.send(
                self,
                event=WalkoffEvent.ActionExecutionError,
                data=self._output.as_json())
        else:
            self._output = result
            logger.debug(
                'Action {0}-{1} (uid {2}) executed successfully'.format(
                    self.app_name, self.action_name, self.uid))
            return result