def find_message_by_filename(self, filename): """Find a message with the given filename .. warning:: This call needs a writeable database in :attr:`Database.MODE`.READ_WRITE mode. The underlying library will exit the program if this method is used on a read-only database! :returns: If the database contains a message with the given filename, then a class:`Message:` is returned. This function returns None if no message is found with the given filename. :exception: :exc:`OutOfMemoryError` If an Out-of-memory occured while constructing the message. :exc:`XapianError` In case of a Xapian Exception. These exceptions include "Database modified" situations, e.g. when the notmuch database has been modified by another program in the meantime. In this case, you should close and reopen the database and retry. :exc:`NotInitializedError` if the database was not intitialized. *Added in notmuch 0.9*""" self._assert_db_is_initialized() msg_p = NotmuchMessageP() status = Database._find_message_by_filename(self._db, _str(filename), byref(msg_p)) if status != STATUS.SUCCESS: raise NotmuchError(status) return msg_p and Message(msg_p, self) or None
def find_message(self, msgid): """Returns a :class:`Message` as identified by its message ID Wraps the underlying *notmuch_database_find_message* function. :param msgid: The message ID :type msgid: unicode or str :returns: :class:`Message` or `None` if no message is found. :exception: :exc:`OutOfMemoryError` If an Out-of-memory occured while constructing the message. :exc:`XapianError` In case of a Xapian Exception. These exceptions include "Database modified" situations, e.g. when the notmuch database has been modified by another program in the meantime. In this case, you should close and reopen the database and retry. :exc:`NotInitializedError` if the database was not intitialized. """ self._assert_db_is_initialized() msg_p = NotmuchMessageP() status = Database._find_message(self._db, _str(msgid), byref(msg_p)) if status != STATUS.SUCCESS: raise NotmuchError(status) return msg_p and Message(msg_p, self) or None
def add_message(self, filename, sync_maildir_flags=False): """Adds a new message to the database :param filename: should be a path relative to the path of the open database (see :meth:`get_path`), or else should be an absolute filename with initial components that match the path of the database. The file should be a single mail message (not a multi-message mbox) that is expected to remain at its current location, since the notmuch database will reference the filename, and will not copy the entire contents of the file. :param sync_maildir_flags: If the message contains Maildir flags, we will -depending on the notmuch configuration- sync those tags to initial notmuch tags, if set to `True`. It is `False` by default to remain consistent with the libnotmuch API. You might want to look into the underlying method :meth:`Message.maildir_flags_to_tags`. :returns: On success, we return 1) a :class:`Message` object that can be used for things such as adding tags to the just-added message. 2) one of the following :attr:`STATUS` values: :attr:`STATUS`.SUCCESS Message successfully added to database. :attr:`STATUS`.DUPLICATE_MESSAGE_ID Message has the same message ID as another message already in the database. The new filename was successfully added to the list of the filenames for the existing message. :rtype: 2-tuple(:class:`Message`, :attr:`STATUS`) :exception: Raises a :exc:`NotmuchError` with the following meaning. If such an exception occurs, nothing was added to the database. :attr:`STATUS`.FILE_ERROR An error occurred trying to open the file, (such as permission denied, or file not found, etc.). :attr:`STATUS`.FILE_NOT_EMAIL The contents of filename don't look like an email message. :attr:`STATUS`.READ_ONLY_DATABASE Database was opened in read-only mode so no message can be added. :attr:`STATUS`.NOT_INITIALIZED The database has not been initialized. """ self._assert_db_is_initialized() msg_p = c_void_p() status = nmlib.notmuch_database_add_message(self._db, _str(filename), byref(msg_p)) if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]: raise NotmuchError(status) #construct Message() and return msg = Message(msg_p, self) #automatic sync initial tags from Maildir flags if sync_maildir_flags: msg.maildir_flags_to_tags() return (msg, status)
def add_message(self, filename, sync_maildir_flags=False): """Adds a new message to the database :param filename: should be a path relative to the path of the open database (see :meth:`get_path`), or else should be an absolute filename with initial components that match the path of the database. The file should be a single mail message (not a multi-message mbox) that is expected to remain at its current location, since the notmuch database will reference the filename, and will not copy the entire contents of the file. :param sync_maildir_flags: If the message contains Maildir flags, we will -depending on the notmuch configuration- sync those tags to initial notmuch tags, if set to `True`. It is `False` by default to remain consistent with the libnotmuch API. You might want to look into the underlying method :meth:`Message.maildir_flags_to_tags`. :returns: On success, we return 1) a :class:`Message` object that can be used for things such as adding tags to the just-added message. 2) one of the following :attr:`STATUS` values: :attr:`STATUS`.SUCCESS Message successfully added to database. :attr:`STATUS`.DUPLICATE_MESSAGE_ID Message has the same message ID as another message already in the database. The new filename was successfully added to the list of the filenames for the existing message. :rtype: 2-tuple(:class:`Message`, :attr:`STATUS`) :exception: Raises a :exc:`NotmuchError` with the following meaning. If such an exception occurs, nothing was added to the database. :attr:`STATUS`.FILE_ERROR An error occurred trying to open the file, (such as permission denied, or file not found, etc.). :attr:`STATUS`.FILE_NOT_EMAIL The contents of filename don't look like an email message. :attr:`STATUS`.READ_ONLY_DATABASE Database was opened in read-only mode so no message can be added. """ self._assert_db_is_initialized() msg_p = NotmuchMessageP() status = self._add_message(self._db, _str(filename), byref(msg_p)) if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]: raise NotmuchError(status) #construct Message() and return msg = Message(msg_p, self) #automatic sync initial tags from Maildir flags if sync_maildir_flags: msg.maildir_flags_to_tags() return (msg, status)
def manage (search) : _t0 = datetime.datetime.now () result = { 'ok' : False } database_path = settings.NOTMUCH_DB exclude_tags = settings.EXCLUDE_TAGS if not "type" in search or search ['type'] == "message" : search_function = notmuch.Query.search_messages valid_global = {'tags' : to_str_arr (Messages.collect_tags)} valid_details = {'id' : to_str (Message.get_message_id), 'thread_id' : to_str (Message.get_thread_id), 'tags' : to_str_arr (Message.get_tags), 'author': lambda msg : Message.get_header (msg, "From"), 'subject' : lambda msg : Message.get_header (msg, "Subject"), 'to': lambda msg : Message.get_header (msg, "To").split (","), 'cc': lambda msg : Message.get_header (msg, "Cc").split (","), 'bcc': lambda msg : Message.get_header (msg, "Bcc").split (","), 'date': lambda msg : time.strftime("%d %b, %X", time.localtime(msg.get_date())), 'parts' : lambda msg : get_parts_details (msg, search ['parts_details'] if 'parts_details' in search else {}), 'head' : get_head_msg, } elif search ['type'] == "thread" : search_function = notmuch.Query.search_threads valid_global = {} valid_details = {'id' : to_str (Thread.get_thread_id), 'authors' : to_str_split (Thread.get_authors, ","), 'subject' : to_str (Thread.get_subject), 'dates' : lambda thread : [time.strftime("%d %b, %X", time.localtime (thread.get_oldest_date())), time.strftime("%d %b, %X", time.localtime (thread.get_newest_date()))], 'tags' : to_str_arr (Thread.get_tags), 'count' : Thread.get_total_messages, 'head' : get_head_thread, 'tree' : get_thread_messages_tree, } if 'options' in search and 'max_delay' in search ['options']: max_delay_present = True max_delay = search ['options']['max_delay'] else : max_delay_present = False if 'global' in search : result ['global'] = {} def global_call () : database = notmuch.Database(database_path) query = notmuch.Query (database, search ['reference'] if "reference" in search else "") for tag in exclude_tags : query.exclude_tag (tag) elements = search_function (query) glob = {} glob ['ok'] = True for key in search ['global'] : if key in valid_global : glob [key] = valid_global [key] (elements) del (elements) database.close() return glob if 'details' in search : result ['details'] = {} def details_call () : database = notmuch.Database(database_path) query = notmuch.Query (database, search ['reference'] if "reference" in search else "") for tag in exclude_tags : query.exclude_tag (tag) elements = search_function (query) details = {} details ['ok'] = True if 'options' in search and 'max_count' in search ['options'] : max_count_present = True max_count = search ['options']['max_count'] else : max_count_present = False _count = 0 for elt in elements : if max_count_present and max_count < _count : details ['ok'] = False details ['message'] = "incomplete" break if max_delay_present and max_delay < (datetime.datetime.now () - _t0).total_seconds() : # Ok, don’t run for ages, even if in a background thread break details [_count] = {} for key in search ['details'] : if key in valid_details : details [_count][key] = valid_details [key] (elt) _count += 1 del (elements) database.close() return details if 'global' in search : if max_delay_present : global_thread, global_queue = timeouted_call (global_call, max_delay - (datetime.datetime.now() - _t0).total_seconds ()).async () else : result ['global'] = global_call () if 'details' in search : if max_delay_present : details_thread, details_queue = timeouted_call (details_call, max_delay - (datetime.datetime.now() - _t0).total_seconds ()).async () else : result ['details'] = details_call () if max_delay_present : if 'global' in search : global_thread.join (max_delay - (datetime.datetime.now() - _t0).total_seconds ()); if global_thread.is_alive () : global_result = { 'ok' : False, 'message' : "Call took to much time"} else : global_result = global_queue.get () result ['global'] = global_result if 'details' in search : details_thread.join (max_delay - (datetime.datetime.now() - _t0).total_seconds ()); if details_thread.is_alive () : details_result = { 'ok' : False, 'message' : "Call took to much time"} else : details_result = details_queue.get () result ['details'] = details_result result ['ok'] = True return result