<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,232 +1,223 @@
 #!/usr/bin/python
-
-# Maelstrom - visualizing email contacts
-# Copyright(c) 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt;
-
-# extracts a directed graph from the database.
-#   the edges are the messages,
-#   the nodes are the persons
-#   the edges have a type to/cc and a weight (number of mails)
-
-import sys, os, cStringIO, codecs, datetime, getopt, platform, csv #TODO , ConfigParser
+&quot;&quot;&quot;
+Maelstrom - visualizing email contacts
+Copyright(c) 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt;
+
+extracts a directed graph from the database.
+  edges are the messages,
+  nodes are the persons
+  edges have a type to/cc and a weight (number of mails)
+&quot;&quot;&quot;
+
+import sys, os, cStringIO, codecs, getopt, platform, csv
+#TODO import ConfigParser
 if(platform.machine()=='i686'):
    import psyco
-from sqlobject import *
-from lib.objects import *
-from lib.utils import decode_header
+from lib.objects import Role
 
 CONFIG = {'database': os.path.abspath('../db/messages.db'),
           'personmapfile' : '../db/persons.map',
-          'format': &quot;csv&quot;, # dot|graphxml|log|csv
-          'stats': False,   # display overall stats
+          'format': &quot;dot&quot;, # dot|graphxml|log|csv
+          'stats': True,   # display overall stats
           'egg': False,    # filter out entity:
           'egger': 'Marsiske Stefan',
           }
-#config = ConfigParser.ConfigParser()
-#config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')])
+#TODO config = ConfigParser.ConfigParser()
+#TODO config.read(['maelstrom.cfg', os.path.expanduser('~/.maelstrom.cfg')])
 
 def usage():
+   &quot;&quot;&quot;Prints out the --help&quot;&quot;&quot;
    print &quot;usage: %s&quot; % (sys.argv[0])
    print &quot;\t-h                                  This Help&quot;
-   print &quot;\t-s|--stats                          Display top ten stats on in/out contacts.&quot;
+   print &quot;\t-s|--stats                          Display stats&quot;
 #   print &quot;\t-f |--format== [dot,log,graphxml]   Output format.&quot;
    print &quot;\t-f |--format== [dot,log,csv]        Output format.&quot;
 
+class Obj:
+   &quot;&quot;&quot;
+   abstract baseclass for node,edge,graph
+   &quot;&quot;&quot;
+   def __getattr__(self, name):
+      if(self.__dict__.has_key(name)):
+         return self.__dict__[name]
+      else:
+         raise AttributeError, name
+
+   def __setattr__(self, name, value):
+      if(self.__dict__.has_key(name)):
+         self.__dict__[name] = value
+      else:
+         raise AttributeError, name
+
+   def __repr__(self):
+      return self.__str__()
+
+   def __str__(self):
+      return reduce(lambda y, x: y + x + &quot;: &quot; + repr(self.__dict__[x]) + &quot;\n&quot;,
+                    self.__dict__.keys())
+
 def counter(start=0):
-    while True:
-        start+=1
-        yield start
-nodeIdGenerator=iter(counter(0))
-edgeIdGenerator=iter(counter(0))
+   &quot;&quot;&quot;
+   auto incrementing id generator
+   &quot;&quot;&quot;
+   while True:
+      start += 1
+      yield start
+# ID generators for nodes and eges
+NodeIdGenerator = iter(counter(0))
+EdgeIdGenerator = iter(counter(0))
+
+class Node(Obj):
+   &quot;&quot;&quot;
+   Simple graph node, that keeps track of the edges by
+    - direction,
+    - time and
+    - type/timestamp
+   &quot;&quot;&quot;
+   def __init__(self, name):
+      self.__dict__['id'] = &quot;n&quot;+str(NodeIdGenerator.next())
+      self.__dict__['name'] = name
+      self.__dict__['srcTypeStamps'] = []
+      self.__dict__['dstTypeStamps'] = []
+
+   def __str__(self):
+      return &quot;%s\t(Wo:%d, Wi:%d)\n&quot; % (self.name,
+                                       len(self.srcTypeStamps),
+                                       len(self.dstTypeStamps))
+
+   def incSrcWeight(self, mode, date):
+      &quot;&quot;&quot;
+      add an incomming edge to the node
+      &quot;&quot;&quot;
+      self.__dict__['srcTypeStamps'].append((date, mode))
+
+   def incDstWeight(self, mode, date):
+      &quot;&quot;&quot;
+      add an outgoing edge to the node
+      &quot;&quot;&quot;
+      self.__dict__['dstTypeStamps'].append((date, mode))
+
+class Edge(Obj):
+   &quot;&quot;&quot;
+   Simple graph edge, that keeps track of the nodes by
+    - type/timestamp
+   &quot;&quot;&quot;
+   def __init__(self, sender, receiver):
+      self.__dict__['id'] = &quot;e&quot;+str(EdgeIdGenerator.next())
+      self.__dict__['sender'] = sender
+      self.__dict__['to'] = receiver
+      self.__dict__['typestamps'] = []
+
+   def __str__(self):
+      return &quot;%s -&gt; %s W:%d\n&quot; % (self.sender,
+                                  self.to,
+                                  len(self.typestamps))
+   def incWeight(self, mode, date):
+      &quot;&quot;&quot;
+      adjust the weight of the edge
+      &quot;&quot;&quot;
+      self.__dict__['typestamps'].append((date, mode))
+
+class Graph(Obj):
+   &quot;&quot;&quot;
+   graph object: implements
+    - building
+    - exporting as dot,log,csv
+    - gathering statistics
+   &quot;&quot;&quot;
+   def __init__(self):
+      self.__dict__['nodes'] = {}
+      self.__dict__['edges'] = {}
+
+   def __str__(self):
+      return &quot;Nodes: %s\nEdges: %s\n&quot; % (self.nodes, self.edges)
+
+   def addToGraph(self, sender, mode, to, date):
+      src = edge = dst = None
+      # create nodes if not yet seen
+      if(not sender in self.nodes.keys()):
+         src = Node(sender)
+         self.nodes[sender] = src
+      else:
+         src = self.nodes[sender]
 
-class UnicodeWriter:
-    &quot;&quot;&quot;
-    A CSV writer which will write rows to CSV file &quot;f&quot;,
-    which is encoded in the given encoding.
-    &quot;&quot;&quot;
-    def __init__(self, f, dialect=csv.excel, encoding=&quot;utf-8&quot;, **kwds):
-        # Redirect output to a queue
-        self.queue = cStringIO.StringIO()
-        self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
-        self.stream = f
-        self.encoder = codecs.getincrementalencoder(encoding)()
-
-    def writerow(self, row):
-        #ORIG:self.writer.writerow([s.encode(&quot;utf-8&quot;) for s in row])
-        self.writer.writerow(row)
-        # Fetch UTF-8 output from the queue ...
-        data = self.queue.getvalue()
-        data = data.decode(&quot;utf-8&quot;)
-        # ... and reencode it into the target encoding
-        data = self.encoder.encode(data)
-        # write to the target stream
-        self.stream.write(data)
-        # empty queue
-        self.queue.truncate(0)
-
-class Node:
-    def __init__(self,name,receivers = []):
-        self.__dict__['id'] = &quot;n&quot;+str(nodeIdGenerator.next())
-        self.__dict__['name'] = name
-        self.__dict__['srcTypeStamps'] = []
-        self.__dict__['dstTypeStamps'] = []
-
-    def __getattr__(self, name):
-        if(self.__dict__.has_key(name)):
-            return self.__dict__[name]
-        else:
-            raise AttributeError, name
-
-    def __setattr__(self, name, value):
-        if(self.__dict__.has_key(name)):
-            self.__dict__[name] = value
-        else:
-            raise AttributeError, name
-
-    def __str__(self):
-        #return reduce(self.__dict__.keys(), lambda y,x: y+x+&quot;: &quot;+repr(self.__dict__[x])+&quot;\n&quot;)
-        return &quot;%s\t(Wo:%d, Wi:%d)\n&quot; % (self.name,
-                                           len(self.srcTypeStamps),
-                                           len(self.dstTypeStamps))
-    def __repr__(self):
-        return self.__str__()
-
-    def incSrcWeight(self,type,date):
-        self.__dict__['srcTypeStamps'].append((date,type))
-
-    def incDstWeight(self,type,date):
-        self.__dict__['dstTypeStamps'].append((date,type))
-
-class Edge:
-    def __init__(self,sender,to):
-        self.__dict__['id'] = &quot;e&quot;+str(edgeIdGenerator.next())
-        self.__dict__['sender'] = sender
-        self.__dict__['to'] = to
-        self.__dict__['typestamps'] = []
-
-    def __getattr__(self, name):
-        if(self.__dict__.has_key(name)):
-            return self.__dict__[name]
-        else:
-            raise AttributeError, name
-
-    def __setattr__(self, name, value):
-        if(self.__dict__.has_key(name)):
-            self.__dict__[name] = value
-        else:
-            raise AttributeError, name
-
-    def __str__(self):
-        #return reduce(self.__dict__.keys(), lambda y,x: y+x+&quot;: &quot;+repr(self.__dict__[x])+&quot;\n&quot;)
-        return &quot;%s -&gt; %s W:%d\n&quot; % (self.sender,
-                                self.to,
-                                len(self.typestamps))
-    def __repr__(self):
-        return self.__str__()
-
-    def incWeight(self,type,date):
-        self.__dict__['typestamps'].append((date,type))
-
-class Graph:
-    def __init__(self):
-        self.__dict__['nodes'] = {}
-        self.__dict__['edges'] = {}
-
-    def __str__(self):
-        return &quot;Nodes: %s\nEdges: %s\n&quot; % (self.nodes, self.edges)
-
-    def __getattr__(self, name):
-        if(self.__dict__.has_key(name)):
-            return self.__dict__[name]
-        else:
-            raise AttributeError, name
-
-    def __setattr__(self, name, value):
-        if(self.__dict__.has_key(name)):
-            self.__dict__[name] = value
-        else:
-            raise AttributeError, name
-
-    def __repr__(self):
-        return self.__str__()
-
-    def addToGraph(self,date,sender,type,to):
-        src = edge = dst = None
-        # create nodes if not yet seen
-        if(not sender in self.nodes.keys()):
-            src = Node(sender)
-            self.nodes[sender] = src
-        else:
-            src = self.nodes[sender]
-
-        if(not to in self.nodes.keys()):
-            dst = Node(to)
-            self.nodes[to] = dst
-        else:
-            dst = self.nodes[to]
-
-        # create edge if a new one is found
-        if(not (sender,to) in self.edges.keys()):
-            edge = Edge(sender,to)
-            self.edges[(sender,to)] = edge
-        else:
-            edge = self.edges[(sender,to)]
-
-        # adjust weight on edge
-        edge.incWeight(type,date)
-
-        # adjust weight on nodes
-        src.incSrcWeight(type,date)
-        dst.incDstWeight(type,date)
-
-    def stats(self):
-        nodes = self.__dict__['nodes'].values()
-        nodes.sort(lambda x,y: cmp(len(y.srcTypeStamps)+len(y.dstTypeStamps),
-                                   len(x.srcTypeStamps)+len(x.dstTypeStamps)))
-        print &quot;Top Overall\n&quot;,nodes[:10]
-
-        # print out top senders
-        nodes=self.__dict__['nodes'].values()
-        nodes.sort(lambda x,y: cmp(len(y.srcTypeStamps),
-                                   len(x.srcTypeStamps)))
-        print &quot;Top Wo\n&quot;, nodes[:10]
-
-        # print out top recipients
-        nodes=self.__dict__['nodes'].values()
-        nodes.sort(lambda x,y: cmp(len(y.dstTypeStamps),
-                                   len(x.dstTypeStamps)))
-        print &quot;Top Wi\n&quot;, nodes[:10]
-
-        # print out top edges
-        edges = self.__dict__['edges'].values()
-        edges.sort(lambda x,y: cmp(len(y.typestamps),len(x.typestamps)))
-        print &quot;Top Overall edges\n&quot;,edges[:10]
-
-        if(CONFIG['egg']):
-            edges=filter(lambda x: ((x.sender!=CONFIG['egger'])),edges)
-            edges.sort(lambda x,y: cmp(len(y.typestamps),len(x.typestamps)))
-            print &quot;Top Overall eggs\n&quot;,edges[:10]
-
-    def dot(self):
-        result=&quot;digraph G {\noverlap = false;\nsplines=true;\n&quot;
-        result+=reduce(lambda y,x: y+'%s [ label=&quot;%s&quot;];\n' % (self.nodes[x.name].id,x.name),
-                       self.nodes.values(),&quot;&quot;)
-        result+=reduce(lambda y,x: y+'&quot;%s&quot; -&gt; &quot;%s&quot; [ weight=&quot;%d&quot; ];\n' %
-                       (self.nodes[x.sender].id, self.nodes[x.to].id, len(x.typestamps)),
-                       self.edges.values(),&quot;&quot;)
-        result+=&quot;}&quot;
-        return result
+      if(not to in self.nodes.keys()):
+         dst = Node(to)
+         self.nodes[to] = dst
+      else:
+         dst = self.nodes[to]
+
+      # create edge if a new one is found
+      if(not (sender, to) in self.edges.keys()):
+         edge = Edge(sender, to)
+         self.edges[(sender, to)] = edge
+      else:
+         edge = self.edges[(sender, to)]
+
+      # adjust weight on edge
+      edge.incWeight(mode, date)
+
+      # adjust weight on nodes
+      src.incSrcWeight(mode, date)
+      dst.incDstWeight(mode, date)
+
+   def stats(self):
+      nodes = self.__dict__['nodes'].values()
+      nodes.sort(lambda x, y: cmp(len(y.srcTypeStamps)+len(y.dstTypeStamps),
+                                 len(x.srcTypeStamps)+len(x.dstTypeStamps)))
+      print &quot;Top Overall\n&quot;, nodes[:10]
+
+      # print out top senders
+      nodes = self.__dict__['nodes'].values()
+      nodes.sort(lambda x, y: cmp(len(y.srcTypeStamps),
+                                 len(x.srcTypeStamps)))
+      print &quot;Top Wo\n&quot;, nodes[:10]
+
+      # print out top recipients
+      nodes = self.__dict__['nodes'].values()
+      nodes.sort(lambda x, y: cmp(len(y.dstTypeStamps),
+                                 len(x.dstTypeStamps)))
+      print &quot;Top Wi\n&quot;, nodes[:10]
+
+      # print out top edges
+      edges = self.__dict__['edges'].values()
+      edges.sort(lambda x, y: cmp(len(y.typestamps), len(x.typestamps)))
+      print &quot;Top Overall edges\n&quot;, edges[:10]
+
+      if(CONFIG['egg']):
+         edges = filter(lambda x: ((x.sender!=CONFIG['egger'])), edges)
+         edges.sort(lambda x, y: cmp(len(y.typestamps), len(x.typestamps)))
+         print &quot;Top Overall eggs\n&quot;, edges[:10]
+
+   def dot(self):
+      &quot;&quot;&quot;
+      outputs the graph in graphviz dot language
+      &quot;&quot;&quot;
+      result = &quot;digraph G {\noverlap = false;\nsplines=true;\n&quot;
+      result += reduce(lambda y, x: y+'%s [ label=&quot;%s&quot;];\n' %
+                     (self.nodes[x.name].id, x.name),
+                     self.nodes.values(),&quot;&quot;)
+      result += reduce(lambda y, x: y+'&quot;%s&quot; -&gt; &quot;%s&quot; [ weight=&quot;%d&quot; ];\n' %
+                     (self.nodes[x.sender].id,
+                      self.nodes[x.to].id,
+                      len(x.typestamps)),
+                     self.edges.values(),&quot;&quot;)
+      result += &quot;}&quot;
+      return result
 
 class PersonMap:
-   def __init__(self,file):
-      self.__dict__['personmap']={}
-      if(os.path.exists(file)):
-         fp = open(file,'r')
+   &quot;&quot;&quot;
+   maps names or email addresses to other names specified in a configure
+   &quot;&quot;&quot;
+   def __init__(self, fname):
+      self.__dict__['personmap'] = {}
+      if(os.path.exists(fname)):
+         fp = open(fname,'r')
          while(fp):
             line = fp.readline()
             if not line:
-                break
-            (email,name) = line.split(&quot; &quot;,1)
+               break
+            (email, name) = line.split(&quot; &quot;, 1)
             self.__dict__['personmap'][email] = name.strip()
          fp.close()
 
@@ -236,59 +227,83 @@ class PersonMap:
       else:
          return name
 
+class UnicodeWriter:
+   &quot;&quot;&quot;
+   A CSV writer which will write rows to CSV file &quot;f&quot;,
+   which is encoded in the given encoding.
+   &quot;&quot;&quot;
+   def __init__(self, f, dialect=csv.excel, encoding=&quot;utf-8&quot;, **kwds):
+      # Redirect output to a queue
+      self.queue = cStringIO.StringIO()
+      self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
+      self.stream = f
+      self.encoder = codecs.getincrementalencoder(encoding)()
+
+   def writerow(self, row):
+      #ORIG:self.writer.writerow([s.encode(&quot;utf-8&quot;) for s in row])
+      self.writer.writerow(row)
+      # Fetch UTF-8 output from the queue ...
+      data = self.queue.getvalue()
+      data = data.decode(&quot;utf-8&quot;)
+      # ... and reencode it into the target encoding
+      data = self.encoder.encode(data)
+      # write to the target stream
+      self.stream.write(data)
+      # empty queue
+      self.queue.truncate(0)
+
+def getName(email):
+   &quot;&quot;&quot;
+   returns the most specific name for an email correspondent
+   &quot;&quot;&quot;
+   if(email and email.owner):
+      return email.owner.fullname
+   elif(email):
+      return email.username+&quot;@&quot;+email.mailserver
+
 def buildGraph():
    graph = Graph()
-   q = Role.select()
    personmap = PersonMap(CONFIG['personmapfile'])
-   csvcoder=None
-   if(CONFIG['format']==&quot;csv&quot;):
-      csvcoder=UnicodeWriter(sys.stdout)
+   csvcoder = None
+   if(CONFIG['format'] == &quot;csv&quot;):
+      csvcoder = UnicodeWriter(sys.stdout)
+
+   q = Role.select()
    for edge in q:
-      sender = edge.msg
-      if(edge.msg.sender and edge.msg.sender.owner):
-         sender = edge.msg.sender.owner.fullname
-      elif(edge.msg.sender):
-         sender = edge.msg.sender.username+&quot;@&quot;+edge.msg.sender.mailserver
-      sender = personmap[sender]
-
-      receiver = edge.email
-      if(edge.email and edge.email.owner):
-         receiver = edge.email.owner.fullname
-      elif(edge.email):
-         receiver = edge.email.username+&quot;@&quot;+edge.email.mailserver
-      receiver = personmap[receiver]
+      sender = personmap[getName(edge.msg.sender)]
+      receiver = personmap[getName(edge.email)]
 
       if(CONFIG['format']==&quot;log&quot;):
-         print edge.msg.delivered,sender, edge.header.name, receiver
+         print edge.msg.delivered, sender, edge.header.name, receiver
       elif(CONFIG['format']==&quot;csv&quot;):
-         csvcoder.writerow(map(lambda x: str(x),(edge.msg.delivered,sender, edge.header.name, receiver)))
+         csvcoder.writerow(map(lambda x: str(x),
+                               (edge.msg.delivered,
+                                sender,
+                                edge.header.name,
+                                receiver)))
 
-      graph.addToGraph(edge.msg.delivered,sender,edge.header.name, receiver)
+      graph.addToGraph(sender, edge.header.name, receiver, edge.msg.delivered)
    return graph
 
 def dumpResults(graph):
-    #print graph
-    if(CONFIG['format']==&quot;dot&quot;):
-        print graph.dot()
-    if(CONFIG['stats']):
-        print graph.stats()
+   #print graph
+   if(CONFIG['format']==&quot;dot&quot;):
+      print graph.dot()
+   if(CONFIG['stats']):
+      print graph.stats()
 
 def main():
-    graph = buildGraph()
-    dumpResults(graph)
-
-if __name__=='__main__':
    try:
-       opts, args = getopt.gnu_getopt(sys.argv[1:],
-                                      &quot;hesf:d:&quot;,
-                                      [&quot;help&quot;,
-                                       &quot;egg&quot;,
-                                       &quot;stats&quot;,
-                                       &quot;format=&quot;,
-                                       &quot;database=&quot;])
+      opts, args = getopt.gnu_getopt(sys.argv[1:],
+                                     &quot;hesf:d:&quot;,
+                                     [&quot;help&quot;,
+                                      &quot;egg&quot;,
+                                      &quot;stats&quot;,
+                                      &quot;format=&quot;,
+                                      &quot;database=&quot;])
    except getopt.GetoptError:
-       usage()
-       sys.exit(2)
+      usage()
+      sys.exit(2)
    for o, a in opts:
       if o in (&quot;-h&quot;, &quot;--help&quot;):
          usage()
@@ -297,15 +312,19 @@ if __name__=='__main__':
          if(a and os.path.isfile(a)):
             CONFIG['database'] = a
       elif o in (&quot;-e&quot;, &quot;--egg&quot;):
-          CONFIG['egg'] = False
+         CONFIG['egg'] = False
       elif o in (&quot;-s&quot;, &quot;--stats&quot;):
-          CONFIG['stats'] = True
+         CONFIG['stats'] = True
       elif o in (&quot;-f&quot;, &quot;--format&quot;):
          if(a and a in (&quot;dot&quot;, &quot;log&quot;, &quot;graphxml&quot;, &quot;csv&quot;)):
             CONFIG['format'] = a
          else:
             usage()
             sys.exit()
+   graph = buildGraph()
+   dumpResults(graph)
+
+if __name__ == '__main__':
    if(platform.machine()=='i686'):
       psyco.full()
    sys.exit(main())</diff>
      <filename>utils/getgraph</filename>
    </modified>
    <modified>
      <diff>@@ -60,17 +60,17 @@ class HeaderValue(sqlobject.SQLObject):
     msg = sqlobject.col.ForeignKey('Message')
     header = sqlobject.col.ForeignKey('Header')
 
-def main():
-    &quot;&quot;&quot; this function creates a new database&quot;&quot;&quot;
-    Header.createTable(ifNotExists = True)
-    HeaderValue.createTable(ifNotExists = True)
-    Person.createTable(ifNotExists = True)
-    Email.createTable(ifNotExists = True)
-    Role.createTable(ifNotExists = True)
-    Message.createTable(ifNotExists = True)
-
 &quot;&quot;&quot; if being executed instead of loaded as a module, create a new
 database&quot;&quot;&quot;
 if (__name__ == '__main__'):
-    psyco.full()
-    sys.exit(main())
+   def main():
+      &quot;&quot;&quot; this function creates a new database&quot;&quot;&quot;
+      Header.createTable(ifNotExists = True)
+      HeaderValue.createTable(ifNotExists = True)
+      Person.createTable(ifNotExists = True)
+      Email.createTable(ifNotExists = True)
+      Role.createTable(ifNotExists = True)
+      Message.createTable(ifNotExists = True)
+
+   psyco.full()
+   sys.exit(main())</diff>
      <filename>utils/lib/objects.py</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>8b7953a39d78659395f9150d9e8cb7e7ad920cb2</id>
    </parent>
  </parents>
  <author>
    <name>Stefan Marsiske</name>
    <email>stefan.marsiske@gmail.com</email>
  </author>
  <url>http://github.com/stef/maelstrom/commit/47c5c606c5eba20dd7c1a0d0e081f48c2af57a64</url>
  <id>47c5c606c5eba20dd7c1a0d0e081f48c2af57a64</id>
  <committed-date>2009-03-07T18:38:16-08:00</committed-date>
  <authored-date>2009-03-07T18:38:16-08:00</authored-date>
  <message>[enh] pylint compliance improved</message>
  <tree>ba78d6f58202d42d2e71b31942a405740e493b8e</tree>
  <committer>
    <name>Stefan Marsiske</name>
    <email>stefan.marsiske@gmail.com</email>
  </committer>
</commit>
