3 # sfquery.py : python library/program to query Sourceforge bug database
6 # See the comments below the license for usage examples.
8 # Copyright (C) 2003 James F. Amundson
9 # <amundson at users dot sourceforge dot net>
11 # This library is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU Library General Public
13 # License as published by the Free Software Foundation; either
14 # version 2 of the License, or (at your option) any later version.
16 # This library is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 # Library General Public License for more details.
21 # You should have received a copy of the GNU Library General Public
22 # License along with this library; if not, write to the
23 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 # Boston, MA 02111-1307, USA.
29 # Find all the bugs submitted by amundson and print summaries:
31 # ./sfquery.py bugs-10-02-2003.xml\
32 # "artifact.get('submitted_by') == 'amundson'"\
33 # "artifact.print_summary()"
35 # Find all the *open* bugs submitted by amundson and print summaries:
37 # ./sfquery.py bugs-10-02-2003.xml\
38 # "artifact.get('submitted_by')=='amundson' and artifact.get('status')=='Open'"\
39 # "artifact.print_summary()"
41 # Find a specific bug and print the full record:
43 # ./sfquery.py bugs-10-02-2003.xml\
44 # "artifact.get('artifact_id') == '596204'"\
45 # "artifact.print_all()"
46 # Find all bugs containing plot and print who submitted them and their status:
48 # ./sfquery.py bugs-10-02-2003.xml\
49 # "contains(artifact,'plot')"\
50 # "sys.stdout.write('%s\n' % artifact.get('submitted_by'));sys.stdout.write('%s\n\n' % artifact.get('status'))"
54 from xml
.sax
import make_parser
, handler
, saxutils
55 from time
import asctime
, gmtime
59 def contains(thing
,pattern
,ignore_case
=1):
60 if type(thing
) == str or type(thing
) == unicode:
62 result
= re
.search(pattern
,thing
,re
.IGNORECASE
)
64 result
= re
.search(pattern
,thing
)
69 elif hasattr(thing
,"contains"):
70 return thing
.contains(pattern
,ignore_case
)
72 print "Warning: contains can not check",thing
,"of type",type(thing
)
78 def add(self
,name
,content
):
79 if self
.fields
.has_key(name
):
80 self
.fields
[name
] += content
82 self
.fields
[name
] = content
84 if self
.fields
.has_key(name
):
85 uni
= self
.fields
[name
]
86 return uni
.encode('iso-8859-1')
89 def contains(self
,pattern
,ignore_case
=1):
90 for key
in self
.fields
.keys():
91 if contains(self
.fields
[key
],pattern
,ignore_case
):
94 def print_field(self
,name
):
95 if self
.fields
.has_key(name
):
97 if string
.find(name
,"date") > -1:
98 print asctime(gmtime(int(self
.fields
[name
])))
99 elif name
== "details":
101 print "-------------------------------------------------"
102 print self
.fields
[name
].encode('iso-8859-1')
103 print "-------------------------------------------------"
105 print self
.fields
[name
]
106 def print_all(self
,prefix
=None):
107 for key
in self
.fields
.keys():
110 self
.print_field(key
)
112 class History(Fielded
):
116 def contains(self
,pattern
,ignore_case
=1):
117 for message
in self
.messages
:
118 if contains(message
,pattern
,ignore_case
):
120 return Fielded
.contains(self
,pattern
,ignore_case
)
121 def print_all(self
,name
,prefix
=None):
122 Fielded
.print_all(self
,prefix
)
127 sub_prefix
= print_prefix
+ " "
128 print print_prefix
,"----------- messages -----------"
129 for message
in self
.messages
:
130 message
.print_all(sub_prefix
)
131 print print_prefix
,"--------------------------------"
133 class Artifact(Fielded
):
138 def print_summary(self
):
139 print self
.get("artifact_id"),":",
140 print self
.get("summary")
141 def contains(self
,pattern
,ignore_case
=1):
142 for message
in self
.messages
:
143 if contains(message
,pattern
,ignore_case
):
145 for history
in self
.histories
:
146 if contains(history
,pattern
,ignore_case
):
148 return Fielded
.contains(self
,pattern
,ignore_case
)
149 def print_all(self
,prefix
=None):
154 print print_prefix
,"==================== artifact ===================="
155 Fielded
.print_all(self
,prefix
)
156 sub_prefix
= print_prefix
+ " "
157 print print_prefix
,"----------- histories -----------"
158 for history
in self
.histories
:
159 history
.print_all(sub_prefix
)
160 print print_prefix
,"----------- messages -----------"
161 for message
in self
.messages
:
162 message
.print_all(sub_prefix
)
163 print print_prefix
,"--------------------------------"
164 print print_prefix
,"================================================="
166 class Artifacts(Fielded
):
171 class ArtifactGrabber(handler
.ContentHandler
):
172 def __init__(self
,warn
=1):
174 self
.artifacts
= None
175 self
.in_named_field
= 0
178 def startElement(self
, name
, attrs
):
179 if name
== "artifacts":
180 self
.current
.append(Artifacts())
181 elif name
== "artifact":
182 self
.current
.append(Artifact())
183 elif name
== "history":
184 self
.current
.append(History())
185 elif name
== "message":
186 self
.current
.append(Fielded())
187 elif name
== "field":
188 if len(self
.current
) > 0:
189 if attrs
.has_key("name"):
190 self
.in_named_field
= 1
191 self
.field_name
= attrs
["name"]
194 print "Warning: ignoring element", name
196 def endElement(self
, name
):
197 if name
== "artifacts":
198 self
.artifacts
= self
.current
.pop()
199 elif name
== "artifact":
200 this
= self
.current
.pop()
201 parent
= self
.current
[len(self
.current
)-1]
202 parent
.artifacts
.append(this
)
203 elif name
== "message":
204 this
= self
.current
.pop()
205 parent
= self
.current
[len(self
.current
)-1]
206 parent
.messages
.append(this
)
207 elif name
== "history":
208 this
= self
.current
.pop()
209 parent
= self
.current
[len(self
.current
)-1]
210 parent
.histories
.append(this
)
211 elif name
== "field":
212 self
.in_named_field
= 0
214 def characters(self
, content
):
215 if self
.in_named_field
:
216 self
.current
[len(self
.current
)-1].add(self
.field_name
,content
)
218 def endDocument(self
):
219 global global_artifacts
220 global_artifacts
= self
.artifacts
.artifacts
222 def get_artifacts(filename
):
223 parser
= make_parser()
224 parser
.setContentHandler(ArtifactGrabber(warn
=0))
225 parser
.parse(filename
)
226 return global_artifacts
228 def simple_query(artifacts
,query
,action
):
229 print "Total number of artifacts =", len(artifacts
)
231 for artifact
in artifacts
:
234 for cmd
in string
.split(action
,';'):
236 print "Found", matches
,"matches."
238 if __name__
== "__main__":
239 if len(sys
.argv
) != 4:
241 sfquery.py <filename> <query> <action>
242 <filename> is an XML dump of a SourceForge bug database.
243 <query> is a python command evaluating to 1 or 0.
244 <action> is a python command or commands to perform on each matched
245 object. Multiple command should be separated by the ';'
250 artifacts
= get_artifacts(sys
.argv
[1])
251 simple_query(artifacts
,sys
.argv
[2],sys
.argv
[3])