Beispiel #1
0
    def test_connected(self):
        graph = Graph()
        graph.add_node(1)
        graph.add_node(2)
        graph.add_node(3)
        graph.add_node(4)

        self.assertFalse(graph.connected())

        graph.add_edge(1, 2)
        graph.add_edge(3, 4)
        self.assertFalse(graph.connected())

        graph.add_edge(2, 3)
        graph.add_edge(4, 1)
        self.assertTrue(graph.connected())
Beispiel #2
0
class Workflow(Process):
    def __init__(self,
                 process_id,
                 inputs,
                 outputs,
                 requirements,
                 hints,
                 label,
                 description,
                 steps,
                 context,
                 data_links=None):
        super(Workflow, self).__init__(process_id, inputs, outputs,
                                       requirements, hints, label, description)
        self.graph = Graph()
        self.executor = context.executor
        self.steps = steps
        self.data_links = data_links or []
        self.context = context
        self.port_step_index = {}

        for step in steps:
            node = AppNode(step.app, {})
            self.add_node(step.id, node)
            for inp in step.inputs:
                self.port_step_index[inp.id] = step.id
                self.move_connect_to_datalink(inp)
                if inp.value:
                    node.inputs[inp.id] = inp.value

            for out in step.outputs:
                self.port_step_index[out.id] = step.id

        for inp in self.inputs:
            self.add_node(inp.id, inp)

        for out in self.outputs:
            self.move_connect_to_datalink(out)
            self.add_node(out.id, out)

        # dedupe links
        s = {tuple(dl.items()) for dl in self.data_links}
        self.data_links = [dict(dl) for dl in s]

        for dl in self.data_links:
            dst = dl['destination'].lstrip('#')
            src = dl['source'].lstrip('#')

            if src in self.port_step_index and dst in self.port_step_index:
                rel = Relation(src, dst, dl.get('position', 0))
                src = self.port_step_index[src]
                dst = self.port_step_index[dst]
            elif src in self._inputs:
                rel = InputRelation(dst, dl.get('position', 0))
                dst = self.port_step_index[dst]
            elif dst in self._outputs:
                rel = OutputRelation(src, dl.get('position', 0))
                src = self.port_step_index[src]
            else:
                raise RabixError("invalid data link %s" % dl)

            self.graph.add_edge(src, dst, rel)

        if not self.graph.connected():
            pass
            # raise ValidationError('Graph is not connected')

    def move_connect_to_datalink(self, port):
        for src in port.source:
            self.data_links.append({
                'source': src,
                'destination': '#' + port.id
            })
        del port.source[:]

    # Graph.add_node silently fails if node already exists
    def add_node(self, node_id, node):
        if node_id in self.graph.nodes:
            raise ValidationError('Duplicate node ID: %s' % node_id)
        self.graph.add_node(node_id, node)

    def run(self, job):
        eg = ExecutionGraph(self, job)
        while eg.has_next():
            next_id, next = eg.next_job()
            self.executor.execute(next, eg.job_done, next_id)
        return eg.outputs

    def to_dict(self, context):
        d = super(Workflow, self).to_dict(context)
        d.update({
            "class": "Workflow",
            'steps': [step.to_dict(context) for step in self.steps]
        })
        return d

    @classmethod
    def from_dict(cls, context, d):
        converted = {}
        for k, v in six.iteritems(d):
            if k == 'steps':
                converted[k] = [Step.from_dict(context, s) for s in v]
            else:
                converted[k] = context.from_dict(v)

        kwargs = Process.kwarg_dict(converted)
        kwargs.update({
            'steps':
            converted['steps'],
            'data_links':
            converted.get('dataLinks'),
            'context':
            context,
            'inputs': [
                InputParameter.from_dict(context, i)
                for i in converted['inputs']
            ],
            'outputs': [
                WorkflowOutput.from_dict(context, o)
                for o in converted['outputs']
            ]
        })
        return cls(**kwargs)
Beispiel #3
0
class Workflow(Process):

    def __init__(self, process_id, inputs, outputs, requirements, hints, label,
                 description, steps, context, data_links=None):
        super(Workflow, self).__init__(
            process_id, inputs, outputs, requirements,
            hints, label, description
        )
        self.graph = Graph()
        self.executor = context.executor
        self.steps = steps
        self.data_links = data_links or []
        self.context = context
        self.port_step_index = {}

        for step in steps:
            node = AppNode(step.app, {})
            self.add_node(step.id, node)
            for inp in step.inputs:
                self.port_step_index[inp.id] = step.id
                self.move_connect_to_datalink(inp)
                if inp.value:
                    node.inputs[inp.id] = inp.value

            for out in step.outputs:
                self.port_step_index[out.id] = step.id

        for inp in self.inputs:
            self.add_node(inp.id, inp)

        for out in self.outputs:
            self.move_connect_to_datalink(out)
            self.add_node(out.id, out)

        for dl in self.data_links:
            dst = dl['destination'].lstrip('#')
            src = dl['source'].lstrip('#')

            if src in self.port_step_index and dst in self.port_step_index:
                rel = Relation(src, dst, dl.get('position', 0))
                src = self.port_step_index[src]
                dst = self.port_step_index[dst]
            elif src in self._inputs:
                rel = InputRelation(dst, dl.get('position', 0))
                dst = self.port_step_index[dst]
            elif dst in self._outputs:
                rel = OutputRelation(src, dl.get('position', 0))
                src = self.port_step_index[src]
            else:
                raise RabixError("invalid data link %s" % dl)

            self.graph.add_edge(src, dst, rel)

        if not self.graph.connected():
            pass
            # raise ValidationError('Graph is not connected')

    def move_connect_to_datalink(self, port):
        for dl in port.connect:
            dl['destination'] = '#'+port.id
            self.data_links.append(dl)
        del port.connect[:]

    # Graph.add_node silently fails if node already exists
    def add_node(self, node_id, node):
        if node_id in self.graph.nodes:
            raise ValidationError('Duplicate node ID: %s' % node_id)
        self.graph.add_node(node_id, node)

    def hide_nodes(self, type):
        for node_id in self.graph.node_list():
            node = self.graph.node_data(node_id)
            if isinstance(node, type):
                self.graph.hide_node(node_id)

    def run(self, job):
        eg = ExecutionGraph(self, job)
        while eg.has_next():
            next_id, next = eg.next_job()
            self.executor.execute(next, eg.job_done, next_id)
        return eg.outputs

    def to_dict(self, context):
        d = super(Workflow, self).to_dict(context)
        d.update({
            "class": "Workflow",
            'steps': [step.to_dict(context) for step in self.steps]
        })
        return d

    @classmethod
    def from_dict(cls, context, d):
        converted = {}
        for k, v in six.iteritems(d):
            if k == 'steps':
                converted[k] = [Step.from_dict(context, s) for s in v]
            else:
                converted[k] = context.from_dict(v)

        kwargs = Process.kwarg_dict(converted)
        kwargs.update({
            'steps': converted['steps'],
            'data_links': converted.get('dataLinks'),
            'context': context,
            'inputs': [InputParameter.from_dict(context, i)
                       for i in converted['inputs']],
            'outputs': [WorkflowOutput.from_dict(context, o)
                        for o in converted['outputs']]
        })
        return cls(**kwargs)
Beispiel #4
0
class WorkflowApp(App):

    def __init__(self, app_id, steps, context,
                 inputs=None, outputs=None, to=None,
                 app_description=None,
                 annotations=None,
                 platform_features=None):
        self.graph = Graph()
        self.inputs = inputs or []
        self.outputs = outputs or []
        self.executor = context.executor
        self.steps = steps
        self.to = to or {}
        self.context = context

        for step in steps:
            self.add_node(step.id,  AppNode(step.app, {}))

        for step in steps:
            # inputs
            for input_port, input_val in six.iteritems(step.inputs):
                inp = wrap_in_list(input_val)
                for item in inp:
                    self.add_edge_or_input(step, input_port, item)

            # outputs
            if step.outputs:
                for output_port, output_val in six.iteritems(step.outputs):
                    self.to[output_val['$to']] = output_port
                    if isinstance(step.app, WorkflowApp):
                        output_node = step.app.get_output(step.app.to.get(output_port))
                    else:
                        output_node = step.app.get_output(output_port)
                    output_id = output_val['$to']
                    self.add_node(output_id, output_node)
                    self.graph.add_edge(
                        step.id, output_id, OutputRelation(output_port)
                    )
                    # output_node.id = output_val['$to']
                    self.outputs.append(output_node)

        if not self.graph.connected():
            pass
            # raise ValidationError('Graph is not connected')

        schema = {
            "@type": "JsonSchema",
            "type": "object",
            "properties": {},
            "required": []
        }

        for inp in self.inputs:
            schema['properties'][inp.id] = inp.validator.schema
            if inp.required:
                schema['required'].append(inp.id)

        super(WorkflowApp, self).__init__(
            app_id, JsonSchema(context, schema), self.outputs,
            app_description=app_description,
            annotations=annotations,
            platform_features=platform_features
        )

    def add_edge_or_input(self, step, input_name, input_val):
        node_id = step.id
        if isinstance(input_val, dict) and '$from' in input_val:
            frm = wrap_in_list(input_val['$from'])
            for inp in frm:
                if '.' in inp:
                    node, outp = inp.split('.')
                    self.graph.add_edge(node, node_id, Relation(outp, input_name))
                else:
                    # TODO: merge input schemas if one input goes to different apps

                    input = step.app.get_input(input_name)
                    if inp not in self.graph.nodes:
                        self.add_node(inp, input)
                    self.graph.add_edge(
                        inp, node_id, InputRelation(input_name)
                    )
                    wf_input = copy.deepcopy(input)
                    wf_input.id = inp
                    self.inputs.append(wf_input)

        else:
            self.graph.node_data(node_id).inputs[input_name] = input_val

    # Graph.add_node silently fails if node already exists
    def add_node(self, node_id, node):
        if node_id in self.graph.nodes:
            raise ValidationError('Duplicate node ID: %s' % node_id)
        self.graph.add_node(node_id, node)

    def hide_nodes(self, type):
        for node_id in self.graph.node_list():
            node = self.graph.node_data(node_id)
            if isinstance(node, type):
                self.graph.hide_node(node_id)

    def run(self, job):
        eg = ExecutionGraph(self, job)
        while eg.has_next():
            next_id, next = eg.next_job()
            self.executor.execute(next, eg.job_done, next_id)
        return eg.outputs

    def to_dict(self, context):
        d = super(WorkflowApp, self).to_dict(context)
        d.update({
            "@type": "Workflow",
            'steps': [step.to_dict(context) for step in self.steps]
        })
        return d

    @classmethod
    def from_dict(cls, context, d):
        steps = [Step(
            step['id'], context.from_dict(step['app']),
            step['inputs'], step.get('outputs')
        )
            for step in d['steps']]

        return cls(
            d.get('@id', six.text_type(uuid4())),
            steps,
            context
        )