Class: ErdMap::Graph

Inherits:
Object
  • Object
show all
Defined in:
lib/erd_map/graph.rb

Constant Summary collapse

CHUNK_SIZE =
3
MAX_COMMUNITY_SIZE =
20

Instance Method Summary collapse

Instance Method Details

#association_columnsObject



111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/erd_map/graph.rb', line 111

def association_columns
  return @association_columns if @association_columns

  @association_columns = Hash.new { |hash, key| hash[key] = [] }
  whole_models.each do |model|
    model.reflect_on_all_associations(:belongs_to).select { |mod| !mod.options[:polymorphic] }.map do |target|
      if target.try(:foreign_key) && model.column_names.include?(target.foreign_key)
        @association_columns[model.name] << target.foreign_key
      end
    end
  end
  @association_columns
end

#chunked_nodesObject

[nodeA, nodeB, nodeC], [nodeD, nodeE, nodeF, nodeG, …], …


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/erd_map/graph.rb', line 34

def chunked_nodes
  return @chunked_nodes if @chunked_nodes

  centralities = networkx.eigenvector_centrality(whole_graph) # { node_name => centrality }
  sorted_nodes = centralities.sort_by { |_node, centrality| centrality }.reverse.map(&:first)

  chunk_sizes = []
  total_nodes = sorted_nodes.size
  while chunk_sizes.sum < total_nodes
    chunk_sizes << (CHUNK_SIZE ** (chunk_sizes.size + 1))
  end

  offset = 0
  @chunked_nodes = chunk_sizes.each_with_object([]) do |size, nodes|
    slice = sorted_nodes[offset, size]
    break nodes if slice.nil? || slice.empty?
    offset += size
    nodes << slice
  end
end

#connectionsObject



95
96
97
98
99
100
# File 'lib/erd_map/graph.rb', line 95

def connections
  @connections ||= edges.each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |(a, b), hash|
    hash[a] << b
    hash[b] << a
  end
end

#edgesObject



87
88
89
# File 'lib/erd_map/graph.rb', line 87

def edges
  @edges ||= PyCall::List.new(whole_graph.edges)
end

#initial_layoutObject



75
76
77
# File 'lib/erd_map/graph.rb', line 75

def initial_layout
  layouts_by_chunk.first
end

#initial_nodesObject



71
72
73
# File 'lib/erd_map/graph.rb', line 71

def initial_nodes
  chunked_nodes.first
end

#layouts_by_chunkObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/erd_map/graph.rb', line 9

def layouts_by_chunk
  return @layouts_by_chunk if @layouts_by_chunk

  @layouts_by_chunk = []

  chunked_nodes.each_with_index do |_, i|
    display_nodes = chunked_nodes[0..i].flatten
    nodes_size = display_nodes.size
    k = 1.0 / Math.sqrt(nodes_size) * 3.0

    subgraph = whole_graph.subgraph(display_nodes)
    layout = networkx.spring_layout(subgraph, seed: 1, k: k)

    layout_hash = {}
    layout.each do |node, xy|
      layout_hash[node] = [xy[0].to_f, xy[1].to_f]
    end

    @layouts_by_chunk << layout_hash
  end

  @layouts_by_chunk
end

#node_colorsObject



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/erd_map/graph.rb', line 125

def node_colors
  return @node_colors if @node_colors

  palette = [
    "#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99",
    "#e74446", "#fdbf6f", "#ff7f00", "#cab2d6", "#7850a4",
    "#ffff99", "#b8693d", "#8dd3c7", "#ffffb3", "#bebada",
    "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5",
    "#d9d9d9", "#bc80bd", "#ccebc5", "#ffed6f", "#1b9e77",
    "#d95f02", "#7570b3", "#ef73b2", "#66a61e", "#e6ab02"
  ]
  community_map = node_with_community_index
  @node_colors = node_names.map do |node_name|
    community_index = community_map[node_name]
    palette[community_index % palette.size]
  end
end

#node_namesObject



83
84
85
# File 'lib/erd_map/graph.rb', line 83

def node_names
  @node_names ||= PyCall::List.new(whole_graph.nodes)
end

#node_radiusObject



91
92
93
# File 'lib/erd_map/graph.rb', line 91

def node_radius
  @node_radius ||= node_names.map { |node_name| nodes_with_radius_according_to_chunk_index[node_name] }
end

#node_with_community_indexObject



56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/erd_map/graph.rb', line 56

def node_with_community_index
  return @node_with_community_index if @node_with_community_index

  whole_communities = networkx_community.louvain_communities(whole_graph).map { |communities| PyCall::List.new(communities).to_a }
  communities = split_communities(whole_graph, whole_communities)

  @node_with_community_index = {}
  communities.each_with_index do |community, i|
    community.each do |node_name|
      @node_with_community_index[node_name] = i
    end
  end
  @node_with_community_index
end

#nodes_with_i18n_labelsObject



102
103
104
105
106
107
108
109
# File 'lib/erd_map/graph.rb', line 102

def nodes_with_i18n_labels
  return @nodes_with_i18n_labels if @nodes_with_i18n_labels
  @nodes_with_i18n_labels = {}
  whole_models.each do |model|
    @nodes_with_i18n_labels[model.name] = model.model_name.human(default: "")
  end
  @nodes_with_i18n_labels
end

#whole_layoutObject



79
80
81
# File 'lib/erd_map/graph.rb', line 79

def whole_layout
  layouts_by_chunk.last
end