Add verbose mode, off by default. Collect and print statistics
authorMagnus Hagander <magnus@hagander.net>
Wed, 20 Jun 2012 20:21:34 +0000 (22:21 +0200)
committerMagnus Hagander <magnus@hagander.net>
Wed, 20 Jun 2012 20:21:34 +0000 (22:21 +0200)
loader/lib/parser.py
loader/lib/storage.py
loader/load_message.py

index dd8d215b4de107ec5785188c5458743043fabfcf..509b1c1c7b8afb0574fb11aafee348d405e07a12 100644 (file)
@@ -6,6 +6,7 @@ from email.parser import Parser
 from email.header import decode_header
 
 from lib.exception import IgnorableException
+from lib.log import log
 
 class ArchivesParser(object):
        def __init__(self):
@@ -25,8 +26,8 @@ class ArchivesParser(object):
                self.attachments = []
                self.get_attachments()
                if len(self.attachments) > 0:
-                       print "Found %s attachments" % len(self.attachments)
-                       print [(a[0],a[1],len(a[2])) for a in self.attachments]
+                       log.status("Found %s attachments" % len(self.attachments))
+                       log.status([(a[0],a[1],len(a[2])) for a in self.attachments])
 
                # Build an list of the message id's we are interested in
                self.parents = []
@@ -97,7 +98,7 @@ class ArchivesParser(object):
                for p in container.get_payload():
                        if p.get_params() == None:
                                # MIME multipart/mixed, but no MIME type on the part
-                               print "Found multipart/mixed in message '%s', but no MIME type on part. Trying text/plain." % self.msgid
+                               log.log("Found multipart/mixed in message '%s', but no MIME type on part. Trying text/plain." % self.msgid)
                                return self.get_payload_as_unicode(p)
                        if p.get_params()[0][0].lower() == 'text/plain':
                                # Don't include it if it looks like an attachment
@@ -166,7 +167,7 @@ class ArchivesParser(object):
                m = self.re_msgid.match(messageid)
                if not m:
                        if ignorebroken:
-                               print "Could not parse messageid '%s', ignoring it" % messageid
+                               log.log("Could not parse messageid '%s', ignoring it" % messageid)
                                return None
                        raise Exception("Could not parse message id '%s'" % messageid)
                return m.groups(1)[0]
@@ -194,7 +195,7 @@ class ArchivesParser(object):
                                dp = datetime.datetime(*dp.utctimetuple()[:6])
                        return dp
                except Exception, e:
-                       print "Failed to parse date '%s'" % d
+                       log.log("Failed to parse date '%s'" % d)
                        raise e
 
        def decode_mime_header(self, hdr):
index 11861068b7d6f6102aa5ceec496fa224c771f2e0..962cf57120adc7379d1c7ee9933b21e285155bc0 100644 (file)
@@ -1,5 +1,7 @@
 from parser import ArchivesParser
 
+from lib.log import log, opstatus
+
 class ArchivesParserStorage(ArchivesParser):
        def __init__(self):
                super(ArchivesParserStorage, self).__init__()
@@ -24,14 +26,15 @@ class ArchivesParserStorage(ArchivesParser):
                if len(r) > 0:
                        # Has to be 1 row, since we have a unique index on id
                        if not r[0][1]:
-                               print "Tagging message %s with list %s" % (self.msgid, listid)
+                               log.status("Tagging message %s with list %s" % (self.msgid, listid))
                                curs.execute("INSERT INTO list_threads (threadid, listid) VALUES (%(threadid)s, %(listid)s)", {
                                                'threadid': r[0][0],
                                                'listid': listid,
                                                })
 
                        #FIXME: option to overwrite existing message!
-                       print "Message %s already stored" % self.msgid
+                       log.status("Message %s already stored" % self.msgid)
+                       opstatus.dupes += 1
                        return
 
                # Resolve own thread
@@ -59,7 +62,7 @@ class ArchivesParserStorage(ArchivesParser):
                        # Slice away all matches that are worse than the one we wanted
                        self.parents = self.parents[:best_parent]
 
-                       print "Message %s resolved to existing thread %s, waiting for %s better messages" % (self.msgid, self.threadid, len(self.parents))
+                       log.status("Message %s resolved to existing thread %s, waiting for %s better messages" % (self.msgid, self.threadid, len(self.parents)))
                else:
                        # No parent exist. But don't create the threadid just yet, since
                        # it's possible that we're somebody elses parent!
@@ -84,7 +87,7 @@ class ArchivesParserStorage(ArchivesParser):
                        mergethreads = set([r[2] for r in childrows]).difference(set((self.threadid,)))
                        if len(mergethreads):
                                # We have one or more merge threads
-                               print "Merging threads %s into thread %s" % (",".join(str(s) for s in mergethreads), self.threadid)
+                               log.status("Merging threads %s into thread %s" % (",".join(str(s) for s in mergethreads), self.threadid))
                                curs.execute("UPDATE messages SET threadid=%(threadid)s WHERE threadid=ANY(%(oldthreadids)s)", {
                                                'threadid': self.threadid,
                                                'oldthreadids': list(mergethreads),
@@ -116,7 +119,7 @@ class ArchivesParserStorage(ArchivesParser):
                        # No parent and no child exists - create a new threadid, just for us!
                        curs.execute("SELECT nextval('threadid_seq')")
                        self.threadid = curs.fetchall()[0][0]
-                       print "Message %s resolved to no parent (out of %s) and no child, new thread %s" % (self.msgid, len(self.parents), self.threadid)
+                       log.status("Message %s resolved to no parent (out of %s) and no child, new thread %s" % (self.msgid, len(self.parents), self.threadid))
 
                # Insert a thread tag if we're on a new list
                curs.execute("INSERT INTO list_threads (threadid, listid) SELECT %(threadid)s, %(listid)s WHERE NOT EXISTS (SELECT * FROM list_threads t2 WHERE t2.threadid=%(threadid)s AND t2.listid=%(listid)s) RETURNING threadid", {
@@ -124,7 +127,7 @@ class ArchivesParserStorage(ArchivesParser):
                        'listid': listid,
                        })
                if len(curs.fetchall()):
-                       print "Tagged thread %s with listid %s" % (self.threadid, listid)
+                       log.status("Tagged thread %s with listid %s" % (self.threadid, listid))
 
                curs.execute("INSERT INTO messages (parentid, threadid, _from, _to, cc, subject, date, has_attachment, messageid, bodytxt) VALUES (%(parentid)s, %(threadid)s, %(from)s, %(to)s, %(cc)s, %(subject)s, %(date)s, %(has_attachment)s, %(messageid)s, %(bodytxt)s) RETURNING id", {
                                'parentid': self.parentid,
@@ -149,7 +152,7 @@ class ArchivesParserStorage(ArchivesParser):
                                                } for a in self.attachments])
 
                if len(self.children):
-                       print "Setting %s other threads to children of %s" % (len(self.children), self.msgid)
+                       log.status("Setting %s other threads to children of %s" % (len(self.children), self.msgid))
                        curs.executemany("UPDATE messages SET parentid=%(parent)s WHERE id=%(id)s",
                                                         [{'parent': id, 'id': c} for c in self.children])
                if len(self.parents):
@@ -157,3 +160,5 @@ class ArchivesParserStorage(ArchivesParser):
                        # properly threaded - so store them in the db.
                        curs.executemany("INSERT INTO unresolved_messages (message, priority, msgid) VALUES (%(id)s, %(priority)s, %(msgid)s)",
                                                         [{'id': id, 'priority': i, 'msgid': self.parents[i]} for i in range(0, len(self.parents))])
+
+               opstatus.stored += 1
index 417d8961c2a61c6f888eb3eeb503592c4b439114..0223a9c7cc373c4ee26113e14e9b5803cb2d93ff 100755 (executable)
@@ -14,6 +14,7 @@ import psycopg2
 from lib.storage import ArchivesParserStorage
 from lib.mbox import MailboxBreakupParser
 from lib.exception import IgnorableException
+from lib.log import log, opstatus
 
 
 if __name__ == "__main__":
@@ -22,6 +23,7 @@ if __name__ == "__main__":
        optparser.add_option('-d', '--directory', dest='directory', help='Load all messages in directory')
        optparser.add_option('-m', '--mbox', dest='mbox', help='Load all messages in mbox')
        optparser.add_option('-i', '--interactive', dest='interactive', action='store_true', help='Prompt after each message')
+       optparser.add_option('-v', '--verbose', dest='verbose', action='store_true', help='Verbose output')
 
        (opt, args) = optparser.parse_args()
 
@@ -40,6 +42,8 @@ if __name__ == "__main__":
                optparser.print_usage()
                sys.exit(1)
 
+       log.set(opt.verbose)
+
        # Yay for hardcoding
        conn = psycopg2.connect("host=/tmp dbname=archives")
 
@@ -50,7 +54,7 @@ if __name__ == "__main__":
                        })
        r = curs.fetchall()
        if len(r) != 1:
-               print "List %s not found" % opt.list
+               log.error("List %s not found" % opt.list)
                conn.close()
                sys.exit(1)
        listid = r[0][0]
@@ -58,14 +62,15 @@ if __name__ == "__main__":
        if opt.directory:
                # Parse all files in directory
                for x in os.listdir(opt.directory):
-                       print "Parsing file %s" % x
+                       log.status("Parsing file %s" % x)
                        with open(os.path.join(opt.directory, x)) as f:
                                ap = ArchivesParserStorage()
                                ap.parse(f)
                                try:
                                        ap.analyze()
                                except IgnorableException, e:
-                                       print "%s :: ignoring" % e
+                                       log.log("%s :: ignoring" % e)
+                                       opstatus.failed += 1
                                        continue
                                ap.store(conn, listid)
                        if opt.interactive:
@@ -87,12 +92,13 @@ if __name__ == "__main__":
                        try:
                                ap.analyze()
                        except IgnorableException, e:
-                               print "%s :: ignoring" % e
+                               log.log("%s :: ignoring" % e)
+                               opstatus.failed += 1
                                continue
                        ap.store(conn, listid)
                if mboxparser.returncode():
-                       print "Failed to parse mbox:"
-                       print mboxparser.stderr_output()
+                       log.error("Failed to parse mbox:")
+                       log.error(mboxparser.stderr_output())
                        sys.exit(1)
        else:
                # Parse single message on stdin
@@ -101,12 +107,13 @@ if __name__ == "__main__":
                try:
                        ap.analyze()
                except IgnorableException, e:
-                       print "%s :: ignoring" % e
+                       log.log("%s :: ignoring" % e)
                        conn.close()
                        sys.exit(1)
                ap.store(conn, listid)
 
-       print "Committing..."
+       log.status("Committing...")
        conn.commit()
-       print "Done."
+       log.status("Done.")
        conn.close()
+       opstatus.print_status()