1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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 hyperkitty.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
|