class Birdwatcher::Modules::Users::SocialGraph

Public Class Methods

info() click to toggle source
# File lib/birdwatcher/modules/users/social_graph.rb, line 33
        def self.info
<<-INFO
The Social Graph module generates an undirected graph between users in the
currently active workspace. The edges between users will be weighted by simply
counting the amount of times they mutually mention each other in statuses. The
module can identify social clusters within the collection of users.

The generated graph is in PNG format.
INFO
        end

Public Instance Methods

run() click to toggle source
# File lib/birdwatcher/modules/users/social_graph.rb, line 44
def run
  if !GraphViz::Constants::FORMATS.include?(option_setting("FORMAT"))
    error("Unsupported format: #{option_setting('FORMAT').bold}")
    return false
  end
  if screen_names = option_setting("USERS")
    users = current_workspace.users_dataset
      .where("screen_name IN ?", screen_names.split(" ").map(&:strip))
      .order(:screen_name)
  else
    users = current_workspace.users_dataset.order(:screen_name)
  end
  if users.empty?
    error("There are no users to process")
    return false
  end
  graph = GraphViz.new(:G, :type => :graph, :use => "sfdp", :overlap => "prism", :splines => "curved")
  edge_weights = {}
  highest_edge_weight = 0
  nodes = {}
  users.each do |user|
    task("Calculating social graph for #{user.screen_name.bold}...") do
      users.each do |other_user|
        next if user.id == other_user.id
        edge_weights[user.screen_name] ||= {}
        edge_weights[other_user.screen_name] ||= {}
        next if (edge_weights[user.screen_name].key?(other_user.screen_name) && edge_weights[other_user.screen_name].key?(user.screen_name))
        edge_weights[user.screen_name][other_user.screen_name] = user.statuses_dataset.where("text LIKE ?", "%#{other_user.screen_name}%").count
        edge_weights[other_user.screen_name][user.screen_name] = other_user.statuses_dataset.where("text LIKE ?", "%#{user.screen_name}%").count
        total_edge_weight = edge_weights[user.screen_name][other_user.screen_name] + edge_weights[other_user.screen_name][user.screen_name]
        highest_edge_weight = total_edge_weight if (total_edge_weight > highest_edge_weight)
      end
    end
  end
  task("Generating social graph...") do
    edge_weights.each_pair do |user, user_graph|
      user_graph.each_pair do |other_user, edge_weight|
        total_edge_weight = edge_weight + edge_weights[other_user][user]
        percentage = (total_edge_weight.to_f / highest_edge_weight.to_f * 100).to_i
        if percentage >= option_setting("MIN_WEIGHT").to_i
          nodes[user] ||= graph.add_nodes(user)
          nodes[other_user] ||= graph.add_nodes(other_user)
          case percentage
          when 0..25
            pen_width = 1
          when 26..50
            pen_width = 2
          when 51..75
            pen_width = 4
          when 76..100
            pen_width = 5
          end
          graph.add_edges(nodes[user], nodes[other_user], :weight => total_edge_weight, :penwidth => pen_width, :color => "lightblue", :fontcolor => "cornflowerblue", :label => total_edge_weight.to_s)
          edge_weights[other_user].delete(user)
        end
      end
    end
  end
  task("Outputting graph...") do
    graph.output(option_setting("FORMAT") => option_setting("DEST"))
  end
  info("Graph written to #{option_setting('DEST').bold}")
end