Class: EmailGraph::InteractionGraph

Inherits:
DirectedGraph show all
Defined in:
lib/email_graph/interaction_graph.rb

Overview

Directed graph of identities and their relationships, created by parsing messages.

Instance Method Summary collapse

Methods inherited from DirectedGraph

#add_edge, #add_vertex, #edge, #edges, #edges_from, #edges_to, #to_undirected, #vertices, #with_each_edge_and_inverse

Constructor Details

#initialize(messages: [], email_processor: nil) ⇒ InteractionGraph

Returns a new instance of InteractionGraph.

Parameters:

  • messages (Array<#from, #to, #cc, #bcc, #date>) (defaults to: [])

    optional message-like objects. See #add_message for specification.

  • email_processor (Proc) (defaults to: nil)

    block that should return a processed email when passed an unprocessed one. Defaults to #default_email_processor; pass Proc.new{ |e| e } for no processing.



12
13
14
15
16
17
# File 'lib/email_graph/interaction_graph.rb', line 12

def initialize(messages: [], email_processor: nil)
  super()
  @email_processor = email_processor || default_email_processor

  messages.each{ |m| add_message(m) }
end

Instance Method Details

#add_message(m, email_processor: nil) ⇒ Object

Adds a message to the graph.

Parameters:

  • m (#from, #to, #cc, #bcc, #date)

    message-like object. Field methods should return an array of objects that respond to #name and #email; #date should return an instance of Time.

  • email_processor (Proc) (defaults to: nil)

    block that should return a processed email when passed an unprocessed one. Pass Proc.new{ |e| e } for no processing.

Returns:

  • m param



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/email_graph/interaction_graph.rb', line 27

def add_message(m, email_processor: nil)
  email_processor ||= @email_processor

  # Fields are in prioritized order (e.g., if in 'to', don't process again in 'cc')
  to_emails = []
  [:to, :cc, :bcc].each do |field|
    addresses = m.send(field) || []
    addresses.each do |a|
      to = email_processor.call(a.email)
      unless to_emails.include?(to)
        from ||= email_processor.call(m.from.first.email)
         
        add_interaction(from, to, m.date)
         
        to_emails << to
      end
    end
  end

  m
end

#default_email_processorObject



68
69
70
71
72
73
74
75
76
77
# File 'lib/email_graph/interaction_graph.rb', line 68

def default_email_processor
  Proc.new do |email| 
    begin
      Normailize::EmailAddress.new(email).normalized_address
    rescue ArgumentError
      # Chokes on emails like "[email protected]"
      email.downcase
    end
  end
end

#to_mutual_graph(&edge_filter) ⇒ Object

Converts graph into an undirected one, where edges are mutual relationships.

The optional edge_filter is used for determining the mutual relationship threshold based on the edge pair. It should take an edge and its inverse as arguments and return true if a MutualRelationship should be created.



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/email_graph/interaction_graph.rb', line 54

def to_mutual_graph(&edge_filter)
  edge_filter ||= Proc.new{ |e, e_inverse| e && e_inverse }

  edge_factory = Proc.new do |e, e_inverse|
    if edge_filter.call(e, e_inverse)
      MutualRelationship.new(e.from, e.to).tap do |r|
        r.interactions.push(*(e.interactions + e_inverse.interactions))
      end
    end
  end

  to_undirected(&edge_factory)
end