summaryrefslogtreecommitdiff
path: root/alot
diff options
context:
space:
mode:
authorpazz <patricktotzke@gmail.com>2011-07-23 18:50:54 +0100
committerpazz <patricktotzke@gmail.com>2011-07-23 18:50:54 +0100
commit23f4e2307a77f8ae43f47ef3f1e0ed686371dd6f (patch)
tree290b22bcbca54ff701a89db679064655dcdcdab1 /alot
parent475b65bde3ee4a331676a8ac8ebbd2f91d049e22 (diff)
separated Message from db
since Message also includes mime-handler calling ist doesn't really fit anymore
Diffstat (limited to 'alot')
-rw-r--r--alot/db.py227
-rw-r--r--alot/message.py227
-rw-r--r--alot/widgets.py1
3 files changed, 237 insertions, 218 deletions
diff --git a/alot/db.py b/alot/db.py
index a6aaac22..a6ccb4fc 100644
--- a/alot/db.py
+++ b/alot/db.py
@@ -18,18 +18,11 @@ Copyright (C) 2011 Patrick Totzke <patricktotzke@gmail.com>
"""
from notmuch import Database, NotmuchError
from datetime import datetime
-import email
from collections import deque
-import os
-import logging
-import tempfile
-from settings import config
-from settings import get_mime_handler
-import helper
-from helper import cmd_output
+from message import Message
-DB_ENC = 'utf8'
+DB_ENC = 'utf-8'
class DatabaseError(Exception):
@@ -96,7 +89,8 @@ class DBManager:
sync_maildir_flags=sync)
msg.thaw()
- def tag(self, querystring, tags, remove_rest=False, sync_maildir_flags=False):
+ def tag(self, querystring, tags, remove_rest=False,
+ sync_maildir_flags=False):
"""
add tags to all matching messages. Raises
:exc:`DatabaseROError` if in read only mode.
@@ -112,9 +106,11 @@ class DBManager:
if self.ro:
raise DatabaseROError()
if remove_rest:
- self.writequeue.append(('set', querystring, tags, sync_maildir_flags))
+ self.writequeue.append(('set', querystring, tags,
+ sync_maildir_flags))
else:
- self.writequeue.append(('tag', querystring, tags, sync_maildir_flags))
+ self.writequeue.append(('tag', querystring, tags,
+ sync_maildir_flags))
def untag(self, querystring, tags, sync_maildir_flags=False):
"""
@@ -129,7 +125,8 @@ class DBManager:
"""
if self.ro:
raise DatabaseROError()
- self.writequeue.append(('untag', querystring, tags, sync_maildir_flags))
+ self.writequeue.append(('untag', querystring, tags,
+ sync_maildir_flags))
def count_messages(self, querystring):
"""returns number of messages that match querystring"""
@@ -181,12 +178,10 @@ class Thread:
if not thread:
query = self._dbman.query('thread:' + self._id)
thread = query.search_threads().next()
- logging.debug(thread)
self._total_messages = thread.get_total_messages()
self._authors = str(thread.get_authors()).decode(DB_ENC)
self._subject = str(thread.get_subject()).decode(DB_ENC)
ts = thread.get_oldest_date()
- logging.debug(ts)
self._oldest_date = datetime.fromtimestamp(ts)
self._newest_date = datetime.fromtimestamp(thread.get_newest_date())
self._tags = set(thread.get_tags())
@@ -302,205 +297,3 @@ class Thread:
def get_total_messages(self):
"""returns number of contained messages"""
return self._total_messages
-
-
-class Message:
- def __init__(self, dbman, msg, thread=None):
- """
- :param dbman: db manager that is used for further lookups
- :type dbman: alot.db.DBManager
- :param msg: the wrapped message
- :type msg: notmuch.database.Message
- :param thread: this messages thread
- :type thread: alot.db.thread
- """
- self._dbman = dbman
- self._id = msg.get_message_id()
- self._thread_id = msg.get_thread_id()
- self._thread = thread
- self._datetime = datetime.fromtimestamp(msg.get_date())
- self._filename = msg.get_filename()
- # TODO: change api to return unicode
- self._from = msg.get_header('From').decode(DB_ENC)
- self._email = None # will be read upon first use
- self._attachments = None # will be read upon first use
- self._tags = set(msg.get_tags())
-
- def __str__(self):
- """prettyprint the message"""
- aname, aaddress = self.get_author()
- if not aname:
- aname = aaddress
- #tags = ','.join(self.get_tags())
- return "%s (%s)" % (aname, self.get_datestring())
-
- def __hash__(self):
- """Implement hash(), so we can use Message() sets"""
- return hash(self._id)
-
- def __cmp__(self, other):
- """Implement cmp(), so we can compare Message()s"""
- res = cmp(self.get_message_id(), other.get_message_id())
- return res
-
- def get_email(self):
- """returns email.Message representing this message"""
- if not self._email:
- f_mail = open(self.get_filename())
- self._email = email.message_from_file(f_mail)
- f_mail.close()
- return self._email
-
- def get_date(self):
- """returns date as datetime obj"""
- return self._datetime
-
- def get_filename(self):
- """returns absolute path of messages location"""
- return self._filename
-
- def get_message_id(self):
- """returns messages id (a string)"""
- return self._id
-
- def get_thread_id(self):
- """returns id of messages thread (a string)"""
- return self._thread_id
-
- def get_message_parts(self):
- """returns a list of all body parts of this message"""
- out = []
- for msg in self.get_email().walk():
- if not msg.is_multipart():
- out.append(msg)
- return out
-
- def get_tags(self):
- """returns tags attached to this message as list of strings"""
- return list(self._tags)
-
- def get_thread(self):
- """returns the thread this msg belongs to as alot.db.Thread object"""
- if not self._thread:
- self._thread = self._dbman.get_thread(self._thread_id)
- return self._thread
-
- def get_replies(self):
- """returns a list of replies to this msg"""
- t = self.get_thread()
- return t.get_replies_to(self)
-
- def get_datestring(self, pretty=True):
- """returns formated datestring in sup-style, eg: 'Yest.3pm'"""
- return helper.pretty_datetime(self._datetime)
-
- def get_author(self):
- """returns realname and address pair of this messages author"""
- return email.Utils.parseaddr(self._from)
-
- def add_tags(self, tags):
- """adds tags to message
-
- :param tags: tags to add
- :type tags: list of str
- """
- self._dbman.tag('id:' + self._id, tags)
- self._tags = self._tags.union(tags)
-
- def remove_tags(self, tags):
- """remove tags from message
-
- :param tags: tags to remove
- :type tags: list of str
- """
- self._dbman.untag('id:' + self._id, tags)
- self._tags = self._tags.difference(tags)
-
- def get_attachments(self):
- if not self._attachments:
- self._attachments = []
- for part in self.get_message_parts():
- if part.get_content_maintype() != 'text':
- self._attachments.append(Attachment(part))
- return self._attachments
-
- def accumulate_body(self):
- bodytxt = ''
- for part in self.get_email().walk():
- ctype = part.get_content_type()
- enc = part.get_content_charset()
- raw_payload = part.get_payload(decode=True)
- if part.get_content_maintype() == 'text':
- if enc:
- raw_payload = unicode(raw_payload, enc)
- else:
- raw_payload = unicode(raw_payload, errors='replace')
- if ctype == 'text/plain':
- bodytxt += raw_payload
- else:
- #get mime handler
- handler = get_mime_handler(ctype, key='view',
- interactive=False)
- if handler:
- #open tempfile. Not all handlers accept stuff from stdin
- tmpfile = tempfile.NamedTemporaryFile(delete=False,
- suffix='.html')
- #write payload to tmpfile
- if part.get_content_maintype() == 'text':
- tmpfile.write(raw_payload.encode('utf8'))
- else:
- tmpfile.write(raw_payload)
- #create and call external command
- cmd = handler % tmpfile.name
- rendered_payload = cmd_output(cmd)
- #remove tempfile
- tmpfile.close()
- os.unlink(tmpfile.name)
- if rendered_payload: # handler had output
- bodytxt += unicode(rendered_payload.strip(),
- encoding='utf8', errors='replace')
- elif part.get_content_maintype() == 'text':
- bodytxt += raw_payload
- # else drop
- return bodytxt
-
-
-class Attachment:
- """represents a single mail attachment"""
-
- def __init__(self, emailpart):
- """
- :param emailpart: a non-multipart email that is the attachment
- :type emailpart: email.message.Message
- """
- self.part = emailpart
-
- def __str__(self):
- return '%s:%s (%s)' % (self.get_content_type(),
- self.get_filename(),
- self.get_size())
-
- def get_filename(self):
- """return the filename, extracted from content-disposition header"""
- return self.part.get_filename()
-
- def get_content_type(self):
- """mime type of the attachment"""
- return self.part.get_content_type()
-
- def get_size(self):
- """returns attachments size as human-readable string"""
- size_in_kbyte = len(self.part.get_payload()) / 1024
- if size_in_kbyte > 1024:
- return "%.1fM" % (size_in_kbyte / 1024.0)
- else:
- return "%dK" % size_in_kbyte
-
- def save(self, path):
- """save the attachment to disk. Uses self.get_filename
- in case path is a directory"""
- if os.path.isdir(path):
- path = os.path.join(path, self.get_filename())
- FILE = open(path, "w")
- FILE.write(self.part.get_payload(decode=True))
- FILE.close()
diff --git a/alot/message.py b/alot/message.py
new file mode 100644
index 00000000..42ba1fb7
--- /dev/null
+++ b/alot/message.py
@@ -0,0 +1,227 @@
+"""
+This file is part of alot.
+
+Alot is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Alot is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with notmuch. If not, see <http://www.gnu.org/licenses/>.
+
+Copyright (C) 2011 Patrick Totzke <patricktotzke@gmail.com>
+"""
+import os
+import email
+import tempfile
+from datetime import datetime
+
+import helper
+from settings import get_mime_handler
+
+
+class Message:
+ def __init__(self, dbman, msg, thread=None):
+ """
+ :param dbman: db manager that is used for further lookups
+ :type dbman: alot.db.DBManager
+ :param msg: the wrapped message
+ :type msg: notmuch.database.Message
+ :param thread: this messages thread
+ :type thread: alot.db.thread
+ """
+ self._dbman = dbman
+ self._id = msg.get_message_id()
+ self._thread_id = msg.get_thread_id()
+ self._thread = thread
+ self._datetime = datetime.fromtimestamp(msg.get_date())
+ self._filename = msg.get_filename()
+ # TODO: change api to return unicode
+ self._from = msg.get_header('From').decode('utf-8')
+ self._email = None # will be read upon first use
+ self._attachments = None # will be read upon first use
+ self._tags = set(msg.get_tags())
+
+ def __str__(self):
+ """prettyprint the message"""
+ aname, aaddress = self.get_author()
+ if not aname:
+ aname = aaddress
+ #tags = ','.join(self.get_tags())
+ return "%s (%s)" % (aname, self.get_datestring())
+
+ def __hash__(self):
+ """Implement hash(), so we can use Message() sets"""
+ return hash(self._id)
+
+ def __cmp__(self, other):
+ """Implement cmp(), so we can compare Message()s"""
+ res = cmp(self.get_message_id(), other.get_message_id())
+ return res
+
+ def get_email(self):
+ """returns email.Message representing this message"""
+ if not self._email:
+ f_mail = open(self.get_filename())
+ self._email = email.message_from_file(f_mail)
+ f_mail.close()
+ return self._email
+
+ def get_date(self):
+ """returns date as datetime obj"""
+ return self._datetime
+
+ def get_filename(self):
+ """returns absolute path of messages location"""
+ return self._filename
+
+ def get_message_id(self):
+ """returns messages id (a string)"""
+ return self._id
+
+ def get_thread_id(self):
+ """returns id of messages thread (a string)"""
+ return self._thread_id
+
+ def get_message_parts(self):
+ """returns a list of all body parts of this message"""
+ out = []
+ for msg in self.get_email().walk():
+ if not msg.is_multipart():
+ out.append(msg)
+ return out
+
+ def get_tags(self):
+ """returns tags attached to this message as list of strings"""
+ return list(self._tags)
+
+ def get_thread(self):
+ """returns the thread this msg belongs to as alot.db.Thread object"""
+ if not self._thread:
+ self._thread = self._dbman.get_thread(self._thread_id)
+ return self._thread
+
+ def get_replies(self):
+ """returns a list of replies to this msg"""
+ t = self.get_thread()
+ return t.get_replies_to(self)
+
+ def get_datestring(self, pretty=True):
+ """returns formated datestring in sup-style, eg: 'Yest.3pm'"""
+ return helper.pretty_datetime(self._datetime)
+
+ def get_author(self):
+ """returns realname and address pair of this messages author"""
+ return email.Utils.parseaddr(self._from)
+
+ def add_tags(self, tags):
+ """adds tags to message
+
+ :param tags: tags to add
+ :type tags: list of str
+ """
+ self._dbman.tag('id:' + self._id, tags)
+ self._tags = self._tags.union(tags)
+
+ def remove_tags(self, tags):
+ """remove tags from message
+
+ :param tags: tags to remove
+ :type tags: list of str
+ """
+ self._dbman.untag('id:' + self._id, tags)
+ self._tags = self._tags.difference(tags)
+
+ def get_attachments(self):
+ if not self._attachments:
+ self._attachments = []
+ for part in self.get_message_parts():
+ if part.get_content_maintype() != 'text':
+ self._attachments.append(Attachment(part))
+ return self._attachments
+
+ def accumulate_body(self):
+ bodytxt = ''
+ for part in self.get_email().walk():
+ ctype = part.get_content_type()
+ enc = part.get_content_charset()
+ raw_payload = part.get_payload(decode=True)
+ if part.get_content_maintype() == 'text':
+ if enc:
+ raw_payload = unicode(raw_payload, enc)
+ else:
+ raw_payload = unicode(raw_payload, errors='replace')
+ if ctype == 'text/plain':
+ bodytxt += raw_payload
+ else:
+ #get mime handler
+ handler = get_mime_handler(ctype, key='view',
+ interactive=False)
+ if handler:
+ #open tempfile. Not all handlers accept stuff from stdin
+ tmpfile = tempfile.NamedTemporaryFile(delete=False,
+ suffix='.html')
+ #write payload to tmpfile
+ if part.get_content_maintype() == 'text':
+ tmpfile.write(raw_payload.encode('utf8'))
+ else:
+ tmpfile.write(raw_payload)
+ #create and call external command
+ cmd = handler % tmpfile.name
+ rendered_payload = helper.cmd_output(cmd)
+ #remove tempfile
+ tmpfile.close()
+ os.unlink(tmpfile.name)
+ if rendered_payload: # handler had output
+ bodytxt += unicode(rendered_payload.strip(),
+ encoding='utf8', errors='replace')
+ elif part.get_content_maintype() == 'text':
+ bodytxt += raw_payload
+ # else drop
+ return bodytxt
+
+
+class Attachment:
+ """represents a single mail attachment"""
+
+ def __init__(self, emailpart):
+ """
+ :param emailpart: a non-multipart email that is the attachment
+ :type emailpart: email.message.Message
+ """
+ self.part = emailpart
+
+ def __str__(self):
+ return '%s:%s (%s)' % (self.get_content_type(),
+ self.get_filename(),
+ self.get_size())
+
+ def get_filename(self):
+ """return the filename, extracted from content-disposition header"""
+ return self.part.get_filename()
+
+ def get_content_type(self):
+ """mime type of the attachment"""
+ return self.part.get_content_type()
+
+ def get_size(self):
+ """returns attachments size as human-readable string"""
+ size_in_kbyte = len(self.part.get_payload()) / 1024
+ if size_in_kbyte > 1024:
+ return "%.1fM" % (size_in_kbyte / 1024.0)
+ else:
+ return "%dK" % size_in_kbyte
+
+ def save(self, path):
+ """save the attachment to disk. Uses self.get_filename
+ in case path is a directory"""
+ if os.path.isdir(path):
+ path = os.path.join(path, self.get_filename())
+ FILE = open(path, "w")
+ FILE.write(self.part.get_payload(decode=True))
+ FILE.close()
diff --git a/alot/widgets.py b/alot/widgets.py
index da35d6f3..e82585d8 100644
--- a/alot/widgets.py
+++ b/alot/widgets.py
@@ -23,7 +23,6 @@ from urwid.command_map import command_map
from settings import config
from helper import shorten
from helper import pretty_datetime
-from helper import cmd_output
class ThreadlineWidget(urwid.AttrMap):