aboutsummaryrefslogblamecommitdiffstatshomepage
path: root/quarg/main.py
blob: 82474b8f94d7d02687caf0309cfb4d9c3b91d05f (plain) (tree)
1
2
3
4
5
6
7
8
9



                   

                                         


                                       
                               


                                                                               
                            
 

                               














                                                                                                              
                                                                                                                                                     



                                                                                                                                                















                                                           




                                                                    

                             
                                                                             

                                  

                                       





                                       
                                 



                                         


















                                                                               





















                                                                                            
                    

















                                                                                      
import argparse
import configparser
import os
import sys
from timeit import default_timer as timer

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

import quarg.actions as actions
import quarg.database.filters as filters
from quarg.database.tables import Backlog, Buffer, Network, QuasselUser, Sender
from quarg.quassel.formatter import format_from
from quarg.utils import errx

# pylint: disable=line-too-long
cli = argparse.ArgumentParser()
cli.add_argument('keyword', nargs='*', help='match messages containing this keyword')
cli.add_argument('-d', action='store_true', dest='debug', help='print debug and SQL query information')
cli.add_argument('-e', action='store_true', dest='expr', help='interpret keywords as LIKE expression')
matchers = cli.add_argument_group('matching message context')
matchers.add_argument('-b', action='append', dest='buffer', help='match this buffer')
matchers.add_argument('-B', action=actions.ParseBufferType, dest='buftype', help='match buffers of this type')
matchers.add_argument('-n', action='append', dest='nick', help='match this nickname')
matchers.add_argument('-m', action='append', dest='umode', help='match nicknames with this mode')
matchers.add_argument('-N', action='append', dest='network', help='match this network')
matchers.add_argument('-Q', action='append', dest='user', help='match this quassel user')
matchers.add_argument('-t', action=actions.ParseMessageType, dest='msgtype', help='match this message type')
matchers.add_argument('-f', action=actions.ParseMessageFlag, dest='msgflag', help='match this message flag')
date_matchers = cli.add_argument_group('matching message timestamps')
date_matchers.add_argument('--after', action=actions.ParseDate, metavar='DATE', help='sent after this date')
date_matchers.add_argument('--before', action=actions.ParseDate, metavar='DATE', help='sent before this date')
date_matchers.add_argument('--around', action=actions.ParseAround, metavar='DATE[/RANGE]', help='sent RANGE hours/minutes before or after this date')

joined_group = matchers.add_mutually_exclusive_group()
joined_group.add_argument('--joined', default=None, action='store_true', dest='joined', help='match buffers which are currently joined')
joined_group.add_argument('--no-joined', default=None, action='store_false', dest='joined', help='match buffers which are not currently joined')
# pylint: enable=line-too-long

Session = sessionmaker()

def get_config():
    xdg_config_home = os.path.expanduser('~/.config/')
    if 'XDG_CONFIG_HOME' in os.environ:
        xdg_config_home = os.environ['XDG_CONFIG_HOME']

    path = os.path.join(xdg_config_home, 'quarg', 'config')

    config = configparser.ConfigParser()
    config.read(path)

    return config

def check_args(args):
    if args.around is not None:
        if args.before is not None or args.after is not None:
            errx('--around cannot be used with --after or --before')

def collect_predicates(args):
    funs = {
        'keyword':   filters.msg_like if args.expr else filters.msg_contains,
        'buffer':  filters.buffer,
        'nick':    filters.nick,
        'after':   filters.time_after,
        'before':  filters.time_before,
        'around':  filters.time_around,
        'user':    filters.user,
        'network': filters.network,
        'msgflag': filters.msgflag,
        'msgtype': filters.msgtype,
        'buftype': filters.buftype,
        'umode':   filters.umode,
        'joined':  filters.joined,
    }

    for key, value in vars(args).items():
        # ignore unset or empty arguments
        # Note: 'if not value' does not work here because 'joined' can be falsy
        # but still needs to be passed to filters.joined
        if value is None or value == []:
            continue

        # ignore arguments that do not map to predicates
        if key not in funs:
            continue

        if args.debug:
            print(f'{key}: {value}', file=sys.stderr)

        fun = funs[key]

        if isinstance(value, list):
            yield filters.any_filter(fun, value)
        else:
            yield fun(value)

def run_query(session, predicates):
    start = timer()

    query = session.query(Backlog).join(Sender).join(Buffer).join(Network).join(QuasselUser)

    for predicate in predicates:
        query = query.filter(predicate)

    rows = query.order_by(Backlog.time).all()

    end = timer()

    return (rows, end - start)

def main():
    config = get_config()

    if not config.has_option('Database', 'url'):
        errx('No database URL set in config file.')

    args = cli.parse_args()
    check_args(args)

    engine = create_engine(config.get('Database', 'url'), echo=args.debug)
    session = Session(bind=engine)

    predicates = list(collect_predicates(args))

    if not predicates:
        errx('Nothing to match.')

    rows, time = run_query(session, predicates)

    for row in rows:
        print(format_from(row))

    print(f'Query returned {len(rows)} lines in {time:.4f} seconds.', file=sys.stderr)

if __name__ == "__main__":
    main()