diff options
author | toshio <toshio@mm3test.fedoraproject.org> | 2012-03-12 23:27:56 +0000 |
---|---|---|
committer | toshio <toshio@mm3test.fedoraproject.org> | 2012-03-12 23:27:56 +0000 |
commit | 1c4f1cb5a0332a1fcc4d2fa8cf908f029cf23594 (patch) | |
tree | 53779390ff498842d2b919da62182fe582d4d0ca /lib/notmuch.py | |
download | hyperkitty-1c4f1cb5a0332a1fcc4d2fa8cf908f029cf23594.tar.gz hyperkitty-1c4f1cb5a0332a1fcc4d2fa8cf908f029cf23594.tar.xz hyperkitty-1c4f1cb5a0332a1fcc4d2fa8cf908f029cf23594.zip |
Import the initial version of the hyperkitty archiver
Diffstat (limited to 'lib/notmuch.py')
-rw-r--r-- | lib/notmuch.py | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/lib/notmuch.py b/lib/notmuch.py new file mode 100644 index 0000000..848adea --- /dev/null +++ b/lib/notmuch.py @@ -0,0 +1,105 @@ +#-*- coding: utf-8 -*- + +from __future__ import absolute_import + +from calendar import timegm +from datetime import datetime, timedelta +import json +import os + +import bunch +import notmuch + +from mm_app.lib import gravatar_url + +# Used to remove tags that notmuch added automatically that we don't want +IGNORED_TAGS = (u'inbox', u'unread', u'signed') + +def get_ro_db(path): + # Instead of throwing an exception, notmuch bindings tend to segfault if + # the database path doesn't exist. + + # Does the notmuch db exist? + actual_db_dir = os.path.join(path, '.notmuch') + if os.access(actual_db_dir, os.W_OK|os.X_OK) and os.path.isdir(actual_db_dir): + return notmuch.Database(path, + mode=notmuch.Database.MODE.READ_ONLY) + + raise IOError('Notmuch database not present in %(path)s' % + {'path': path}) + +def get_thread_info(thread): + thread_info = bunch.Bunch() + + # + # Get information about the first email of a thread + # + + first_email = tuple(thread.get_toplevel_messages())[0] + for tag in (tg for tg in first_email.get_tags() if tg.startswith('=msgid=')): + thread_info.email_id = tag.split('=msgid=', 1)[-1] + break + first_email_data = json.loads(first_email.format_message_as_json()) + + # Python-3.3 has ''.rsplit(maxsplit=1) (keyword arg). Until then, we need + # rsplit(None, 1) to get the desired behaviour + author = first_email_data['headers']['From'].rsplit(None, 1) + if author[-1].startswith('<'): + author[-1] = author[-1][1:] + if author[-1].endswith('>'): + author[-1] = author[-1][:-1] + # This accounts for From lines without a real name, just email address + name = author[0] + email = author[-1] + thread_info.author = name + thread_info.avatar = gravatar_url(email) + + for body_part in first_email_data['body']: + try: + # The body may have many parts. We only want the part that we + # can guess is the actual text of an email message. + # For this prototype, that is defined as + # has a content-type and content keys. and the content-type + # is text/plain. When this is not a prototype, the heuristic + # should be more advanced + if body_part['content-type'] == u'text/plain': + thread_info.body = body_part['content'] + break + except KeyError: + continue + + # + # Get meta info about the thread itself + # + + # Used for sorting threads + thread_info.most_recent = thread.get_newest_date() + date_as_offset = timegm(datetime.utcnow().timetuple()) - thread_info.most_recent + thread_info.age = str(timedelta(seconds=date_as_offset)) + thread_info.title = thread.get_subject() + # Because notmuch doesn't allow us to extend the schema, everything is + # in a tag. Extract those tags that have special meaning to us + thread_info.tags = [] + thread_info.answers = [] + thread_info.liked = 0 + for tag in thread.get_tags(): + if tag.startswith('=msgid='): + msgid = tag.split('=msgid=', 1)[-1] + # The first email doesn't count as a reply :-) + if msgid != thread_info.email_id: + thread_info.answers.append(tag.split('=msgid=', 1)[-1]) + elif tag.startswith('=threadlike='): + thread_info.liked = int(tag.split('=threadlike=', 1)[-1]) + elif tag.startswith('=topic='): + thread_info.category = tag.split('=topic=', 1)[-1] + print thread_info.category + elif tag in IGNORED_TAGS: + continue + else: + thread_info.tags.append(tag) + # notmuch has this nice method call to give us the info but it returns + # it as a string instead of a list + thread_info.participants = set(thread.get_authors().replace(u'| ', u', ', 1).split(u', ')) + + + return thread_info |