def notmuch_status(self): with Database() as db: inbox = Query(db, 'not tag:spam and tag:inbox').search_messages() unread = Query(db, 'not tag:spam and tag:unread').search_messages() inbox_count = len(list(inbox)) unread_count = len(list(unread)) return { 'full_text': 'M: {}({})'.format(inbox_count, unread_count), 'cached_until': self.py3.time_in(5) }
def part(): db = Database() query_string = '' part_num = 0 first_search_term = 0 for (num, arg) in enumerate(sys.argv[1:]): if arg.startswith('--part='): part_num_str = arg.split("=")[1] try: part_num = int(part_num_str) except ValueError: # just emulating behavior exit(1) elif not arg.startswith('--'): # save the position of the first sys.argv # that is a search term first_search_term = num + 1 if first_search_term: # mangle arguments wrapping terms with spaces in quotes querystr = quote_query_line(sys.argv[first_search_term:]) qry = Query(db, querystr) msgs = [msg for msg in qry.search_messages()] if not msgs: sys.exit(1) elif len(msgs) > 1: raise Exception("search term did not match precisely one message") else: msg = msgs[0] print msg.get_part(part_num)
def search(): db = Database() query_string = '' sort_order = "newest-first" first_search_term = 0 for (num, arg) in enumerate(sys.argv[1:]): if arg.startswith('--sort='): sort_order = arg.split("=")[1] if not sort_order in ("oldest-first", "newest-first"): raise Exception("unknown sort order") elif not arg.startswith('--'): # save the position of the first sys.argv that is a search term first_search_term = num + 1 if first_search_term: # mangle arguments wrapping terms with spaces in quotes querystr = quote_query_line(sys.argv[first_search_term:]) qry = Query(db, querystr) if sort_order == "oldest-first": qry.set_sort(Query.SORT.OLDEST_FIRST) else: qry.set_sort(Query.SORT.NEWEST_FIRST) threads = qry.search_threads() for thread in threads: print thread
def thread(thread_id): query = Query(db, 'thread:%s' % thread_id) if not query.count_threads() == 1: return 'Thread not found', 404 messages = [('/messages/' + m.get_message_id(), m.get_header('subject')) \ for m in query.search_messages()] return {'heading': _get_thread(thread_id).get_subject(), 'list': messages}
def get_training_data(progress=False): training_data = [] training_labels = [] db = Database() # query that returns all the messages q = Query(db, '') if progress: count = q.count_messages() n = 0 pbar = ProgressBar(widgets=[Percentage(), Bar(), ETA()], maxval=count).start() data = [] for m in q.search_messages(): if progress: n += 1 pbar.update(n) data.append(m.get_header('To')) data.append(m.get_header('From')) data.append(m.get_header('Subject')) data.append(m.get_part(1).decode("utf8", errors="ignore")) try: training_data.append('\n'.join(data)) except UnicodeDecodeError: print map(lambda x: type(x), data) sys.exit(1) training_labels.append(erase_irrelevant_tags(list(m.get_tags()))) data = [] if progress: pbar.finish() return training_data, training_labels
def message(message_id): query = Query(db, 'id:%s' % message_id) if not query.count_messages() == 1: return 'Message not found', 404 m = next(query.search_messages()) return { 'heading': m.get_header('subject'), 'body': m.get_part(1), }
def datesearch(): if request.method == 'POST': tTo = datetime.datetime.strptime(request.form.get('to'), "%d %B, %Y").timetuple() tFrom = datetime.datetime.strptime(request.form.get('from'), "%d %B, %Y").timetuple() #to=int(time.mktime(tTo)),from=int(time.mktime(tFrom)) msgs = list(Query(db,'%i..%i'%(int(time.mktime(tFrom)),int(time.mktime(tTo))) ).search_messages()) return render_template('dateSearch.html',msgs=msgs) #msgs = list(Query(db, request.form.get('search')).search_messages()) return render_template('dateSearch.html')
def recent(): query = Query(db, '') messages = [] for i, m in enumerate(query.search_messages()): if i >= 30: break messages.append( ('/messages/' + m.get_message_id(), m.get_header('subject'))) return { 'heading': 'Recent messages', 'list': messages, }
def get_new_mails(): db = Database() query = Query(db, 'tag:new') data = [] ids = [] m_data = [] for m in query.search_messages(): m_data.append(m.get_header('To')) m_data.append(m.get_header('From')) m_data.append(m.get_header('Subject')) m_data.append(m.get_part(1).decode("utf8", errors="ignore")) try: data.append('\n'.join(m_data)) ids.append(m.get_message_id()) except UnicodeDecodeError: print map(lambda x: type(x), m_data) sys.exit(1) m_data = [] return data, ids
def show(): entire_thread = False db = Database() out_format = "text" querystr = '' first_search_term = None # ugly homegrown option parsing # TODO: use OptionParser for (num, arg) in enumerate(sys.argv[1:]): if arg == '--entire-thread': entire_thread = True elif arg.startswith("--format="): out_format = arg.split("=")[1] if out_format == 'json': # for compatibility use --entire-thread for json entire_thread = True if not out_format in ("json", "text"): raise Exception("unknown format") elif not arg.startswith('--'): # save the position of the first sys.argv that is a search term first_search_term = num + 1 if first_search_term: # mangle arguments wrapping terms with spaces in quotes querystr = quote_query_line(sys.argv[first_search_term:]) threads = Query(db, querystr).search_threads() first_toplevel = True if out_format == "json": sys.stdout.write("[") for thread in threads: msgs = thread.get_toplevel_messages() if not first_toplevel: if out_format == "json": sys.stdout.write(", ") first_toplevel = False msgs.print_messages(out_format, 0, entire_thread) if out_format == "json": sys.stdout.write("]") sys.stdout.write("\n")
def main(): parser = argparse.ArgumentParser( description="Sync message 'X-Keywords' header with notmuch tags.") parser.add_argument("-V", "--version", action="version", version="%(prog)s " + "v%s (%s)" % (__version__, __date__)) parser.add_argument("-q", "--query", dest="query", required=True, help="notmuch database query string") parser.add_argument("-p", "--db-path", dest="dbpath", help="notmuch database path (default to try user configuration)") parser.add_argument("-n", "--dry-run", dest="dryrun", action="store_true", help="dry run") parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="show verbose information") # Exclusive argument group for sync mode exgroup1 = parser.add_mutually_exclusive_group(required=True) exgroup1.add_argument("-m", "--merge-keywords-tags", dest="direction_merge", action="store_true", help="merge 'X-Keywords' and tags and update both") exgroup1.add_argument("-k", "--keywords-to-tags", dest="direction_keywords2tags", action="store_true", help="sync 'X-Keywords' to notmuch tags") exgroup1.add_argument("-t", "--tags-to-keywords", dest="direction_tags2keywords", action="store_true", help="sync notmuch tags to 'X-Keywords'") # Exclusive argument group for tag operation mode exgroup2 = parser.add_mutually_exclusive_group(required=False) exgroup2.add_argument("-a", "--add-only", dest="mode_addonly", action="store_true", help="only add notmuch tags") exgroup2.add_argument("-r", "--remove-only", dest="mode_removeonly", action="store_true", help="only remove notmuch tags") # Parse args = parser.parse_args() # Sync direction if args.direction_merge: sync_direction = SyncDirection.MERGE_KEYWORDS_TAGS elif args.direction_keywords2tags: sync_direction = SyncDirection.KEYWORDS_TO_TAGS elif args.direction_tags2keywords: sync_direction = SyncDirection.TAGS_TO_KEYWORDS else: raise ValueError("Invalid synchronization direction") # Sync mode if args.mode_addonly: sync_mode = SyncMode.ADD_ONLY elif args.mode_removeonly: sync_mode = SyncMode.REMOVE_ONLY else: sync_mode = SyncMode.ADD_REMOVE # if args.dbpath: dbpath = os.path.abspath(os.path.expanduser(args.dbpath)) else: dbpath = None # db = Database(path=dbpath, create=False, mode=Database.MODE.READ_WRITE) dbinfo = get_notmuch_revision(dbpath=dbpath) q = Query(db, args.query) total_msgs = q.count_messages() msgs = q.search_messages() # if args.verbose: print("# Notmuch database path: %s" % dbpath) print("# Database revision: %d (uuid: %s)" % (dbinfo['revision'], dbinfo['uuid'])) print("# Query: %s" % args.query) print("# Sync direction: %s" % sync_direction.name) print("# Sync mode: %s" % sync_mode.name) print("# Total messages to check: %d" % total_msgs) print("# Dryn run: %s" % args.dryrun) # for msg in msgs: kwmsg = KwMessage(msg) kwmsg.sync(direction=sync_direction, mode=sync_mode, dryrun=args.dryrun, verbose=args.verbose) # db.close()
def get_messages(query_string): for msg in Query(db(), query_string).search_messages(): yield MessageProxy(msg)
def _get_thread(thread_id): querystr = 'thread:' + thread_id thread = next(iter(Query(db, querystr).search_threads())) return thread.get_subject()
def search(querystr): query = Query(db, querystr).search_threads() threads = [('/threads/' + t.get_thread_id(), t.get_subject()) \ for t in query] return {'heading': 'Results for "%s"' % querystr, 'list': threads}
def index(): db = Database() msgs = Query(db, 'inbox').search_messages() return render_template('index.html',msgs=list(msgs)[:100])
def search(): if request.method == 'POST': msgs = list(Query(db, request.form.get('search')).search_messages()) else: msgs = list(Query(db, request.args.get('q')).search_messages()) return render_template('index.html',msgs=list(msgs)[:100])
def main(): # Handle command line options #------------------------------------ # No option given, print USAGE and exit if len(sys.argv) == 1: Notmuch().cmd_usage() #------------------------------------ elif sys.argv[1] == 'setup': """Interactively setup notmuch for first use.""" exit("Not implemented.") #------------------------------------- elif sys.argv[1] == 'new': """Check for new and removed messages.""" Notmuch().cmd_new() #------------------------------------- elif sys.argv[1] == 'help': """Print the help text""" Notmuch().cmd_help(sys.argv[1:]) #------------------------------------- elif sys.argv[1] == 'part': part() #------------------------------------- elif sys.argv[1] == 'search': search() #------------------------------------- elif sys.argv[1] == 'show': show() #------------------------------------- elif sys.argv[1] == 'reply': db = Database() if len(sys.argv) == 2: # no search term. abort exit("Error: notmuch reply requires at least one search term.") # mangle arguments wrapping terms with spaces in quotes querystr = quote_query_line(sys.argv[2:]) msgs = Query(db, querystr).search_messages() print Notmuch().format_reply(msgs) #------------------------------------- elif sys.argv[1] == 'count': if len(sys.argv) == 2: # no further search term, count all querystr = '' else: # mangle arguments wrapping terms with spaces in quotes querystr = quote_query_line(sys.argv[2:]) print Database().create_query(querystr).count_messages() #------------------------------------- elif sys.argv[1] == 'tag': # build lists of tags to be added and removed add = [] remove = [] while not sys.argv[2] == '--' and \ (sys.argv[2].startswith('+') or sys.argv[2].startswith('-')): if sys.argv[2].startswith('+'): # append to add list without initial + add.append(sys.argv.pop(2)[1:]) else: # append to remove list without initial - remove.append(sys.argv.pop(2)[1:]) # skip eventual '--' if sys.argv[2] == '--': sys.argv.pop(2) # the rest is search terms querystr = quote_query_line(sys.argv[2:]) db = Database(mode=Database.MODE.READ_WRITE) msgs = Query(db, querystr).search_messages() for msg in msgs: # actually add and remove all tags map(msg.add_tag, add) map(msg.remove_tag, remove) #------------------------------------- elif sys.argv[1] == 'search-tags': if len(sys.argv) == 2: # no further search term print "\n".join(Database().get_all_tags()) else: # mangle arguments wrapping terms with spaces in quotes querystr = quote_query_line(sys.argv[2:]) db = Database() msgs = Query(db, querystr).search_messages() print "\n".join([t for t in msgs.collect_tags()]) #------------------------------------- elif sys.argv[1] == 'dump': if len(sys.argv) == 2: f = sys.stdout else: f = open(sys.argv[2], "w") db = Database() query = Query(db, '') query.set_sort(Query.SORT.MESSAGE_ID) msgs = query.search_messages() for msg in msgs: f.write("%s (%s)\n" % (msg.get_message_id(), msg.get_tags())) #------------------------------------- elif sys.argv[1] == 'restore': if len(sys.argv) == 2: print("No filename given. Reading dump from stdin.") f = sys.stdin else: f = open(sys.argv[2], "r") # split the msg id and the tags MSGID_TAGS = re.compile("(\S+)\s\((.*)\)$") db = Database(mode=Database.MODE.READ_WRITE) #read each line of the dump file for line in f: msgs = MSGID_TAGS.match(line) if not msgs: sys.stderr.write("Warning: Ignoring invalid input line: %s" % line) continue # split line in components and fetch message msg_id = msgs.group(1) new_tags = set(msgs.group(2).split()) msg = db.find_message(msg_id) if msg == None: sys.stderr.write( "Warning: Cannot apply tags to missing message: %s\n" % msg_id) continue # do nothing if the old set of tags is the same as the new one old_tags = set(msg.get_tags()) if old_tags == new_tags: continue # set the new tags msg.freeze() # only remove tags if the new ones are not a superset anyway if not (new_tags > old_tags): msg.remove_all_tags() for tag in new_tags: msg.add_tag(tag) msg.thaw() #------------------------------------- else: # unknown command exit("Error: Unknown command '%s' (see \"notmuch help\")" % sys.argv[1])
def count_messages(query_string): return Query(db(), query_string).count_messages()
def viewmessage(messageid): msgs = list(Query(db, 'id:%s'%messageid).search_messages()) msgParts = list(msgs[0].get_message_parts()) mail = msgParts[0].as_string() return render_template('index.html',msgs=list(msgs)[:100],view=True,mail=mail)