Mercurial > ~dholland > hg > swallowtail > index.cgi
comparison shelltools/query-pr/query.py @ 50:bb0ece71355e
Untabify.
author | David A. Holland |
---|---|
date | Sat, 02 Apr 2022 18:10:52 -0400 |
parents | 4b7f0ee35994 |
children | ef6d572c4e1e |
comparison
equal
deleted
inserted
replaced
49:4b7f0ee35994 | 50:bb0ece71355e |
---|---|
28 # The QueryBuilder class knows how to arrange for all known fields to | 28 # The QueryBuilder class knows how to arrange for all known fields to |
29 # be present. | 29 # be present. |
30 # | 30 # |
31 | 31 |
32 class QueryBuilder: | 32 class QueryBuilder: |
33 # these fields are in the PRs table | 33 # these fields are in the PRs table |
34 prtable_fields = [ | 34 prtable_fields = [ |
35 "id", "synopsis", "confidential", "state", "locked", | 35 "id", "synopsis", "confidential", "state", "locked", |
36 "timeout_date", "timeout_state", | 36 "timeout_date", "timeout_state", |
37 "arrival_schemaversion", "arrival_date", "modified_date", | 37 "arrival_schemaversion", "arrival_date", "modified_date", |
38 "closed_date", | 38 "closed_date", |
39 "release", "environment" | 39 "release", "environment" |
40 ] | 40 ] |
41 | 41 |
42 # these fields are aliases for others | 42 # these fields are aliases for others |
43 alias_fields = { | 43 alias_fields = { |
44 "number" : "id", | 44 "number" : "id", |
45 "date" : "arrival_date", | 45 "date" : "arrival_date", |
46 } | 46 } |
47 | 47 |
48 def __init__(self): | 48 def __init__(self): |
49 self.present = {} | 49 self.present = {} |
50 self.joined = {} | 50 self.joined = {} |
51 self.fromitems = [] | 51 self.fromitems = [] |
52 self.whereitems = [] | 52 self.whereitems = [] |
53 self.order = None | 53 self.order = None |
54 | 54 |
55 def setorder(self, order): | 55 def setorder(self, order): |
56 self.order = order | 56 self.order = order |
57 | 57 |
58 # add to present{} and return the value for convenience (internal) | 58 # add to present{} and return the value for convenience (internal) |
59 def makepresent(self, field, name): | 59 def makepresent(self, field, name): |
60 self.present[field] = name | 60 self.present[field] = name |
61 return name | 61 return name |
62 | 62 |
63 # add a join item (once only) (internal) | 63 # add a join item (once only) (internal) |
64 def addjoin(self, table, as_ = None): | 64 def addjoin(self, table, as_ = None): |
65 if as_ is not None: | 65 if as_ is not None: |
66 key = table + "-" + as_ | 66 key = table + "-" + as_ |
67 val = table + " AS " + as_ | 67 val = table + " AS " + as_ |
68 else: | 68 else: |
69 key = table | 69 key = table |
70 val = table | 70 val = table |
71 if key not in self.joined: | 71 if key not in self.joined: |
72 self.joined[key] = True | 72 self.joined[key] = True |
73 self.fromitems.append(val) | 73 self.fromitems.append(val) |
74 | 74 |
75 # returns a sql expression for the field | 75 # returns a sql expression for the field |
76 def getfield(self, field): | 76 def getfield(self, field): |
77 # already-fetched fields | 77 # already-fetched fields |
78 if field in self.present: | 78 if field in self.present: |
79 return self.present[field] | 79 return self.present[field] |
80 | 80 |
81 # aliases for other fields | 81 # aliases for other fields |
82 if field in alias_fields: | 82 if field in alias_fields: |
83 return self.getfield(alias_fields[field]) | 83 return self.getfield(alias_fields[field]) |
84 | 84 |
85 # simple fields directly in the PRs table | 85 # simple fields directly in the PRs table |
86 if field in prtable_fields: | 86 if field in prtable_fields: |
87 self.addjoin("PRs") | 87 self.addjoin("PRs") |
88 return self.makepresent(field, "PRs." + field) | 88 return self.makepresent(field, "PRs." + field) |
89 | 89 |
90 # now it gets more interesting... | 90 # now it gets more interesting... |
91 if field == "closed": | 91 if field == "closed": |
92 self.addjoin("PRs") | 92 self.addjoin("PRs") |
93 self.addjoin("states") | 93 self.addjoin("states") |
94 self.addwhere("PRs.state = states.name") | 94 self.addwhere("PRs.state = states.name") |
95 return self.makepresent(field, "states.closed") | 95 return self.makepresent(field, "states.closed") |
96 | 96 |
97 # XXX let's pick one set of names and use them everywhere | 97 # XXX let's pick one set of names and use them everywhere |
98 # (e.g. change "posttime" in the schema to "message_date" | 98 # (e.g. change "posttime" in the schema to "message_date" |
99 # or something) | 99 # or something) |
100 if field == "comment_date" or field == "posttime": | 100 if field == "comment_date" or field == "posttime": |
101 self.addjoin("PRs") | 101 self.addjoin("PRs") |
102 self.addjoin("messages") | 102 self.addjoin("messages") |
103 self.addwhere("PRs.id = messages.pr") | 103 self.addwhere("PRs.id = messages.pr") |
104 return self.makepresent(field, "messages.posttime") | 104 return self.makepresent(field, "messages.posttime") |
105 | 105 |
106 if field == "comment" or field == "message" or field == "post": | 106 if field == "comment" or field == "message" or field == "post": |
107 self.addjoin("PRs") | 107 self.addjoin("PRs") |
108 self.addjoin("messages") | 108 self.addjoin("messages") |
109 self.addwhere("PRs.id = messages.pr") | 109 self.addwhere("PRs.id = messages.pr") |
110 return self.makepresent(field, "messages.body") | 110 return self.makepresent(field, "messages.body") |
111 | 111 |
112 if field == "attachment": | 112 if field == "attachment": |
113 self.addjoin("PRs") | 113 self.addjoin("PRs") |
114 self.addjoin("messages") | 114 self.addjoin("messages") |
115 self.addjoin("attachments") | 115 self.addjoin("attachments") |
116 self.addwhere("PRs.id = messages.pr") | 116 self.addwhere("PRs.id = messages.pr") |
117 self.addwhere("messages.id = attachments.msgid") | 117 self.addwhere("messages.id = attachments.msgid") |
118 return self.makepresent(field, "attachments.body") | 118 return self.makepresent(field, "attachments.body") |
119 | 119 |
120 if field == "patch": | 120 if field == "patch": |
121 self.addjoin("PRs") | 121 self.addjoin("PRs") |
122 self.addjoin("messages") | 122 self.addjoin("messages") |
123 self.addjoin("attachments", "patches") | 123 self.addjoin("attachments", "patches") |
124 self.addwhere("PRs.id = messages.pr") | 124 self.addwhere("PRs.id = messages.pr") |
125 self.addwhere("messages.id = patches.msgid") | 125 self.addwhere("messages.id = patches.msgid") |
126 self.addwhere("patches.mimetype = " + | 126 self.addwhere("patches.mimetype = " + |
127 "'application/x-patch'") | 127 "'application/x-patch'") |
128 return self.makepresent(field, "patches.body") | 128 return self.makepresent(field, "patches.body") |
129 | 129 |
130 if field == "mimetype": | 130 if field == "mimetype": |
131 subquery = "((SELECT mtmessages1.pr as pr, " + \ | 131 subquery = "((SELECT mtmessages1.pr as pr, " + \ |
132 "mtmessages1.mimetype as mimetype " + \ | 132 "mtmessages1.mimetype as mimetype " + \ |
133 "FROM messages as mtmessages1) " + \ | 133 "FROM messages as mtmessages1) " + \ |
134 "UNION " + \ | 134 "UNION " + \ |
135 "(SELECT mtmessages2.pr as pr, " + \ | 135 "(SELECT mtmessages2.pr as pr, " + \ |
136 "mtattach2.mimetype as mimetype " + \ | 136 "mtattach2.mimetype as mimetype " + \ |
137 "FROM messages as mtmessages2, " + \ | 137 "FROM messages as mtmessages2, " + \ |
138 " attachments as mtattach2 " + \ | 138 " attachments as mtattach2 " + \ |
139 "WHERE mtmessages2.id = mtattach2.msgid))" | 139 "WHERE mtmessages2.id = mtattach2.msgid))" |
140 self.addjoin("PRs") | 140 self.addjoin("PRs") |
141 self.addjoin(subquery, "mimetypes") | 141 self.addjoin(subquery, "mimetypes") |
142 self.addwhere("PRs.id = mimetypes.pr") | 142 self.addwhere("PRs.id = mimetypes.pr") |
143 return self.makepresent(field, "mimetypes.mimetype") | 143 return self.makepresent(field, "mimetypes.mimetype") |
144 | 144 |
145 # XXX: need view userstrings | 145 # XXX: need view userstrings |
146 # select (id, username as name) from users | 146 # select (id, username as name) from users |
147 # union select (id, realname as name) from users | 147 # union select (id, realname as name) from users |
148 # (allow searching emails? ugh) | 148 # (allow searching emails? ugh) |
149 if field == "originator" or field == "submitter": | 149 if field == "originator" or field == "submitter": |
150 self.addjoin("PRs") | 150 self.addjoin("PRs") |
151 self.addjoin("userstrings", "originators") | 151 self.addjoin("userstrings", "originators") |
152 self.addwhere("PRs.originator = originators.id") | 152 self.addwhere("PRs.originator = originators.id") |
153 return self.makepresent(field, "originators.name") | 153 return self.makepresent(field, "originators.name") |
154 | 154 |
155 if field == "reporter" or field == "respondent": | 155 if field == "reporter" or field == "respondent": |
156 self.addjoin("PRs") | 156 self.addjoin("PRs") |
157 self.addjoin("subscriptions") | 157 self.addjoin("subscriptions") |
158 self.addjoin("userstrings", "reporters") | 158 self.addjoin("userstrings", "reporters") |
159 self.addwhere("subscriptions.userid = reporters.id") | 159 self.addwhere("subscriptions.userid = reporters.id") |
160 self.addwhere("subscriptions.reporter") | 160 self.addwhere("subscriptions.reporter") |
161 return self.makepresent(field, "reporters.name") | 161 return self.makepresent(field, "reporters.name") |
162 | 162 |
163 if field == "responsible": | 163 if field == "responsible": |
164 self.addjoin("PRs") | 164 self.addjoin("PRs") |
165 self.addjoin("subscriptions") | 165 self.addjoin("subscriptions") |
166 self.addjoin("userstrings", "responsibles") | 166 self.addjoin("userstrings", "responsibles") |
167 self.addwhere("subscriptions.userid = responsibles.id") | 167 self.addwhere("subscriptions.userid = responsibles.id") |
168 self.addwhere("subscriptions.responsible") | 168 self.addwhere("subscriptions.responsible") |
169 return self.makepresent(field, "responsibles.name") | 169 return self.makepresent(field, "responsibles.name") |
170 | 170 |
171 if field in hierclasses: | 171 if field in hierclasses: |
172 col = field + "_data" | 172 col = field + "_data" |
173 self.addjoin("PRs") | 173 self.addjoin("PRs") |
174 self.addjoin("hierclass_data", col) | 174 self.addjoin("hierclass_data", col) |
175 self.addwhere("PRs.id = %s.pr" % col) | 175 self.addwhere("PRs.id = %s.pr" % col) |
176 self.addwhere("%s.scheme = '%s'" % (col, field)) | 176 self.addwhere("%s.scheme = '%s'" % (col, field)) |
177 return self.makepresent(field, "%s.value" % col) | 177 return self.makepresent(field, "%s.value" % col) |
178 | 178 |
179 if field in flatclasses: | 179 if field in flatclasses: |
180 col = field + "_data" | 180 col = field + "_data" |
181 self.addjoin("PRs") | 181 self.addjoin("PRs") |
182 self.addjoin("flatclass_data", col) | 182 self.addjoin("flatclass_data", col) |
183 self.addwhere("PRs.id = %s.pr" % col) | 183 self.addwhere("PRs.id = %s.pr" % col) |
184 self.addwhere("%s.scheme = '%s'" % (col, field)) | 184 self.addwhere("%s.scheme = '%s'" % (col, field)) |
185 return self.makepresent(field, "%s.value" % col) | 185 return self.makepresent(field, "%s.value" % col) |
186 | 186 |
187 if field in textclasses: | 187 if field in textclasses: |
188 col = field + "_data" | 188 col = field + "_data" |
189 self.addjoin("PRs") | 189 self.addjoin("PRs") |
190 self.addjoin("textclass_data", col) | 190 self.addjoin("textclass_data", col) |
191 self.addwhere("PRs.id = %s.pr" % col) | 191 self.addwhere("PRs.id = %s.pr" % col) |
192 self.addwhere("%s.scheme = '%s'" % (col, field)) | 192 self.addwhere("%s.scheme = '%s'" % (col, field)) |
193 return self.makepresent(field, "%s.value" % col) | 193 return self.makepresent(field, "%s.value" % col) |
194 | 194 |
195 if field in tagclasses: | 195 if field in tagclasses: |
196 col = field + "_data" | 196 col = field + "_data" |
197 self.addjoin("PRs") | 197 self.addjoin("PRs") |
198 self.addjoin("tagclass_data", col) | 198 self.addjoin("tagclass_data", col) |
199 self.addwhere("PRs.id = %s.pr" % col) | 199 self.addwhere("PRs.id = %s.pr" % col) |
200 self.addwhere("%s.scheme = '%s'" % (col, field)) | 200 self.addwhere("%s.scheme = '%s'" % (col, field)) |
201 return self.makepresent(field, "%s.value" % col) | 201 return self.makepresent(field, "%s.value" % col) |
202 | 202 |
203 sys.stderr.write("Unknown field %s" % field) | 203 sys.stderr.write("Unknown field %s" % field) |
204 exit(1) | 204 exit(1) |
205 # end getfield | 205 # end getfield |
206 | 206 |
207 # emit sql | 207 # emit sql |
208 def build(self, sels): | 208 def build(self, sels): |
209 s = ", ".join(sels) | 209 s = ", ".join(sels) |
210 f = ", ".join(self.fromitems) | 210 f = ", ".join(self.fromitems) |
211 w = " and ".join(self.whereitems) | 211 w = " and ".join(self.whereitems) |
212 q = "SELECT %s\nFROM %s\nWHERE %s\n" % (s, f, w) | 212 q = "SELECT %s\nFROM %s\nWHERE %s\n" % (s, f, w) |
213 if self.order is not None: | 213 if self.order is not None: |
214 q = q + "ORDER BY " + self.order + "\n" | 214 q = q + "ORDER BY " + self.order + "\n" |
215 return q | 215 return q |
216 # endif | 216 # endif |
217 | 217 |
218 # end class QueryBuilder | 218 # end class QueryBuilder |
219 | 219 |
220 # XXX we need to add dynamically: | 220 # XXX we need to add dynamically: |
221 # hierclass_names.name to hierclasses[] | 221 # hierclass_names.name to hierclasses[] |
227 # database | 227 # database |
228 | 228 |
229 dblink = None | 229 dblink = None |
230 | 230 |
231 def opendb(): | 231 def opendb(): |
232 global dblink | 232 global dblink |
233 | 233 |
234 host = "localhost" | 234 host = "localhost" |
235 user = "swallowtail" | 235 user = "swallowtail" |
236 database = "swallowtail" | 236 database = "swallowtail" |
237 dblink = psycopg2.connect("host=%s user=%s dbname=%s" % | 237 dblink = psycopg2.connect("host=%s user=%s dbname=%s" % |
238 (host, user, database)) | 238 (host, user, database)) |
239 # end opendb | 239 # end opendb |
240 | 240 |
241 def closedb(): | 241 def closedb(): |
242 global dblink | 242 global dblink |
243 | 243 |
244 dblink.close() | 244 dblink.close() |
245 dblink = None | 245 dblink = None |
246 # end closedb | 246 # end closedb |
247 | 247 |
248 def querydb(qtext, args): | 248 def querydb(qtext, args): |
249 print "Executing this query:" | 249 print "Executing this query:" |
250 print qtext | 250 print qtext |
251 print "Args are:" | 251 print "Args are:" |
252 print args | 252 print args |
253 | 253 |
254 cursor = dblink.cursor() | 254 cursor = dblink.cursor() |
255 cursor.execute(qtext, args) | 255 cursor.execute(qtext, args) |
256 result = cursor.fetchall() | 256 result = cursor.fetchall() |
257 cursor.close() | 257 cursor.close() |
258 return result | 258 return result |
259 # end querydb | 259 # end querydb |
260 | 260 |
261 ############################################################ | 261 ############################################################ |
262 # query class for searches | 262 # query class for searches |
263 # XXX: obsolete, remove | 263 # XXX: obsolete, remove |
264 | 264 |
265 class Query: | 265 class Query: |
266 def __init__(self): | 266 def __init__(self): |
267 self.selections = [] | 267 self.selections = [] |
268 self.tables = [] | 268 self.tables = [] |
269 self.constraints = [] | 269 self.constraints = [] |
270 self.args = [] | 270 self.args = [] |
271 prtables = ["PRs"] | 271 prtables = ["PRs"] |
272 prconstraints = [] | 272 prconstraints = [] |
273 | 273 |
274 def select(self, s): | 274 def select(self, s): |
275 self.selections.append(s) | 275 self.selections.append(s) |
276 | 276 |
277 def addtable(self, t): | 277 def addtable(self, t): |
278 assert(t not in self.tables) | 278 assert(t not in self.tables) |
279 self.tables.append(t) | 279 self.tables.append(t) |
280 | 280 |
281 def constrain(self, expr): | 281 def constrain(self, expr): |
282 self.constraints.append(t) | 282 self.constraints.append(t) |
283 | 283 |
284 def internval(self, val): | 284 def internval(self, val): |
285 num = len(self.args) | 285 num = len(self.args) |
286 self.args[num] = val | 286 self.args[num] = val |
287 return "$%d" % num | 287 return "$%d" % num |
288 | 288 |
289 def textify(self): | 289 def textify(self): |
290 s = "SELECT %s\n" % ",".join(self.selections) | 290 s = "SELECT %s\n" % ",".join(self.selections) |
291 f = "FROM %s\n" % ",".join(self.tables) | 291 f = "FROM %s\n" % ",".join(self.tables) |
292 w = "WHERE %s\n" % " AND ".join(self.constraints) | 292 w = "WHERE %s\n" % " AND ".join(self.constraints) |
293 return s + f + w | 293 return s + f + w |
294 # end class Query | 294 # end class Query |
295 | 295 |
296 def regexp_constraint(q, field, value): | 296 def regexp_constraint(q, field, value): |
297 cleanval = q.internval(value) | 297 cleanval = q.internval(value) |
298 if not isregexp(value): | 298 if not isregexp(value): |
299 return "%s = %s" % (field, cleanval) | 299 return "%s = %s" % (field, cleanval) |
300 else: | 300 else: |
301 # XXX what's the right operator again? | 301 # XXX what's the right operator again? |
302 return "%s ~= %s" % (field, cleanval) | 302 return "%s ~= %s" % (field, cleanval) |
303 # end regexp_constraint | 303 # end regexp_constraint |
304 | 304 |
305 def intrange_constraint(q, field, value): | 305 def intrange_constraint(q, field, value): |
306 (lower, upper) = args.number | 306 (lower, upper) = args.number |
307 if lower is not None: | 307 if lower is not None: |
308 assert(typeof(lower) == int) | 308 assert(typeof(lower) == int) |
309 prq.constrain("%s >= %d" % (field, lower)) | 309 prq.constrain("%s >= %d" % (field, lower)) |
310 if upper is not None: | 310 if upper is not None: |
311 assert(typeof(upper) == int) | 311 assert(typeof(upper) == int) |
312 prq.constrain("%s <= %d" % (field, upper)) | 312 prq.constrain("%s <= %d" % (field, upper)) |
313 # end intrange_constraint | 313 # end intrange_constraint |
314 | 314 |
315 def daterange_constraint(q, field, value): | 315 def daterange_constraint(q, field, value): |
316 # XXX | 316 # XXX |
317 assert(0) | 317 assert(0) |
318 # end daterange_constraint | 318 # end daterange_constraint |
319 | 319 |
320 ############################################################ | 320 ############################################################ |
321 | 321 |
322 # this is old code that needs to be merged or deleted into the new stuff | 322 # this is old code that needs to be merged or deleted into the new stuff |
323 def oldstuff(): | 323 def oldstuff(): |
324 | 324 |
325 # If we're doing something other than a search, do it now | 325 # If we're doing something other than a search, do it now |
326 if args.attach is not None: | 326 if args.attach is not None: |
327 get_attachment(args.attach) | 327 get_attachment(args.attach) |
328 exit(0) | 328 exit(0) |
329 if args.message is not None: | 329 if args.message is not None: |
330 get_message(args.message) | 330 get_message(args.message) |
331 exit(0) | 331 exit(0) |
332 | 332 |
333 if args.prs is not None and len(args.prs) > 0: | 333 if args.prs is not None and len(args.prs) > 0: |
334 show_prs(args.prs) | 334 show_prs(args.prs) |
335 exit(0) | 335 exit(0) |
336 | 336 |
337 # | 337 # |
338 # Collect up the search constraints | 338 # Collect up the search constraints |
339 # | 339 # |
340 | 340 |
341 # 1. Constraints on the PRs table | 341 # 1. Constraints on the PRs table |
342 checkprtable = False | 342 checkprtable = False |
343 prq = Query() | 343 prq = Query() |
344 prq.select("PRs.id as id") | 344 prq.select("PRs.id as id") |
345 prq.addtable("PRs") | 345 prq.addtable("PRs") |
346 if not args.closed: | 346 if not args.closed: |
347 checkprtable = True | 347 checkprtable = True |
348 prq.addtable("states") | 348 prq.addtable("states") |
349 prq.constrain("PRs.state = states.name") | 349 prq.constrain("PRs.state = states.name") |
350 prq.constrain("states.closed = FALSE") | 350 prq.constrain("states.closed = FALSE") |
351 if args.public: | 351 if args.public: |
352 checkprtable = True | 352 checkprtable = True |
353 prq.constrain("NOT PRs.confidential") | 353 prq.constrain("NOT PRs.confidential") |
354 if args.number is not None: | 354 if args.number is not None: |
355 checkprtable = True | 355 checkprtable = True |
356 intrange_constraint(prq, "PRs.id", args.number) | 356 intrange_constraint(prq, "PRs.id", args.number) |
357 if args.synopsis is not None: | 357 if args.synopsis is not None: |
358 checkprtable = True | 358 checkprtable = True |
359 regexp_constraint(prq, "PRs.synopsis", args.synopsis) | 359 regexp_constraint(prq, "PRs.synopsis", args.synopsis) |
360 if args.confidential is not None: | 360 if args.confidential is not None: |
361 checkprtable = True | 361 checkprtable = True |
362 assert(typeof(args.confidential) == bool) | 362 assert(typeof(args.confidential) == bool) |
363 if args.confidential: | 363 if args.confidential: |
364 prq.constrain("PRs.confidential") | 364 prq.constrain("PRs.confidential") |
365 else: | 365 else: |
366 prq.constrain("not PRs.confidential") | 366 prq.constrain("not PRs.confidential") |
367 if args.state is not None: | 367 if args.state is not None: |
368 checkprtable = True | 368 checkprtable = True |
369 regexp_constraint(prq, "PRs.state", args.state) | 369 regexp_constraint(prq, "PRs.state", args.state) |
370 if args.locked is not None: | 370 if args.locked is not None: |
371 checkprtable = True | 371 checkprtable = True |
372 assert(typeof(args.locked) == bool) | 372 assert(typeof(args.locked) == bool) |
373 if args.locked: | 373 if args.locked: |
374 prq.constrain("PRs.locked") | 374 prq.constrain("PRs.locked") |
375 else: | 375 else: |
376 prq.constrain("not PRs.locked") | 376 prq.constrain("not PRs.locked") |
377 if args.arrival_schemaversion is not None: | 377 if args.arrival_schemaversion is not None: |
378 checkprtable = True | 378 checkprtable = True |
379 intrange_constraint(prq, "PRs.arrival_schemaversion", | 379 intrange_constraint(prq, "PRs.arrival_schemaversion", |
380 args.arrival_schemaversion) | 380 args.arrival_schemaversion) |
381 if args.arrival_date is not None: | 381 if args.arrival_date is not None: |
382 checkprtable = True | 382 checkprtable = True |
383 daterange_constraint(prq, "PRs.arrival_date", | 383 daterange_constraint(prq, "PRs.arrival_date", |
384 args.arrival_date) | 384 args.arrival_date) |
385 if args.closed_date is not None: | 385 if args.closed_date is not None: |
386 checkprtable = True | 386 checkprtable = True |
387 daterange_constraint(prq, "PRs.closed_date", | 387 daterange_constraint(prq, "PRs.closed_date", |
388 args.closed_date) | 388 args.closed_date) |
389 if args.last_modified is not None: | 389 if args.last_modified is not None: |
390 checkprtable = True | 390 checkprtable = True |
391 daterange_constraint(prq, "PRs.last_modified", | 391 daterange_constraint(prq, "PRs.last_modified", |
392 args.last_modified) | 392 args.last_modified) |
393 if args.release is not None: | 393 if args.release is not None: |
394 checkprtable = True | 394 checkprtable = True |
395 regexp_constraint(prq, "PRs.release", args.release) | 395 regexp_constraint(prq, "PRs.release", args.release) |
396 if args.environment is not None: | 396 if args.environment is not None: |
397 checkprtable = True | 397 checkprtable = True |
398 regexp_constraint(prq, "PRs.environment", args.environment) | 398 regexp_constraint(prq, "PRs.environment", args.environment) |
399 | 399 |
400 if args.originator_name is not None or \ | 400 if args.originator_name is not None or \ |
401 args.originator_email is not None: | 401 args.originator_email is not None: |
402 prq.addtable("usermail as originator") | 402 prq.addtable("usermail as originator") |
403 prq.constrain("PRs.originator = originator.id") | 403 prq.constrain("PRs.originator = originator.id") |
404 if args.originator_name is not None: | 404 if args.originator_name is not None: |
405 checkprtable = True | 405 checkprtable = True |
406 regexp_constraint(prq, "originator.realname", | 406 regexp_constraint(prq, "originator.realname", |
407 args.originator_name) | 407 args.originator_name) |
408 if args.originator_email is not None: | 408 if args.originator_email is not None: |
409 checkprtable = True | 409 checkprtable = True |
410 regexp_constraint(prq, "originator.email", | 410 regexp_constraint(prq, "originator.email", |
411 args.originator_name) | 411 args.originator_name) |
412 if args.originator_id is not None: | 412 if args.originator_id is not None: |
413 checkprtable = True | 413 checkprtable = True |
414 intrange_constraint(prq, "PRs.originator", args.originator_id) | 414 intrange_constraint(prq, "PRs.originator", args.originator_id) |
415 | 415 |
416 queries = [] | 416 queries = [] |
417 if checkprtable: | 417 if checkprtable: |
418 queries.append(prq) | 418 queries.append(prq) |
419 | 419 |
420 if args.responsible is not None: | 420 if args.responsible is not None: |
421 sq = Query() | 421 sq = Query() |
422 sq.select("subscriptions.pr as id") | 422 sq.select("subscriptions.pr as id") |
423 sq.addtable("subscriptions") | 423 sq.addtable("subscriptions") |
424 sq.addtable("users") | 424 sq.addtable("users") |
425 sq.constrain("subscriptions.userid = users.id") | 425 sq.constrain("subscriptions.userid = users.id") |
426 regexp_constraint(sq, "users.realname", args.responsible) | 426 regexp_constraint(sq, "users.realname", args.responsible) |
427 sq.constrain("subscriptions.responsible") | 427 sq.constrain("subscriptions.responsible") |
428 queries.append(sq) | 428 queries.append(sq) |
429 if args.respondent is not None: | 429 if args.respondent is not None: |
430 sq = Query() | 430 sq = Query() |
431 sq.select("subscriptions.pr as id") | 431 sq.select("subscriptions.pr as id") |
432 sq.addtable("subscriptions") | 432 sq.addtable("subscriptions") |
433 sq.addtable("users as subscribed") | 433 sq.addtable("users as subscribed") |
434 sq.constrain("subscriptions.userid = users.id") | 434 sq.constrain("subscriptions.userid = users.id") |
435 regexp_constraint(sq, "users.realname", args.respondent) | 435 regexp_constraint(sq, "users.realname", args.respondent) |
436 sq.constrain("subscriptions.reporter") | 436 sq.constrain("subscriptions.reporter") |
437 queries.append(sq) | 437 queries.append(sq) |
438 if args.subscribed is not None: | 438 if args.subscribed is not None: |
439 sq = Query() | 439 sq = Query() |
440 sq.select("subscriptions.pr as id") | 440 sq.select("subscriptions.pr as id") |
441 sq.addtable("subscriptions") | 441 sq.addtable("subscriptions") |
442 sq.addtable("users as subscribed") | 442 sq.addtable("users as subscribed") |
443 sq.constrain("subscriptions.userid = users.id") | 443 sq.constrain("subscriptions.userid = users.id") |
444 regexp_constraint(sq, "users.realname", args.subscribed) | 444 regexp_constraint(sq, "users.realname", args.subscribed) |
445 queries.append(sq) | 445 queries.append(sq) |
446 | 446 |
447 if args.messages is not None: | 447 if args.messages is not None: |
448 mq = Query() | 448 mq = Query() |
449 mq.select("messages.pr as id") | 449 mq.select("messages.pr as id") |
450 mq.addtable("messages") | 450 mq.addtable("messages") |
451 regexp_constraint(sq, "messages.text", args.messages) | 451 regexp_constraint(sq, "messages.text", args.messages) |
452 queries.append(mq) | 452 queries.append(mq) |
453 | 453 |
454 if args.adminlog is not None: | 454 if args.adminlog is not None: |
455 aq = Query() | 455 aq = Query() |
456 aq.select("adminlog.pr as id") | 456 aq.select("adminlog.pr as id") |
457 aq.addtable("adminlog") | 457 aq.addtable("adminlog") |
458 regexp_constraint(sq, "adminlog.change", args.adminlog) | 458 regexp_constraint(sq, "adminlog.change", args.adminlog) |
459 regexp_constraint(sq, "adminlog.comment", args.adminlog) | 459 regexp_constraint(sq, "adminlog.comment", args.adminlog) |
460 assert(len(aq.constraints) == 2) | 460 assert(len(aq.constraints) == 2) |
461 x = "%s OR %s" % (aq.constraints[0], aq.constraints[1]) | 461 x = "%s OR %s" % (aq.constraints[0], aq.constraints[1]) |
462 aq.constraints = [x] | 462 aq.constraints = [x] |
463 queries.append(aq) | 463 queries.append(aq) |
464 | 464 |
465 if args.anytext is not None: | 465 if args.anytext is not None: |
466 choke("--anytext isn't supported yet") | 466 choke("--anytext isn't supported yet") |
467 | 467 |
468 for scheme in classification_schemes: | 468 for scheme in classification_schemes: |
469 if args[scheme] is not None: | 469 if args[scheme] is not None: |
470 schemetype = classification_schemetypes[scheme] | 470 schemetype = classification_schemetypes[scheme] |
471 tbl = "%sclass_data" % schemetype | 471 tbl = "%sclass_data" % schemetype |
472 cq = Query() | 472 cq = Query() |
473 cq.select("scheme.pr as id") | 473 cq.select("scheme.pr as id") |
474 cq.addtable("%s as scheme" % schemetype) | 474 cq.addtable("%s as scheme" % schemetype) |
475 cq.constrain("scheme.scheme = '%s'" % scheme) | 475 cq.constrain("scheme.scheme = '%s'" % scheme) |
476 regexp_constraint(cq, "scheme.value", args[scheme]) | 476 regexp_constraint(cq, "scheme.value", args[scheme]) |
477 queries.append(cq) | 477 queries.append(cq) |
478 # end loop | 478 # end loop |
479 | 479 |
480 querytexts = [q.textify() for q in queries] | 480 querytexts = [q.textify() for q in queries] |
481 return "INTERSECT\n".join(querytexts) | 481 return "INTERSECT\n".join(querytexts) |
482 | 482 |
483 ############################################################ | 483 ############################################################ |
484 # printing | 484 # printing |
485 | 485 |
486 class PrintText: | 486 class PrintText: |
487 def __init__(self, output): | 487 def __init__(self, output): |
488 self.lines = (output == "RAW" or output == "LIST") | 488 self.lines = (output == "RAW" or output == "LIST") |
489 def printheader(self, row): | 489 def printheader(self, row): |
490 # nothing | 490 # nothing |
491 pass | 491 pass |
492 def printrow(self, row): | 492 def printrow(self, row): |
493 # XXX | 493 # XXX |
494 print row | 494 print row |
495 def printfooter(self, row): | 495 def printfooter(self, row): |
496 # nothing | 496 # nothing |
497 pass | 497 pass |
498 # end class PrintText | 498 # end class PrintText |
499 | 499 |
500 class PrintCsv: | 500 class PrintCsv: |
501 def __init__(self, output): | 501 def __init__(self, output): |
502 # nothing | 502 # nothing |
503 pass | 503 pass |
504 def printheader(self, row): | 504 def printheader(self, row): |
505 # XXX | 505 # XXX |
506 pass | 506 pass |
507 def printrow(self, row): | 507 def printrow(self, row): |
508 # XXX | 508 # XXX |
509 pass | 509 pass |
510 def printfooter(self, row): | 510 def printfooter(self, row): |
511 # nothing | 511 # nothing |
512 pass | 512 pass |
513 # end class PrintCsv | 513 # end class PrintCsv |
514 | 514 |
515 class PrintXml: | 515 class PrintXml: |
516 def __init__(self, output): | 516 def __init__(self, output): |
517 # nothing | 517 # nothing |
518 pass | 518 pass |
519 def printheader(self, row): | 519 def printheader(self, row): |
520 # XXX | 520 # XXX |
521 pass | 521 pass |
522 def printrow(self, row): | 522 def printrow(self, row): |
523 # XXX | 523 # XXX |
524 pass | 524 pass |
525 def printfooter(self, row): | 525 def printfooter(self, row): |
526 # XXX | 526 # XXX |
527 pass | 527 pass |
528 # end class PrintXml | 528 # end class PrintXml |
529 | 529 |
530 class PrintJson: | 530 class PrintJson: |
531 def __init__(self, output): | 531 def __init__(self, output): |
532 # nothing | 532 # nothing |
533 pass | 533 pass |
534 def printheader(self, row): | 534 def printheader(self, row): |
535 # XXX | 535 # XXX |
536 pass | 536 pass |
537 def printrow(self, row): | 537 def printrow(self, row): |
538 # XXX | 538 # XXX |
539 pass | 539 pass |
540 def printfooter(self, row): | 540 def printfooter(self, row): |
541 # XXX | 541 # XXX |
542 pass | 542 pass |
543 # end class PrintJson | 543 # end class PrintJson |
544 | 544 |
545 class PrintRdf: | 545 class PrintRdf: |
546 def __init__(self, output): | 546 def __init__(self, output): |
547 # nothing | 547 # nothing |
548 pass | 548 pass |
549 def printheader(self, row): | 549 def printheader(self, row): |
550 # XXX | 550 # XXX |
551 pass | 551 pass |
552 def printrow(self, row): | 552 def printrow(self, row): |
553 # XXX | 553 # XXX |
554 pass | 554 pass |
555 def printfooter(self, row): | 555 def printfooter(self, row): |
556 # XXX | 556 # XXX |
557 pass | 557 pass |
558 # end class PrintRdf | 558 # end class PrintRdf |
559 | 559 |
560 class PrintRdflike: | 560 class PrintRdflike: |
561 def __init__(self, output): | 561 def __init__(self, output): |
562 # nothing | 562 # nothing |
563 pass | 563 pass |
564 def printheader(self, row): | 564 def printheader(self, row): |
565 # XXX | 565 # XXX |
566 pass | 566 pass |
567 def printrow(self, row): | 567 def printrow(self, row): |
568 # XXX | 568 # XXX |
569 pass | 569 pass |
570 def printfooter(self, row): | 570 def printfooter(self, row): |
571 # XXX | 571 # XXX |
572 pass | 572 pass |
573 # end class PrintRdflike | 573 # end class PrintRdflike |
574 | 574 |
575 def print_prs(ids): | 575 def print_prs(ids): |
576 if sel.outformat == "TEXT": | 576 if sel.outformat == "TEXT": |
577 mkprinter = PrintText | 577 mkprinter = PrintText |
578 elif sel.outformat == "CSV": | 578 elif sel.outformat == "CSV": |
579 mkprinter = PrintCsv | 579 mkprinter = PrintCsv |
580 elif sel.outformat == "XML": | 580 elif sel.outformat == "XML": |
581 mkprinter = PrintXml | 581 mkprinter = PrintXml |
582 elif sel.outformat == "JSON": | 582 elif sel.outformat == "JSON": |
583 mkprinter = PrintJson | 583 mkprinter = PrintJson |
584 elif sel.outformat == "RDF": | 584 elif sel.outformat == "RDF": |
585 mkprinter = PrintRdf | 585 mkprinter = PrintRdf |
586 elif sel.outformat == "RDFLIKE": | 586 elif sel.outformat == "RDFLIKE": |
587 mkprinter = PrintRdflike | 587 mkprinter = PrintRdflike |
588 else: | 588 else: |
589 assert(False) | 589 assert(False) |
590 | 590 |
591 # reset the printer | 591 # reset the printer |
592 printer = mkprinter(sel.output) | 592 printer = mkprinter(sel.output) |
593 | 593 |
594 if sel.output == "RAW": | 594 if sel.output == "RAW": |
595 printer.printheader(ids[0]) | 595 printer.printheader(ids[0]) |
596 for id in ids: | 596 for id in ids: |
597 printer(id) | 597 printer(id) |
598 printer.printfooter(ids[0]) | 598 printer.printfooter(ids[0]) |
599 return | 599 return |
600 elif sel.output == "LIST": | 600 elif sel.output == "LIST": |
601 # XXX is there a clean way to do this passing the | 601 # XXX is there a clean way to do this passing the |
602 # whole list of ids at once? | 602 # whole list of ids at once? |
603 query = "SELECT id, synopsis\n" + \ | 603 query = "SELECT id, synopsis\n" + \ |
604 "FROM PRs\n" + \ | 604 "FROM PRs\n" + \ |
605 "WHERE id = $1" | 605 "WHERE id = $1" |
606 elif sel.output == "HEADERS": | 606 elif sel.output == "HEADERS": |
607 query = None # XXX | 607 query = None # XXX |
608 elif sel.output == "META": | 608 elif sel.output == "META": |
609 query = None # XXX | 609 query = None # XXX |
610 elif sel.output == "FULL": | 610 elif sel.output == "FULL": |
611 query = None # XXX | 611 query = None # XXX |
612 else: | 612 else: |
613 assert(False) | 613 assert(False) |
614 | 614 |
615 first = True | 615 first = True |
616 for id in ids: | 616 for id in ids: |
617 results = querydb(query, [id]) | 617 results = querydb(query, [id]) |
618 if first: | 618 if first: |
619 printer.printheader(results[0]) | 619 printer.printheader(results[0]) |
620 first = False | 620 first = False |
621 for r in results: | 621 for r in results: |
622 printer.printrow(r) | 622 printer.printrow(r) |
623 printer.printfooter(results[0]) | 623 printer.printfooter(results[0]) |
624 # end print_prs | 624 # end print_prs |
625 | 625 |
626 # XXX if in public mode we need to check if the PR is public | 626 # XXX if in public mode we need to check if the PR is public |
627 def print_message(pr, msgnum): | 627 def print_message(pr, msgnum): |
628 query = "SELECT users.username AS username,\n" + \ | 628 query = "SELECT users.username AS username,\n" + \ |
629 " users.realname AS realname,\n" + \ | 629 " users.realname AS realname,\n" + \ |
630 " messages.id AS id, parent_id,\n" + \ | 630 " messages.id AS id, parent_id,\n" + \ |
631 " posttime, mimetype, body\n" + \ | 631 " posttime, mimetype, body\n" + \ |
632 "FROM messages, users\n" + \ | 632 "FROM messages, users\n" + \ |
633 "WHERE messages.who = users.id\n" + \ | 633 "WHERE messages.who = users.id\n" + \ |
634 " AND messages.pr = $1\n" + \ | 634 " AND messages.pr = $1\n" + \ |
635 " AND messages.number_in_pr = $2\n" | 635 " AND messages.number_in_pr = $2\n" |
636 # Note that while pr is safe, msgnum came from the commandline | 636 # Note that while pr is safe, msgnum came from the commandline |
637 # and may not be. | 637 # and may not be. |
638 results = querydb(query, [pr, msgnum]) | 638 results = querydb(query, [pr, msgnum]) |
639 [result] = results | 639 [result] = results |
640 (username, realname, id, parent_id, posttime, mimetype, body) = result | 640 (username, realname, id, parent_id, posttime, mimetype, body) = result |
641 # XXX honor mimetype | 641 # XXX honor mimetype |
642 # XXX honor output format (e.g. html) | 642 # XXX honor output format (e.g. html) |
643 sys.stdout.write("From swallowtail@%s %s\n" % (organization,posttime)) | 643 sys.stdout.write("From swallowtail@%s %s\n" % (organization,posttime)) |
644 sys.stdout.write("From: %s (%s)\n" % (username, realname)) | 644 sys.stdout.write("From: %s (%s)\n" % (username, realname)) |
645 sys.stdout.write("References: %s\n" % parent_id) | 645 sys.stdout.write("References: %s\n" % parent_id) |
646 sys.stdout.write("Date: %s\n" % posttime) | 646 sys.stdout.write("Date: %s\n" % posttime) |
647 sys.stdout.write("Content-Type: %s\n" % mimetype) | 647 sys.stdout.write("Content-Type: %s\n" % mimetype) |
648 sys.stdout.write("\n") | 648 sys.stdout.write("\n") |
649 sys.stdout.write(body) | 649 sys.stdout.write(body) |
650 # end print_message | 650 # end print_message |
651 | 651 |
652 # XXX if in public mode we need to check if the PR is public | 652 # XXX if in public mode we need to check if the PR is public |
653 def print_attachment(pr, attachnum): | 653 def print_attachment(pr, attachnum): |
654 query = "SELECT a.mimetype as mimetype, a.body as body\n" + \ | 654 query = "SELECT a.mimetype as mimetype, a.body as body\n" + \ |
655 "FROM messages, attachments as a\n" + \ | 655 "FROM messages, attachments as a\n" + \ |
656 "WHERE messages.pr = $1\n" + \ | 656 "WHERE messages.pr = $1\n" + \ |
657 " AND messages.id = a.msgid\n" + \ | 657 " AND messages.id = a.msgid\n" + \ |
658 " AND a.number_in_pr = $2\n" | 658 " AND a.number_in_pr = $2\n" |
659 # Note that while pr is safe, attachnum came from the | 659 # Note that while pr is safe, attachnum came from the |
660 # commandline and may not be. | 660 # commandline and may not be. |
661 results = querydb(query, [pr, msgnum]) | 661 results = querydb(query, [pr, msgnum]) |
662 [result] = results | 662 [result] = results |
663 (mimetype, body) = result | 663 (mimetype, body) = result |
664 # XXX honor mimetype | 664 # XXX honor mimetype |
665 # XXX need an http output mode so we can send the mimetype! | 665 # XXX need an http output mode so we can send the mimetype! |
666 sys.stdout.write(body) | 666 sys.stdout.write(body) |
667 # end print_attachment | 667 # end print_attachment |
668 | 668 |
669 ############################################################ | 669 ############################################################ |
670 # AST for query | 670 # AST for query |
671 | 671 |
672 class Invocation: | 672 class Invocation: |
673 class Query: | 673 class Query: |
674 Q_TERM = 1 # XXX unused so far | 674 Q_TERM = 1 # XXX unused so far |
675 Q_SQL = 2 | 675 Q_SQL = 2 |
676 Q_AND = 3 | 676 Q_AND = 3 |
677 Q_OR = 4 | 677 Q_OR = 4 |
678 Q_TSTRING = 5 | 678 Q_TSTRING = 5 |
679 Q_QSTRING = 6 | 679 Q_QSTRING = 6 |
680 | 680 |
681 def __init__(self, type): | 681 def __init__(self, type): |
682 self.type = type | 682 self.type = type |
683 def doterm(term): | 683 def doterm(term): |
684 self = Query(Q_TERM) | 684 self = Query(Q_TERM) |
685 self.term = term | 685 self.term = term |
686 return self | 686 return self |
687 def dosql(s): | 687 def dosql(s): |
688 self = Query(Q_SQL) | 688 self = Query(Q_SQL) |
689 self.sql = s | 689 self.sql = s |
690 return self | 690 return self |
691 def doand(qs): | 691 def doand(qs): |
692 self = Query(Q_AND) | 692 self = Query(Q_AND) |
693 self.args = qs | 693 self.args = qs |
694 return self | 694 return self |
695 def door(qs): | 695 def door(qs): |
696 self = Query(Q_OR) | 696 self = Query(Q_OR) |
697 self.args = qs | 697 self.args = qs |
698 return self | 698 return self |
699 # query term string | 699 # query term string |
700 def dotstring(q): | 700 def dotstring(q): |
701 self = Query(Q_TSTRING) | 701 self = Query(Q_TSTRING) |
702 self.string = q | 702 self.string = q |
703 return self | 703 return self |
704 # whole query string | 704 # whole query string |
705 def doqstring(q): | 705 def doqstring(q): |
706 self = Query(Q_QSTRING) | 706 self = Query(Q_QSTRING) |
707 self.string = q | 707 self.string = q |
708 return self | 708 return self |
709 # end class Query | 709 # end class Query |
710 | 710 |
711 class Order: | 711 class Order: |
712 def __init__(self, field, rev = False): | 712 def __init__(self, field, rev = False): |
713 self.field = field | 713 self.field = field |
714 self.rev = rev | 714 self.rev = rev |
715 def dooldest(ign): | 715 def dooldest(ign): |
716 return Order("number") | 716 return Order("number") |
717 def donewest(ign): | 717 def donewest(ign): |
718 return Order("number", True) | 718 return Order("number", True) |
719 def dostaleness(ign): | 719 def dostaleness(ign): |
720 return Order("modified_date", True) | 720 return Order("modified_date", True) |
721 def dofield(field): | 721 def dofield(field): |
722 return Order(field) | 722 return Order(field) |
723 def dorevfield(field): | 723 def dorevfield(field): |
724 return Order(field, True) | 724 return Order(field, True) |
725 # end class Order | 725 # end class Order |
726 | 726 |
727 class Search: | 727 class Search: |
728 def __init__(self, qs, openonly, publiconly, os): | 728 def __init__(self, qs, openonly, publiconly, os): |
729 self.queries = qs | 729 self.queries = qs |
730 self.openonly = openonly | 730 self.openonly = openonly |
731 self.publiconly = publiconly | 731 self.publiconly = publiconly |
732 self.orders = os | 732 self.orders = os |
733 # end class Search | 733 # end class Search |
734 | 734 |
735 class Selection: | 735 class Selection: |
736 S_PR = 1 | 736 S_PR = 1 |
737 S_MESSAGE = 2 | 737 S_MESSAGE = 2 |
738 S_ATTACHMENT = 3 | 738 S_ATTACHMENT = 3 |
739 | 739 |
740 def __init__(self, type): | 740 def __init__(self, type): |
741 self.type = type | 741 self.type = type |
742 def dopr(output, outformat): | 742 def dopr(output, outformat): |
743 self = Selection(S_PR) | 743 self = Selection(S_PR) |
744 self.output = output | 744 self.output = output |
745 self.outformat = outformat | 745 self.outformat = outformat |
746 return self | 746 return self |
747 def domessage(arg): | 747 def domessage(arg): |
748 self = Selection(S_MESSAGE) | 748 self = Selection(S_MESSAGE) |
749 self.message = arg | 749 self.message = arg |
750 return self | 750 return self |
751 def doattachment(arg): | 751 def doattachment(arg): |
752 self = Selection(S_ATTACHMENT) | 752 self = Selection(S_ATTACHMENT) |
753 self.attachment = arg | 753 self.attachment = arg |
754 return self | 754 return self |
755 # end class Selection | 755 # end class Selection |
756 | 756 |
757 class Op: | 757 class Op: |
758 # operation codes | 758 # operation codes |
759 OP_FIELDS = 1 | 759 OP_FIELDS = 1 |
760 OP_SHOW = 2 | 760 OP_SHOW = 2 |
761 OP_RANGE = 3 | 761 OP_RANGE = 3 |
762 OP_SEARCH = 4 | 762 OP_SEARCH = 4 |
763 | 763 |
764 def __init__(self, type): | 764 def __init__(self, type): |
765 self.type = type | 765 self.type = type |
766 | 766 |
767 def dofields(): | 767 def dofields(): |
768 return Op(OP_FIELDS) | 768 return Op(OP_FIELDS) |
769 def doshow(field): | 769 def doshow(field): |
770 self = Op(OP_SHOW) | 770 self = Op(OP_SHOW) |
771 self.field = field | 771 self.field = field |
772 return self | 772 return self |
773 def dorange(field): | 773 def dorange(field): |
774 self = Op(OP_RANGE) | 774 self = Op(OP_RANGE) |
775 self.field = field | 775 self.field = field |
776 return self | 776 return self |
777 def dosearch(s, sels): | 777 def dosearch(s, sels): |
778 self = Op(OP_SEARCH) | 778 self = Op(OP_SEARCH) |
779 self.search = s | 779 self.search = s |
780 self.sels = sels | 780 self.sels = sels |
781 return self | 781 return self |
782 # end class Op | 782 # end class Op |
783 | 783 |
784 def __init__(self, ops): | 784 def __init__(self, ops): |
785 self.ops = ops | 785 self.ops = ops |
786 # end class Invocation | 786 # end class Invocation |
787 | 787 |
788 ############################################################ | 788 ############################################################ |
789 # run (eval the SQL and print the results) | 789 # run (eval the SQL and print the results) |
790 | 790 |
791 def run_sel(sel, ids): | 791 def run_sel(sel, ids): |
792 if sel.type == S_PR: | 792 if sel.type == S_PR: |
793 if ids == []: | 793 if ids == []: |
794 sys.stderr.write("No PRs matched.\n") | 794 sys.stderr.write("No PRs matched.\n") |
795 exit(1) | 795 exit(1) |
796 | 796 |
797 print_prs(ids) | 797 print_prs(ids) |
798 elif sel.type == S_MESSAGE: | 798 elif sel.type == S_MESSAGE: |
799 if len(ids) != 1: | 799 if len(ids) != 1: |
800 sys.stderr.write("Cannot retrieve messages " + | 800 sys.stderr.write("Cannot retrieve messages " + |
801 "from multiple PRs.") | 801 "from multiple PRs.") |
802 exit(1) | 802 exit(1) |
803 print_message(ids[0], sel.message) | 803 print_message(ids[0], sel.message) |
804 elif sel.type == S_ATTACHMENT: | 804 elif sel.type == S_ATTACHMENT: |
805 if len(ids) != 1: | 805 if len(ids) != 1: |
806 sys.stderr.write("Cannot retrieve attachments " + | 806 sys.stderr.write("Cannot retrieve attachments " + |
807 "from multiple PRs.") | 807 "from multiple PRs.") |
808 exit(1) | 808 exit(1) |
809 print_message(ids[0], sel.attachment) | 809 print_message(ids[0], sel.attachment) |
810 else: | 810 else: |
811 assert(False) | 811 assert(False) |
812 | 812 |
813 def run_op(op): | 813 def run_op(op): |
814 if op.type == OP_FIELDS: | 814 if op.type == OP_FIELDS: |
815 list_fields() | 815 list_fields() |
816 elif op.type == OP_SHOW: | 816 elif op.type == OP_SHOW: |
817 describe_field(op.field) | 817 describe_field(op.field) |
818 elif op.type == OP_RANGE: | 818 elif op.type == OP_RANGE: |
819 print_field_range(op.field) | 819 print_field_range(op.field) |
820 elif op.type == OP_SEARCH: | 820 elif op.type == OP_SEARCH: |
821 sql = op.search | 821 sql = op.search |
822 args = op.args # XXX not there! | 822 args = op.args # XXX not there! |
823 ids = querydb(op.search, args) | 823 ids = querydb(op.search, args) |
824 for s in op.sels: | 824 for s in op.sels: |
825 run_sel(s, ids) | 825 run_sel(s, ids) |
826 else: | 826 else: |
827 assert(False) | 827 assert(False) |
828 | 828 |
829 def run(ast): | 829 def run(ast): |
830 for op in ast.ops: | 830 for op in ast.ops: |
831 run_op(op) | 831 run_op(op) |
832 | 832 |
833 ############################################################ | 833 ############################################################ |
834 # compile (convert the AST so the searches are pure SQL) | 834 # compile (convert the AST so the searches are pure SQL) |
835 | 835 |
836 # | 836 # |
841 def matches(s, rx): | 841 def matches(s, rx): |
842 # XXX | 842 # XXX |
843 return True | 843 return True |
844 | 844 |
845 def compile_query(q): | 845 def compile_query(q): |
846 if q.type == Q_QSTRING: | 846 if q.type == Q_QSTRING: |
847 # XXX should use a split that honors quotes | 847 # XXX should use a split that honors quotes |
848 terms = q.string.split() | 848 terms = q.string.split() |
849 terms = [dotstring(t) for t in terms] | 849 terms = [dotstring(t) for t in terms] |
850 return compile_query(doand(terms)) | 850 return compile_query(doand(terms)) |
851 if q.type == Q_TSTRING: | 851 if q.type == Q_TSTRING: |
852 qb = QueryBuilder() | 852 qb = QueryBuilder() |
853 s = q.string | 853 s = q.string |
854 if matches(s, "^[0-9]+$"): | 854 if matches(s, "^[0-9]+$"): |
855 f = qb.getfield("number") | 855 f = qb.getfield("number") |
856 # Note: s is user-supplied but clean to insert directly | 856 # Note: s is user-supplied but clean to insert directly |
857 qb.addwhere("%s = %s" % (f, s)) | 857 qb.addwhere("%s = %s" % (f, s)) |
858 elif matches(s, "^[0-9]+-[0-9]+$"): | 858 elif matches(s, "^[0-9]+-[0-9]+$"): |
859 f = qb.getfield("number") | 859 f = qb.getfield("number") |
860 ss = s.split("-") | 860 ss = s.split("-") |
861 # Note: ss[] is user-supplied but clean | 861 # Note: ss[] is user-supplied but clean |
862 qb.addwhere("%s >= %s" % (f, ss[0])) | 862 qb.addwhere("%s >= %s" % (f, ss[0])) |
863 qb.addwhere("%s <= %s" % (f, ss[1])) | 863 qb.addwhere("%s <= %s" % (f, ss[1])) |
864 elif matches(s, "^[0-9]+-$"): | 864 elif matches(s, "^[0-9]+-$"): |
865 f = qb.getfield("number") | 865 f = qb.getfield("number") |
866 ss = s.split("-") | 866 ss = s.split("-") |
867 # Note: ss[] is user-supplied but clean | 867 # Note: ss[] is user-supplied but clean |
868 qb.addwhere("%s >= %s" % (f, ss[0])) | 868 qb.addwhere("%s >= %s" % (f, ss[0])) |
869 elif matches(s, "^-[0-9]+$"): | 869 elif matches(s, "^-[0-9]+$"): |
870 f = qb.getfield("number") | 870 f = qb.getfield("number") |
871 ss = s.split("-") | 871 ss = s.split("-") |
872 # Note: ss[] is user-supplied but clean | 872 # Note: ss[] is user-supplied but clean |
873 qb.addwhere("%s <= %s" % (f, ss[1])) | 873 qb.addwhere("%s <= %s" % (f, ss[1])) |
874 elif matches(s, "^[^:]+:[^:]+$"): | 874 elif matches(s, "^[^:]+:[^:]+$"): |
875 # XXX honor quoted terms | 875 # XXX honor quoted terms |
876 # XXX = or LIKE? | 876 # XXX = or LIKE? |
877 ss = s.split(":") | 877 ss = s.split(":") |
878 # ss[0] is not clean but if it's crap it won't match | 878 # ss[0] is not clean but if it's crap it won't match |
879 f = qb.getfield(ss[0]) | 879 f = qb.getfield(ss[0]) |
880 # ss[1] is not clean, so intern it for safety | 880 # ss[1] is not clean, so intern it for safety |
881 s = qb.intern(ss[1]) | 881 s = qb.intern(ss[1]) |
882 qb.addwhere("%s = %s" % (f, s)) | 882 qb.addwhere("%s = %s" % (f, s)) |
883 elif matches(s, "^-[^:]+:[^:]+$"): | 883 elif matches(s, "^-[^:]+:[^:]+$"): |
884 # XXX honor quoted terms | 884 # XXX honor quoted terms |
885 # XXX <> or NOT LIKE? | 885 # XXX <> or NOT LIKE? |
886 ss = s.split(":") | 886 ss = s.split(":") |
887 # ss[0] is not clean but if it's crap it won't match | 887 # ss[0] is not clean but if it's crap it won't match |
888 f = qb.getfield(ss[0]) | 888 f = qb.getfield(ss[0]) |
889 # ss[1] is not clean, so intern it for safety | 889 # ss[1] is not clean, so intern it for safety |
890 s = qb.intern(ss[1]) | 890 s = qb.intern(ss[1]) |
891 qb.addwhere("%s <> %s" % (f, s)) | 891 qb.addwhere("%s <> %s" % (f, s)) |
892 elif matches(s, "^-"): | 892 elif matches(s, "^-"): |
893 # XXX <> or NOT LIKE? | 893 # XXX <> or NOT LIKE? |
894 f = qb.getfield("alltext") | 894 f = qb.getfield("alltext") |
895 # s is not clean, so intern it for safety | 895 # s is not clean, so intern it for safety |
896 s = qb.intern(s) | 896 s = qb.intern(s) |
897 qb.addwhere("%s <> %s" % (f, s)) | 897 qb.addwhere("%s <> %s" % (f, s)) |
898 else: | 898 else: |
899 # XXX = or LIKE? | 899 # XXX = or LIKE? |
900 f = qb.getfield("alltext") | 900 f = qb.getfield("alltext") |
901 # s is not clean, so intern it for safety | 901 # s is not clean, so intern it for safety |
902 s = qb.intern(s) | 902 s = qb.intern(s) |
903 qb.addwhere("%s = %s" % (f, s)) | 903 qb.addwhere("%s = %s" % (f, s)) |
904 | 904 |
905 # XXX also does not handle: | 905 # XXX also does not handle: |
906 # | 906 # |
907 # field: with no string (supposed to use a default | 907 # field: with no string (supposed to use a default |
908 # search string) | 908 # search string) |
909 # | 909 # |
910 # generated search fields that parse dates: | 910 # generated search fields that parse dates: |
911 # {arrived,closed,modified,etc.}-{before,after}:date | 911 # {arrived,closed,modified,etc.}-{before,after}:date |
912 # | 912 # |
913 # stale:time | 913 # stale:time |
914 | 914 |
915 return qb.build("PRs.id") | 915 return qb.build("PRs.id") |
916 # end Q_TSTRING case | 916 # end Q_TSTRING case |
917 if q.type == Q_OR: | 917 if q.type == Q_OR: |
918 subqueries = ["(" + compile_query(sq) + ")" for sq in q.args] | 918 subqueries = ["(" + compile_query(sq) + ")" for sq in q.args] |
919 return " UNION ".join(subqueries) | 919 return " UNION ".join(subqueries) |
920 if q.type == Q_AND: | 920 if q.type == Q_AND: |
921 subqueries = ["(" + compile_query(sq) + ")" for sq in q.args] | 921 subqueries = ["(" + compile_query(sq) + ")" for sq in q.args] |
922 return " INTERSECT ".join(subqueries) | 922 return " INTERSECT ".join(subqueries) |
923 if q.type == Q_SQL: | 923 if q.type == Q_SQL: |
924 return q.sql | 924 return q.sql |
925 assert(False) | 925 assert(False) |
926 # end compile_query | 926 # end compile_query |
927 | 927 |
928 def compile_order(qb, o): | 928 def compile_order(qb, o): |
929 str = qb.getfield(o.field) | 929 str = qb.getfield(o.field) |
930 if o.rev: | 930 if o.rev: |
931 str = str + " DESCENDING" | 931 str = str + " DESCENDING" |
932 return str | 932 return str |
933 | 933 |
934 def compile_search(s): | 934 def compile_search(s): |
935 qb2 = QueryBuilder() | 935 qb2 = QueryBuilder() |
936 | 936 |
937 # multiple query strings are treated as OR | 937 # multiple query strings are treated as OR |
938 query = door(s.queries) | 938 query = door(s.queries) |
939 query = compile_query(q) | 939 query = compile_query(q) |
940 | 940 |
941 if s.openonly: | 941 if s.openonly: |
942 qb2.addwhere("not %s" % qb.getfield("closed")) | 942 qb2.addwhere("not %s" % qb.getfield("closed")) |
943 if s.publiconly: | 943 if s.publiconly: |
944 qb2.addwhere("not %s" % qb.getfield("confidential")) | 944 qb2.addwhere("not %s" % qb.getfield("confidential")) |
945 | 945 |
946 orders = [compile_order(qb2, o) for o in s.orders] | 946 orders = [compile_order(qb2, o) for o in s.orders] |
947 order = ", ".join(orders) | 947 order = ", ".join(orders) |
948 if order != "": | 948 if order != "": |
949 qb2.setorder(order) | 949 qb2.setorder(order) |
950 | 950 |
951 if qb2.nonempty(): | 951 if qb2.nonempty(): |
952 qb2.addjoin(query, "search") | 952 qb2.addjoin(query, "search") |
953 qb2.addjoin("PRs") | 953 qb2.addjoin("PRs") |
954 qb2.addwhere("search = PRs.id") | 954 qb2.addwhere("search = PRs.id") |
955 query = qb2.build(["search"]) | 955 query = qb2.build(["search"]) |
956 | 956 |
957 return query | 957 return query |
958 # end compile_search | 958 # end compile_search |
959 | 959 |
960 def compile_op(op): | 960 def compile_op(op): |
961 if op.type == OP_SEARCH: | 961 if op.type == OP_SEARCH: |
962 op.search = compile_search(op.search) | 962 op.search = compile_search(op.search) |
963 return op | 963 return op |
964 | 964 |
965 def compile(ast): | 965 def compile(ast): |
966 ast.ops = [compile_op(op) for op in ast.ops] | 966 ast.ops = [compile_op(op) for op in ast.ops] |
967 | 967 |
968 ############################################################ | 968 ############################################################ |
969 # arg handling | 969 # arg handling |
970 | 970 |
971 # | 971 # |
985 # be able to pass action=CtorAppend(ctor), but since it has to be a | 985 # be able to pass action=CtorAppend(ctor), but since it has to be a |
986 # class that doesn't work... unless you make a new class for every | 986 # class that doesn't work... unless you make a new class for every |
987 # ctor you want to use, which seems completely insane. | 987 # ctor you want to use, which seems completely insane. |
988 # | 988 # |
989 class CtorAppend(argparse.Action): | 989 class CtorAppend(argparse.Action): |
990 def __call__(self, parser, namespace, values, option_string=None): | 990 def __call__(self, parser, namespace, values, option_string=None): |
991 items = getattr(namespace, self.dest) | 991 items = getattr(namespace, self.dest) |
992 item = self.const(values) | 992 item = self.const(values) |
993 items.append(item) | 993 items.append(item) |
994 setattr(namespace, self.dest, items) | 994 setattr(namespace, self.dest, items) |
995 | 995 |
996 def getargs(): | 996 def getargs(): |
997 p = argparse.ArgumentParser(program_description) | 997 p = argparse.ArgumentParser(program_description) |
998 | 998 |
999 # note: -h/--help is built in by default | 999 # note: -h/--help is built in by default |
1000 p.add_argument("-v", "--version", | 1000 p.add_argument("-v", "--version", |
1001 action='version', version=program_version, | 1001 action='version', version=program_version, |
1002 help="Print program version and exit") | 1002 help="Print program version and exit") |
1003 | 1003 |
1004 p.add_argument("--show", nargs=1, | 1004 p.add_argument("--show", nargs=1, |
1005 action=CtorAppend, dest='ops', | 1005 action=CtorAppend, dest='ops', |
1006 const=Invocation.Op.doshow, | 1006 const=Invocation.Op.doshow, |
1007 help="Show description of field") | 1007 help="Show description of field") |
1008 p.add_argument("--range", nargs=1, | 1008 p.add_argument("--range", nargs=1, |
1009 action=CtorAppend, dest='ops', | 1009 action=CtorAppend, dest='ops', |
1010 const=Invocation.Op.dorange, | 1010 const=Invocation.Op.dorange, |
1011 help="Show range of extant values for field") | 1011 help="Show range of extant values for field") |
1012 | 1012 |
1013 p.add_argument("--search", nargs=1, | 1013 p.add_argument("--search", nargs=1, |
1014 action=CtorAppend, dest='queries', | 1014 action=CtorAppend, dest='queries', |
1015 const=Invocation.Query.doqstring, | 1015 const=Invocation.Query.doqstring, |
1016 help="Force string to be read as a search string") | 1016 help="Force string to be read as a search string") |
1017 p.add_argument("-s", "--sql", nargs=1, | 1017 p.add_argument("-s", "--sql", nargs=1, |
1018 action=CtorAppend, dest='queries', | 1018 action=CtorAppend, dest='queries', |
1019 const=Invocation.Query.dosql, | 1019 const=Invocation.Query.dosql, |
1020 help="Supply explicit sql query as search") | 1020 help="Supply explicit sql query as search") |
1021 | 1021 |
1022 p.add_argument("--open", | 1022 p.add_argument("--open", |
1023 action='store_const', dest='openonly', const="True", | 1023 action='store_const', dest='openonly', const="True", |
1024 help="Exclude closed PRs (default)") | 1024 help="Exclude closed PRs (default)") |
1025 p.add_argument("--closed", | 1025 p.add_argument("--closed", |
1026 action='store_const', dest='openonly', const="False", | 1026 action='store_const', dest='openonly', const="False", |
1027 help="Include closed PRs in search") | 1027 help="Include closed PRs in search") |
1028 p.add_argument("--public", | 1028 p.add_argument("--public", |
1029 action='store_const', dest='publiconly', const="True", | 1029 action='store_const', dest='publiconly', const="True", |
1030 help="Exclude confidential PRs") | 1030 help="Exclude confidential PRs") |
1031 p.add_argument("--privileged", | 1031 p.add_argument("--privileged", |
1032 action='store_const', dest='publiconly', const="False", | 1032 action='store_const', dest='publiconly', const="False", |
1033 help="Allow confidential PRs (default)") | 1033 help="Allow confidential PRs (default)") |
1034 | 1034 |
1035 p.add_argument("--oldest", | 1035 p.add_argument("--oldest", |
1036 action=CtorAppend, dest='orders', | 1036 action=CtorAppend, dest='orders', |
1037 const=Invocation.Order.dooldest, | 1037 const=Invocation.Order.dooldest, |
1038 help="Sort output with oldest PRs first") | 1038 help="Sort output with oldest PRs first") |
1039 p.add_argument("--newest", | 1039 p.add_argument("--newest", |
1040 action=CtorAppend, dest='orders', | 1040 action=CtorAppend, dest='orders', |
1041 const=Invocation.Order.donewest, | 1041 const=Invocation.Order.donewest, |
1042 help="Sort output with newest PRs first") | 1042 help="Sort output with newest PRs first") |
1043 p.add_argument("--staleness", | 1043 p.add_argument("--staleness", |
1044 action=CtorAppend, dest='orders', | 1044 action=CtorAppend, dest='orders', |
1045 const=Invocation.Order.dostaleness, | 1045 const=Invocation.Order.dostaleness, |
1046 help="Sort output by time since last modification") | 1046 help="Sort output by time since last modification") |
1047 p.add_argument("--orderby", nargs=1, | 1047 p.add_argument("--orderby", nargs=1, |
1048 action=CtorAppend, dest='orders', | 1048 action=CtorAppend, dest='orders', |
1049 const=Invocation.Order.dofield, | 1049 const=Invocation.Order.dofield, |
1050 help="Sort output by specific field") | 1050 help="Sort output by specific field") |
1051 p.add_argument("--revorderby", nargs=1, | 1051 p.add_argument("--revorderby", nargs=1, |
1052 action=CtorAppend, dest='orders', | 1052 action=CtorAppend, dest='orders', |
1053 const=Invocation.Order.dorevfield, | 1053 const=Invocation.Order.dorevfield, |
1054 help="Sort output by specific field, reversed") | 1054 help="Sort output by specific field, reversed") |
1055 | 1055 |
1056 p.add_argument("-m", "--message", nargs=1, | 1056 p.add_argument("-m", "--message", nargs=1, |
1057 action=CtorAppend, dest='selections', | 1057 action=CtorAppend, dest='selections', |
1058 const=Invocation.Selection.domessage, | 1058 const=Invocation.Selection.domessage, |
1059 help="Print selected message (single PR only)") | 1059 help="Print selected message (single PR only)") |
1060 p.add_argument("-a", "--attachment", nargs=1, | 1060 p.add_argument("-a", "--attachment", nargs=1, |
1061 action=CtorAppend, dest='selections', | 1061 action=CtorAppend, dest='selections', |
1062 const=Invocation.Selection.doattachment, | 1062 const=Invocation.Selection.doattachment, |
1063 help="Print selected attachment (single PR only)") | 1063 help="Print selected attachment (single PR only)") |
1064 | 1064 |
1065 p.add_argument("-r", "--raw", | 1065 p.add_argument("-r", "--raw", |
1066 action = 'store_const', const="RAW", | 1066 action = 'store_const', const="RAW", |
1067 dest = 'output', | 1067 dest = 'output', |
1068 help="Print exactly what the database returns") | 1068 help="Print exactly what the database returns") |
1069 p.add_argument("-l", "--list", | 1069 p.add_argument("-l", "--list", |
1070 action = 'store_const', const="LIST", | 1070 action = 'store_const', const="LIST", |
1071 dest = 'output', | 1071 dest = 'output', |
1072 help="Print in list form (default)") | 1072 help="Print in list form (default)") |
1073 p.add_argument("--headers", | 1073 p.add_argument("--headers", |
1074 action = 'store_const', const="HEADERS", | 1074 action = 'store_const', const="HEADERS", |
1075 dest = 'output', | 1075 dest = 'output', |
1076 help="Print header information only") | 1076 help="Print header information only") |
1077 p.add_argument("--meta", | 1077 p.add_argument("--meta", |
1078 action = 'store_const', const="META", | 1078 action = 'store_const', const="META", |
1079 dest = 'output', | 1079 dest = 'output', |
1080 help="Print all metadata") | 1080 help="Print all metadata") |
1081 p.add_argument("--metadata", | 1081 p.add_argument("--metadata", |
1082 action = 'store_const', const="META", | 1082 action = 'store_const', const="META", |
1083 dest = 'output') | 1083 dest = 'output') |
1084 p.add_argument("-f", "--full", | 1084 p.add_argument("-f", "--full", |
1085 action = 'store_const', const="FULL", | 1085 action = 'store_const', const="FULL", |
1086 dest = 'output', | 1086 dest = 'output', |
1087 help="Print everything") | 1087 help="Print everything") |
1088 | 1088 |
1089 p.add_argument("--text", | 1089 p.add_argument("--text", |
1090 action = 'store_const', const="TEXT", | 1090 action = 'store_const', const="TEXT", |
1091 dest = 'outformat', | 1091 dest = 'outformat', |
1092 help="Print in text format (default)") | 1092 help="Print in text format (default)") |
1093 p.add_argument("--csv", | 1093 p.add_argument("--csv", |
1094 action = 'store_const', const="CSV", | 1094 action = 'store_const', const="CSV", |
1095 dest = 'outformat', | 1095 dest = 'outformat', |
1096 help="Print a CSV file") | 1096 help="Print a CSV file") |
1097 p.add_argument("--xml", | 1097 p.add_argument("--xml", |
1098 action = 'store_const', const="XML", | 1098 action = 'store_const', const="XML", |
1099 dest = 'outformat', | 1099 dest = 'outformat', |
1100 help="Print in XML") | 1100 help="Print in XML") |
1101 p.add_argument("--json", | 1101 p.add_argument("--json", |
1102 action = 'store_const', const="JSON", | 1102 action = 'store_const', const="JSON", |
1103 dest = 'outformat', | 1103 dest = 'outformat', |
1104 help="Print in JSON") | 1104 help="Print in JSON") |
1105 p.add_argument("--rdf", | 1105 p.add_argument("--rdf", |
1106 action = 'store_const', const="RDF", | 1106 action = 'store_const', const="RDF", |
1107 dest = 'outbformat', | 1107 dest = 'outbformat', |
1108 help="Print in RDF") | 1108 help="Print in RDF") |
1109 p.add_argument("--rdflike", | 1109 p.add_argument("--rdflike", |
1110 action = 'store_const', const="RDFLIKE", | 1110 action = 'store_const', const="RDFLIKE", |
1111 dest = 'outformat', | 1111 dest = 'outformat', |
1112 help="Print RDF-like text") | 1112 help="Print RDF-like text") |
1113 | 1113 |
1114 p.add_argument("TERM", nargs='*', | 1114 p.add_argument("TERM", nargs='*', |
1115 action=CtorAppend, dest='queries', | 1115 action=CtorAppend, dest='queries', |
1116 const=Invocation.Query.doqstring, | 1116 const=Invocation.Query.doqstring, |
1117 help="Search term") | 1117 help="Search term") |
1118 | 1118 |
1119 args = p.parse_args() | 1119 args = p.parse_args() |
1120 | 1120 |
1121 ops = args.ops | 1121 ops = args.ops |
1122 if ops is None: | 1122 if ops is None: |
1123 ops = [] | 1123 ops = [] |
1124 queries = args.queries | 1124 queries = args.queries |
1125 if queries is not None: | 1125 if queries is not None: |
1126 openonly = args.openonly | 1126 openonly = args.openonly |
1127 if openonly is None: | 1127 if openonly is None: |
1128 openonly = True | 1128 openonly = True |
1129 publiconly = args.publiconly | 1129 publiconly = args.publiconly |
1130 if publiconly is None: | 1130 if publiconly is None: |
1131 publiconly = False | 1131 publiconly = False |
1132 orders = args.orders | 1132 orders = args.orders |
1133 if orders is None: | 1133 if orders is None: |
1134 orders = [Invocation.Order.dooldest(None)] | 1134 orders = [Invocation.Order.dooldest(None)] |
1135 output = args.output | 1135 output = args.output |
1136 if output is None: | 1136 if output is None: |
1137 output = "LIST" | 1137 output = "LIST" |
1138 outformat = args.outformat | 1138 outformat = args.outformat |
1139 if outformat is None: | 1139 if outformat is None: |
1140 outformat = "TEXT" | 1140 outformat = "TEXT" |
1141 selections = args.selections | 1141 selections = args.selections |
1142 if selections is None: | 1142 if selections is None: |
1143 sel = Invocation.Selection.dopr(output, outformat) | 1143 sel = Invocation.Selection.dopr(output, outformat) |
1144 selections = [sel] | 1144 selections = [sel] |
1145 search = Search(queries, openonly, publiconly, orders) | 1145 search = Search(queries, openonly, publiconly, orders) |
1146 op = dosearch(search, selections) | 1146 op = dosearch(search, selections) |
1147 ops.append(op) | 1147 ops.append(op) |
1148 # endif | 1148 # endif |
1149 | 1149 |
1150 return Invocation(ops) | 1150 return Invocation(ops) |
1151 # end getargs | 1151 # end getargs |
1152 | 1152 |
1153 ############################################################ | 1153 ############################################################ |
1154 # main | 1154 # main |
1155 | 1155 |