Completed
Push — master ( 265317...7ca1a7 )
by W
06:30
created

main()   D

Complexity

Conditions 9

Size

Total Lines 73

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 73
rs 4.1001
cc 9

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
#!/usr/bin/env python
2
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
3
# contributor license agreements.  See the NOTICE file distributed with
4
# this work for additional information regarding copyright ownership.
5
# The ASF licenses this file to You under the Apache License, Version 2.0
6
# (the "License"); you may not use this file except in compliance with
7
# the License.  You may obtain a copy of the License at
8
#
9
#     http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16
17
"""
18
Script which creates graphviz visualization of an action-chain workflow.
19
"""
20
21
import os
22
import argparse
23
import sets
24
25
try:
26
    from graphviz import Digraph
27
except ImportError:
28
    msg = ('Missing "graphviz" dependency. You can install it using pip: \n'
29
           'pip install graphviz')
30
    raise ImportError(msg)
31
32
from st2common.content.loader import MetaLoader
33
from st2actions.runners.actionchainrunner import ChainHolder
34
35
36
def main(metadata_path, output_path, print_source=False):
37
    metadata_path = os.path.abspath(metadata_path)
38
    metadata_dir = os.path.dirname(metadata_path)
39
40
    meta_loader = MetaLoader()
41
    data = meta_loader.load(metadata_path)
42
43
    action_name = data['name']
44
    entry_point = data['entry_point']
45
46
    workflow_metadata_path = os.path.join(metadata_dir, entry_point)
47
    chainspec = meta_loader.load(workflow_metadata_path)
48
49
    chain_holder = ChainHolder(chainspec, 'workflow')
50
51
    graph_label = '%s action-chain workflow visualization' % (action_name)
52
53
    graph_attr = {
54
        'rankdir': 'TD',
55
        'labelloc': 't',
56
        'fontsize': '15',
57
        'label': graph_label
58
    }
59
    node_attr = {}
60
    dot = Digraph(comment='Action chain work-flow visualization',
61
                  node_attr=node_attr, graph_attr=graph_attr, format='png')
62
    #  dot.body.extend(['rankdir=TD', 'size="10,5"'])
63
64
    # Add all nodes
65
    node = chain_holder.get_next_node()
66
    while node:
67
        dot.node(node.name, node.name)
68
        node = chain_holder.get_next_node(curr_node_name=node.name)
69
70
    # Add connections
71
    node = chain_holder.get_next_node()
72
    processed_nodes = sets.Set([node.name])
73
    nodes = [node]
74
    while nodes:
75
        previous_node = nodes.pop()
76
        success_node = chain_holder.get_next_node(curr_node_name=previous_node.name,
77
                                                  condition='on-success')
78
        failure_node = chain_holder.get_next_node(curr_node_name=previous_node.name,
79
                                                  condition='on-failure')
80
81
        # Add success node (if any)
82
        if success_node:
83
            dot.edge(previous_node.name, success_node.name, constraint='true',
84
                     color='green', label='on success')
85
            if success_node.name not in processed_nodes:
86
                nodes.append(success_node)
87
                processed_nodes.add(success_node.name)
88
89
        # Add failure node (if any)
90
        if failure_node:
91
            dot.edge(previous_node.name, failure_node.name, constraint='true',
92
                     color='red', label='on failure')
93
            if failure_node.name not in processed_nodes:
94
                nodes.append(failure_node)
95
                processed_nodes.add(failure_node.name)
96
97
    if print_source:
98
        print(dot.source)
99
100
    if output_path:
101
        output_path = os.path.join(output_path, action_name)
102
    else:
103
        output_path = output_path or os.path.join(os.getcwd(), action_name)
104
105
    dot.format = 'png'
106
    dot.render(output_path)
107
108
    print('Graph saved at %s' % (output_path + '.png'))
109
110
if __name__ == '__main__':
111
    parser = argparse.ArgumentParser(description='Action chain visualization')
112
    parser.add_argument('--metadata-path', action='store', required=True,
113
                        help='Path to the workflow action metadata file')
114
    parser.add_argument('--output-path', action='store', required=False,
115
                        help='Output directory for the generated image')
116
    parser.add_argument('--print-source', action='store_true', default=False,
117
                        help='Print graphviz source code to the stdout')
118
    args = parser.parse_args()
119
120
    main(metadata_path=args.metadata_path, output_path=args.output_path,
121
         print_source=args.print_source)
122