1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
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
# TODO Make --after/--before and --around mutually exclusive
# FIXME why need default=None for --joined/--no-joined?
# pylint: disable=line-too-long
cli = argparse.ArgumentParser()
cli.add_argument('query', nargs='*', help='match messages containing this query')
cli.add_argument('-d', action='store_true', dest='debug', help='print SQL query information')
cli.add_argument('-e', action='store_true', dest='expr', help='interpret query as LIKE expression')
cli.add_argument('-b', action='append', dest='buffer', help='match messages sent to this buffer')
cli.add_argument('-B', action=actions.ParseBufferType, dest='buftype', help='match messages sent to buffers of this type')
cli.add_argument('-n', action='append', dest='nick', help='match messages sent by this nickname')
cli.add_argument('-N', action='append', dest='network', help='match messages sent to this network')
cli.add_argument('-u', action='append', dest='user', help='match messages received by this quassel user')
cli.add_argument('-t', action=actions.ParseMessageType, dest='msgtype', help='match messages of this message type')
cli.add_argument('-f', action=actions.ParseMessageFlag, dest='msgflag', help='match messages with this flag')
cli.add_argument('-p', action='append', dest='prefix', help='match nicks with this prefix')
cli.add_argument('--joined', default=None, action='store_true', dest='joined', help='match messages sent to channels which are currently joined')
cli.add_argument('--no-joined', default=None, action='store_false', dest='joined', help='match messages sent to channels which are not currently joined')
cli.add_argument('--after', action=actions.ParseDate, metavar='DATE', help='match messages sent after this date')
cli.add_argument('--before', action=actions.ParseDate, metavar='DATE', help='match messages sent before this date')
cli.add_argument('--around', action=actions.ParseAround, metavar='DATE', help='match messages sent within 12 hours of this date')
# 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 collect_predicates(args):
funs = {
'query': filters.msg_like if args.expr else filters.msg_contains,
'buffer': filters.buffer,
'nick': filters.nick,
'after': filters.time_from,
'before': filters.time_to,
'around': filters.time_around,
'user': filters.user,
'network': filters.network,
'msgflag': filters.msgflag,
'msgtype': filters.msgtype,
'buftype': filters.buftype,
'prefix': filters.prefix,
'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()
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()
|