Mercurial > ~dholland > hg > swallowtail > index.cgi
annotate shelltools/query-pr/query.py @ 45:233dcc4d80e1
update
author | David A. Holland |
---|---|
date | Mon, 16 Jun 2014 01:27:54 -0400 |
parents | 298c8a7f5181 |
children | 73e6dac29391 |
rev | line source |
---|---|
7
c013fb703183
Empty placeholder scripts so the build will run.
David A. Holland
parents:
diff
changeset
|
1 #!@PYTHON@ |
33 | 2 |
3 import argparse | |
4 import psycopg2 | |
5 | |
6 program_description = """ | |
7 Search for and retrieve problem reports. | |
8 """ | |
9 program_version = "@VERSION@" | |
10 | |
11 ############################################################ | |
12 # settings | |
13 | |
14 outfile = sys.stdout | |
15 outmaterial = "headers" | |
16 outformat = "text" | |
17 | |
18 ############################################################ | |
19 # database | |
20 | |
21 dblink = None | |
22 | |
23 def opendb(): | |
24 global dblink | |
25 | |
26 host = "localhost" | |
27 user = "swallowtail" | |
28 database = "swallowtail" | |
29 dblink = psycopg2.connect("host=%s user=%s dbname=%s" % | |
30 (host, user, database)) | |
31 # end opendb | |
32 | |
33 def closedb(): | |
34 global dblink | |
35 | |
36 dblink.close() | |
37 dblink = None | |
38 # end closedb | |
39 | |
40 def querydb(qtext, args): | |
41 print "Executing this query:" | |
42 print qtext | |
43 print "Args are:" | |
44 print args | |
45 | |
46 cursor = dblink.cursor() | |
47 cursor.execute(qtext, args) | |
48 result = cursor.fetchall() | |
49 cursor.close() | |
50 return result | |
51 # end querydb | |
52 | |
53 ############################################################ | |
54 # | |
55 | |
56 ############################################################ | |
57 # query class for searches | |
58 | |
59 class Query: | |
60 __init__(self): | |
61 self.selections = [] | |
62 self.tables = [] | |
63 self.constraints = [] | |
64 self.args = [] | |
65 prtables = ["PRs"] | |
66 prconstraints = [] | |
67 | |
68 def select(self, s): | |
69 self.selections.append(s) | |
70 | |
71 def addtable(self, t): | |
72 assert(t not in self.tables) | |
73 self.tables.append(t) | |
74 | |
75 def constrain(self, expr): | |
76 self.constraints.append(t) | |
77 | |
78 def internval(self, val): | |
79 num = len(self.args) | |
80 self.args[num] = val | |
81 return "$%d" % num | |
82 | |
83 def textify(self): | |
84 s = "SELECT %s\n" % ",".join(self.selections) | |
85 f = "FROM %s\n" % ",".join(self.tables) | |
86 w = "WHERE %s\n" % " AND ".join(self.constraints) | |
87 return s + f + w | |
88 # end class Query | |
89 | |
90 def regexp_constraint(q, field, value): | |
91 cleanval = q.internval(value) | |
92 if not isregexp(value): | |
93 return "%s = %s" % (field, cleanval) | |
94 else: | |
95 # XXX what's the right operator again? | |
96 return "%s ~= %s" % (field, cleanval) | |
97 # end regexp_constraint | |
98 | |
99 def intrange_constraint(q, field, value): | |
100 (lower, upper) = args.number | |
101 if lower is not None: | |
102 assert(typeof(lower) == int) | |
103 prq.constrain("%s >= %d" % (field, lower)) | |
104 if upper is not None: | |
105 assert(typeof(upper) == int) | |
106 prq.constrain("%s <= %d" % (field, upper)) | |
107 # end intrange_constraint | |
108 | |
109 def daterange_constraint(q, field, value): | |
110 # XXX | |
111 assert(0) | |
112 # end daterange_constraint | |
113 | |
114 ############################################################ | |
115 # arg handling | |
116 | |
117 def getargs(classification_schemes, classification_schemetypes): | |
118 global outmaterial | |
119 global outformat | |
120 | |
121 p = argparse.ArgumentParser(program_description) | |
122 | |
123 # note: -h/--help is built in by default | |
124 p.add_argument("-v", "--version", | |
125 action='version' version=program_version, | |
126 help="Print program version and exit") | |
127 | |
128 p.add_argument("--short", | |
129 action='store_const', const="short", | |
130 dest='outmaterial', | |
131 help="Output in short form (one per line)") | |
132 p.add_argument("--headers", | |
133 action='store_const', const="headers", | |
134 dest='outmaterial', | |
135 help="Output in default form (headers only)") | |
136 p.add_argument("--full", | |
137 action='store_const', const="full", | |
138 dest='outmaterial', | |
139 help="Output in full form (all material)") | |
140 p.add_argument("--attach", nargs=1, | |
141 type=int, | |
142 action='store', | |
143 dest='attach', | |
144 help="Output specified attachment") | |
145 p.add_argument("--message", nargs=1, | |
146 type=int, | |
147 action='store', | |
148 dest='message', | |
149 help="Output specified message") | |
150 | |
151 p.add_argument("--text", | |
152 action='store_const', const="text", | |
153 dest='outformat', | |
154 help="Output as text (default)") | |
155 p.add_argument("--csv", | |
156 action='store_const', const="csv", | |
157 dest='outformat', | |
158 help="Output as comma-separated values") | |
159 p.add_argument("--xml", | |
160 action='store_const', const="xml", | |
161 dest='outformat', | |
162 help="Output as XML") | |
163 p.add_argument("--json", | |
164 action='store_const', const="json", | |
165 dest='outformat', | |
166 help="Output as JSON") | |
167 p.add_argument("--rdf", | |
168 action='store_const', const="rdf", | |
169 dest='outformat', | |
170 help="Output as RDF") | |
171 p.add_argument("--rdflike", | |
172 action='store_const', const="rdflike", | |
173 dest='outformat', | |
174 help="Output as RDF-like text") | |
175 | |
176 p.add_argument("--closed", | |
177 action='store_true', | |
178 dest='closed', default=False, | |
179 help="Search closed PRs as well as open") | |
180 p.add_argument("--public", | |
181 action='store_true', | |
182 dest='public', default=False, | |
183 help="Search only public information") | |
184 | |
185 p.add_argument("--number", nargs=1, | |
186 type=intrange, | |
187 dest='number', | |
188 help="Restrict search to a range of PRs by number") | |
189 p.add_argument("--synopsis", nargs=1, | |
190 dest='synopsis', | |
191 help="Search PRs by their synopsis field") | |
192 p.add_argument("--confidential", nargs=1, | |
193 type=yesno, choices=["yes", "no"], | |
194 dest='confidential', | |
195 help="Search PRs by their confidentiality setting") | |
196 p.add_argument("--state", nargs=1, | |
197 dest='state', | |
198 help="Search PRs by their state") | |
199 p.add_argument("--locked", nargs=1, | |
200 type=yesno, choices=["yes", "no"], | |
201 dest='locked', | |
202 help="Search PRs by their locked setting") | |
203 p.add_argument("--arrival-schemaversion", nargs=1, | |
204 type=intrange, | |
205 dest='arrival_schemaversion', | |
206 help="Search PRs by their original schema version") | |
207 p.add_argument("--arrival-date", nargs=1, | |
208 type=daterange, | |
209 dest='arrival_date', | |
210 help="Search PRs by arrival date") | |
211 p.add_argument("--closed-date", nargs=1, | |
212 type=daterange, | |
213 dest='closed_date', | |
214 help="Search PRs by the date they were closed") | |
215 p.add_argument("--last-modified", nargs=1, | |
216 type=daterange, | |
217 dest='last_modified', | |
218 help="Search PRs by the date they were last modified") | |
219 p.add_argument("--release", nargs=1, | |
220 dest='release', | |
221 help="Search PRs by their release information") | |
222 p.add_argument("--environment", nargs=1, | |
223 dest='environment', | |
224 help="Search PRs by their environment information") | |
225 | |
226 p.add_argument("--originator-name", nargs=1, | |
227 dest='originator_name', | |
228 help="Search PRs by the name of their originator") | |
229 p.add_argument("--originator-email", nargs=1, | |
230 dest='originator_email', | |
231 help="Search PRs by the email of their originator") | |
232 p.add_argument("--originator-id", nargs=1, | |
233 type=intrange, | |
234 dest='originator_id', | |
235 help="Search PRs by the database ID of their originator") | |
236 | |
237 | |
238 p.add_argument("--responsible", nargs=1, | |
239 dest='responsible', | |
240 help="Search PRs by who's responsible for them") | |
241 p.add_argument("--respondent", nargs=1, | |
242 dest='respondent', | |
243 help="Search PRs by who's a respondent") | |
244 p.add_argument("--subscribed", nargs=1, | |
245 dest='subscribed', | |
246 help="Search PRs by who's subscribed") | |
247 | |
248 p.add_argument("--messages", nargs=1, | |
249 dest='messages', | |
250 help="Search PRs by text in the message log") | |
251 p.add_argument("--adminlog", nargs=1, | |
252 dest='adminlog', | |
253 help="Search PRs by text in the administrative log") | |
254 p.add_argument("--anytext", nargs=1, | |
255 dest='anytext', | |
256 help="Search PRs by text in various fields") | |
257 | |
258 for scheme in classification_schemes: | |
259 p.add_argument("--%s" % scheme, nargs=1, | |
260 dest=scheme, | |
261 help="Search PRs by the %s classification scheme" % | |
262 scheme) | |
263 | |
264 p.add_argument("prs", nargs='*', metavar="PR", | |
265 type=int, | |
266 action='append', | |
267 dest='prs', | |
268 help="Specific PRs to retrieve by number") | |
269 | |
270 args = p.parse_args() | |
271 | |
272 if args.outmaterial is not None: | |
273 outmaterial = args.outmaterial | |
274 if args.outformat is not None: | |
275 outformat = args.outformat | |
276 | |
277 # If we're doing something other than a search, do it now | |
278 if args.attach is not None: | |
279 get_attachment(args.attach) | |
280 exit(0) | |
281 if args.message is not None: | |
282 get_message(args.message) | |
283 exit(0) | |
284 | |
285 if args.prs is not None and len(args.prs) > 0: | |
286 show_prs(args.prs) | |
287 exit(0) | |
288 | |
289 # | |
290 # Collect up the search constraints | |
291 # | |
292 | |
293 # 1. Constraints on the PRs table | |
294 checkprtable = False | |
295 prq = Query() | |
296 prq.select("PRs.id as id") | |
297 prq.addtable("PRs") | |
298 if not args.closed: | |
299 checkprtable = True | |
300 prq.addtable("states") | |
301 prq.constrain("PRs.state = states.name") | |
302 prq.constrain("states.closed = FALSE") | |
303 if args.public: | |
304 checkprtable = True | |
305 prq.constrain("NOT PRs.confidential") | |
306 if args.number is not None: | |
307 checkprtable = True | |
308 intrange_constraint(prq, "PRs.id", args.number) | |
309 if args.synopsis is not None: | |
310 checkprtable = True | |
311 regexp_constraint(prq, "PRs.synopsis", args.synopsis) | |
312 if args.confidential is not None: | |
313 checkprtable = True | |
314 assert(typeof(args.confidential) == bool) | |
315 if args.confidential: | |
316 prq.constrain("PRs.confidential") | |
317 else: | |
318 prq.constrain("not PRs.confidential") | |
319 if args.state is not None: | |
320 checkprtable = True | |
321 regexp_constraint(prq, "PRs.state", args.state) | |
322 if args.locked is not None: | |
323 checkprtable = True | |
324 assert(typeof(args.locked) == bool) | |
325 if args.locked: | |
326 prq.constrain("PRs.locked") | |
327 else: | |
328 prq.constrain("not PRs.locked") | |
329 if args.arrival_schemaversion is not None: | |
330 checkprtable = True | |
331 intrange_constraint(prq, "PRs.arrival_schemaversion", | |
332 args.arrival_schemaversion) | |
333 if args.arrival_date is not None: | |
334 checkprtable = True | |
335 daterange_constraint(prq, "PRs.arrival_date", | |
336 args.arrival_date) | |
337 if args.closed_date is not None: | |
338 checkprtable = True | |
339 daterange_constraint(prq, "PRs.closed_date", | |
340 args.closed_date) | |
341 if args.last_modified is not None: | |
342 checkprtable = True | |
343 daterange_constraint(prq, "PRs.last_modified", | |
344 args.last_modified) | |
345 if args.release is not None: | |
346 checkprtable = True | |
347 regexp_constraint(prq, "PRs.release", args.release) | |
348 if args.environment is not None: | |
349 checkprtable = True | |
350 regexp_constraint(prq, "PRs.environment", args.environment) | |
351 | |
352 if args.originator_name is not None or | |
353 args.originator_email is not None: | |
354 prq.addtable("usermail as originator") | |
355 prq.constrain("PRs.originator = originator.id") | |
356 if args.originator_name is not None: | |
357 checkprtable = True | |
358 regexp_constraint(prq, "originator.realname", | |
359 args.originator_name) | |
360 if args.originator_email is not None: | |
361 checkprtable = True | |
362 regexp_constraint(prq, "originator.email", | |
363 args.originator_name) | |
364 if args.originator_id is not None: | |
365 checkprtable = True | |
366 intrange_constraint(prq, "PRs.originator", args.originator_id) | |
367 | |
368 queries = [] | |
369 if checkprtable: | |
370 queries.append(prq) | |
371 | |
372 if args.responsible is not None: | |
373 sq = Query() | |
374 sq.select("subscriptions.pr as id") | |
375 sq.addtable("subscriptions") | |
376 sq.addtable("users") | |
377 sq.constrain("subscriptions.userid = users.id") | |
378 regexp_constraint(sq, "users.realname", args.responsible) | |
379 sq.constrain("subscriptions.responsible") | |
380 queries.append(sq) | |
381 if args.respondent is not None: | |
382 sq = Query() | |
383 sq.select("subscriptions.pr as id") | |
384 sq.addtable("subscriptions") | |
385 sq.addtable("users as subscribed") | |
386 sq.constrain("subscriptions.userid = users.id") | |
387 regexp_constraint(sq, "users.realname", args.respondent) | |
388 sq.constrain("subscriptions.reporter") | |
389 queries.append(sq) | |
390 if args.subscribed is not None: | |
391 sq = Query() | |
392 sq.select("subscriptions.pr as id") | |
393 sq.addtable("subscriptions") | |
394 sq.addtable("users as subscribed") | |
395 sq.constrain("subscriptions.userid = users.id") | |
396 regexp_constraint(sq, "users.realname", args.subscribed) | |
397 queries.append(sq) | |
398 | |
399 if args.messages is not None: | |
400 mq = Query() | |
401 mq.select("messages.pr as id") | |
402 mq.addtable("messages") | |
403 regexp_constraint(sq, "messages.text", args.messages) | |
404 queries.append(mq) | |
405 | |
406 if args.adminlog is not None: | |
407 aq = Query() | |
408 aq.select("adminlog.pr as id") | |
409 aq.addtable("adminlog") | |
410 regexp_constraint(sq, "adminlog.change", args.adminlog) | |
411 regexp_constraint(sq, "adminlog.comment", args.adminlog) | |
412 assert(len(aq.constraints) == 2) | |
413 x = "%s OR %s" % (aq.constraints[0], aq.constraints[1]) | |
414 aq.constraints = [x] | |
415 queries.append(aq) | |
416 | |
417 if args.anytext is not None: | |
418 choke("--anytext isn't supported yet") | |
419 | |
420 for scheme in classification_schemes: | |
421 if args[scheme] is not None: | |
422 schemetype = classification_schemetypes[scheme] | |
423 tbl = "%sclass_data" % schemetype | |
424 cq = Query() | |
425 cq.select("scheme.pr as id") | |
426 cq.addtable("%s as scheme" % schemetype) | |
427 cq.constrain("scheme.scheme = '%s'" % scheme) | |
428 regexp_constraint(cq, "scheme.value", args[scheme]) | |
429 queries.append(cq) | |
430 # end loop | |
431 | |
432 querytexts = [q.textify() for q in queries] | |
433 return "INTERSECT\n".join(querytexts) | |
434 # end getargs | |
435 | |
436 ############################################################ | |
437 # main | |
438 | |
439 def main(): | |
440 opendb() | |
441 (classification_schemes, classification_schemetypes) = getclassify() | |
442 query = getargs(classification_schemes, classification_schemetypes) | |
443 ids = querydb(query) | |
444 if len(ids) > 0: | |
445 show_prs(ids) | |
446 else: | |
447 sys.stderr.write("No PRs matched.\n") | |
448 exit(1) | |
449 closedb() | |
450 return 0 | |
451 # end main | |
452 | |
453 # real python hackers doubtless laugh at this | |
454 exit(main()) |