Beispiel #1
0
 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)
     }
Beispiel #2
0
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)
Beispiel #3
0
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
Beispiel #4
0
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}
Beispiel #5
0
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
Beispiel #6
0
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),
    }
Beispiel #7
0
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')
Beispiel #8
0
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,
    }
Beispiel #9
0
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
Beispiel #10
0
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")
Beispiel #11
0
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()
Beispiel #12
0
def get_messages(query_string):
    for msg in Query(db(), query_string).search_messages():
        yield MessageProxy(msg)
Beispiel #13
0
def _get_thread(thread_id):
    querystr = 'thread:' + thread_id
    thread = next(iter(Query(db, querystr).search_threads()))
    return thread.get_subject()
Beispiel #14
0
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}
Beispiel #15
0
def index():
    db = Database()
    msgs = Query(db, 'inbox').search_messages()
    return render_template('index.html',msgs=list(msgs)[:100])
Beispiel #16
0
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])
Beispiel #17
0
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])
Beispiel #18
0
def count_messages(query_string):
    return Query(db(), query_string).count_messages()
Beispiel #19
0
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)