class ChaosDetector::Graphing::DirectedGraphs
Constants
- BOLD_HTML
- BR
- CLR_BLACK
- CLR_BLUE
- CLR_BRIGHTGREEN
- CLR_CYAN
- CLR_DARKGREEN
- CLR_DARKRED
- CLR_GREY
- CLR_NICEGREY
- CLR_ORANGE
- CLR_PALEGREEN
- CLR_PINK
- CLR_PURPLE
- CLR_SLATE
- CLR_WHITE
- EDGE_ATTRS
- EDGE_BASELINE
- EDGE_MIN
- GRAPH_ATTRS
- LF
- NODE_ATTRS
- PRE_STATUS_MSG
Status messages:
- STUB_NODE_ATTRS
- SUBDOMAIN_ATTRS
- TBL_CELL_HTML
- TBL_HTML
- TBL_ROW_HTML
Attributes
cluster_node_hash[R]
edges[R]
node_hash[R]
render_folder[R]
rendered_path[R]
root_graph[R]
Public Class Methods
new(render_folder: nil)
click to toggle source
TODO: integrate options as needed:
# File lib/chaos_detector/graphing/directed_graphs.rb, line 133 def initialize(render_folder: nil) @root_graph = nil @node_hash = {} @cluster_node_hash = {} @edges = Set.new @render_folder = render_folder || 'render' end
Public Instance Methods
add_edges(edges, calc_weight:true, node_safe:true)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 283 def add_edges(edges, calc_weight:true, node_safe:true) assert_graph_state # @node_hash.each do |k, v| # log("NODE_HASH: Has value for #{ChaosUtils.decorate(k)} => #{ChaosUtils.decorate(v)}") # end weight_max = edges.map(&:weight).max edges.each do |edge| src_clust, src = find_graph_node(edge.src_node) dep_clust, dep = find_graph_node(edge.dep_node) next unless !node_safe || (src && dep) @edges << [src, dep] edge_attrs = build_edge_attrs(edge, calc_weight: calc_weight, max_weight: weight_max, src_clust: src_clust, dep_clust: dep_clust) @root_graph.add_edges( node_key(edge.src_node, cluster: src_clust ? :stub : false), node_key(edge.dep_node, cluster: dep_clust ? :stub : false), edge_attrs ) # , {label: e.reduce_sum, penwidth: weight}) end end
add_node_to_graph(node, graph: nil, as_cluster: false, metrics_table:false)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 235 def add_node_to_graph(node, graph: nil, as_cluster: false, metrics_table:false) assert_graph_state raise 'node is required' unless node parent_graph = graph || @root_graph key = node.to_k attrs = { label: node_label(node, metrics_table: metrics_table) } if as_cluster # tab good shape subgraph_name = "cluster_#{key}" attrs.merge!(SUBDOMAIN_ATTRS) # attrs = {}.merge(SUBDOMAIN_ ATTRS) @cluster_node_hash[key] = parent_graph.add_graph(subgraph_name, attrs) @cluster_node_hash[key].add_nodes(node_key(node, cluster: :stub), STUB_NODE_ATTRS) # @cluster_node_hash[key].attrs(attrs) # puts ("attrs: #{key}: #{attrs} / #{@cluster_node_hash.length}") else attrs = attrs.merge!(NODE_ATTRS) @node_hash[key] = parent_graph.add_nodes(key, attrs) end end
add_node_to_parent(node, parent_node:, as_cluster: false, metrics_table:false)
click to toggle source
Add node to given parent_node, assuming parent_node is a subgraph
# File lib/chaos_detector/graphing/directed_graphs.rb, line 220 def add_node_to_parent(node, parent_node:, as_cluster: false, metrics_table:false) assert_graph_state raise 'node is required' unless node parent_graph = if parent_node _clust, p_graph = find_graph_node(parent_node) raise "Couldn't find parent node: #{parent_node}" unless p_graph p_graph else @root_graph end add_node_to_graph(node, graph: parent_graph, as_cluster: as_cluster, metrics_table: metrics_table) end
append_nodes(nodes, as_cluster: false, metrics_table: false) { |node| ... }
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 261 def append_nodes(nodes, as_cluster: false, metrics_table: false) assert_graph_state return unless nodes # raise 'node is required' unless nodes nodes.each do |node| parent_node = block_given? ? yield(node) : nil # puts "gotit #{parent_node}" if parent_node add_node_to_parent(node, parent_node: parent_node, as_cluster: as_cluster, metrics_table: metrics_table) end end
assert_graph_state()
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 215 def assert_graph_state raise '@root_graph is not set yet. Call create_directed_graph.' unless @root_graph end
create_directed_graph(title, graph_attrs: nil)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 141 def create_directed_graph(title, graph_attrs: nil) @title = title @node_hash.clear @cluster_node_hash.clear @cluster_node_hash.clear lbl = title_html(@title, subtitle: graph_attrs&.dig(:subtitle)) attrs = { label: "<#{lbl}>", **GRAPH_ATTRS, } attrs.merge(graph_attrs) if graph_attrs&.any? attrs.delete(:subtitle) @root_graph = GraphViz.digraph(:G, attrs) end
html_tbl_from(hash:) { |k, v| ... }
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 198 def html_tbl_from(hash:) trs = hash.map.with_index do |h, n| k, v = h key_content, val_content = yield(k, v) if block_given? key_td = TBL_CELL_HTML % (key_content || k) val_td = TBL_CELL_HTML % (val_content || v) td_html = [key_td, val_td].join html = format(TBL_ROW_HTML, { color: n.even? ? 'blue' : 'white', cells: td_html.strip }) html.strip end TBL_HTML % trs.join().strip end
in_font(str, font_size:12)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 194 def in_font(str, font_size:12) "<FONT POINT-SIZE='#{font_size}'>#{str}</FONT>" end
node_key(node, cluster: false)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 273 def node_key(node, cluster: false) if cluster==:stub "cluster_stub_#{node.to_k}" elsif !!cluster "cluster_#{node.to_k}" else node.to_k end end
node_label(node, metrics_table: false)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 160 def node_label(node, metrics_table: false) if metrics_table tbl_hash = {title: node.title, subtitle: node.subtitle, **node.graph_props} html = html_tbl_from(hash: tbl_hash) do |k, v| if k==:title [in_font(k, font_size: 24), in_font(v, font_size: 16)]#[BOLD_HTML % k, BOLD_HTML % v] else [k, v] end end else html = title_html(node.title, subtitle: node.subtitle) end html.strip! # puts '_' * 50 # puts "html: #{html}" '<%s>' % html end
render_graph()
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 308 def render_graph assert_graph_state filename = "#{@title}.png" @rendered_path = File.join(@render_folder, filename).to_s log("Rendering graph to to #{@rendered_path}") ChaosDetector::Utils::FSUtil.ensure_paths_to_file(@rendered_path) @root_graph.output(png: @rendered_path) self end
title_html(title, subtitle:nil, font_size:24, subtitle_fontsize:nil)
click to toggle source
HTML Label with subtitle:
# File lib/chaos_detector/graphing/directed_graphs.rb, line 180 def title_html(title, subtitle:nil, font_size:24, subtitle_fontsize:nil) lbl_buf = [in_font(title, font_size: font_size)] sub_fontsize = subtitle_fontsize || 3 * font_size / 4 if ChaosUtils.aught?(subtitle) lbl_buf << in_font(subtitle, font_size: sub_fontsize) end # Fake out some padding: lbl_buf << in_font(' ', font_size: sub_fontsize) lbl_buf.join(BR) end
Private Instance Methods
build_edge_attrs(edge, calc_weight: true, max_weight: nil, src_clust: nil, dep_clust: nil)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 322 def build_edge_attrs(edge, calc_weight: true, max_weight: nil, src_clust: nil, dep_clust: nil) edge_attrs = EDGE_ATTRS.dup # Edge attaches to cluster if possible: edge_attrs[:ltail] = node_key(edge.src_node, cluster: true) if src_clust edge_attrs[:lhead] = node_key(edge.dep_node, cluster: true) if dep_clust # Proportional edge weight: if calc_weight && max_weight edge_attrs.merge!( label: edge.weight, penwidth: edge_weight(edge.weight / max_weight) ) end # Intra-domain: if edge.src_node.domain_name == edge.dep_node.domain_name edge_attrs.merge!( style: 'dotted', color: CLR_ORANGE, constraint: 'true', ) end # Props for edge_type: edge_attrs.merge!( case edge.edge_type when :superclass { arrowhead: 'empty', arrowsize: 1.0, color: CLR_BLUE } when :association { arrowhead: 'diamond', arrowsize: 1.0, color: CLR_ORANGE } when :class_association { arrowhead: 'diamond', arrowsize: 1.0, color: CLR_PINK } else { arrowhead: 'open', arrowsize: 1.0 } end ) end
edge_weight(n, edge_min: EDGE_MIN, edge_baseline: EDGE_BASELINE)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 387 def edge_weight(n, edge_min: EDGE_MIN, edge_baseline: EDGE_BASELINE) edge_min + n * edge_baseline end
find_graph_node(node)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 376 def find_graph_node(node) assert_graph_state # log("NODE_HASH: LOOKING UP #{ChaosUtils.decorate(node)}") cnode = @cluster_node_hash[node.to_k] if cnode [true, cnode] else [false, @node_hash[node.to_k]] end end
log(msg, **opts)
click to toggle source
# File lib/chaos_detector/graphing/directed_graphs.rb, line 391 def log(msg, **opts) ChaosUtils.log_msg(msg, subject: 'DGraphDiagram', **opts) end