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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
28 # Find all the bugs submitted by amundson and print summaries:
30 # ./sfquery.py bugs-10-02-2003.xml\
31 # "artifact.get('submitted_by') == 'amundson'"\
32 # "artifact.print_summary()"
34 # Find all the *open* bugs submitted by amundson and print summaries:
36 # ./sfquery.py bugs-10-02-2003.xml\
37 # "artifact.get('submitted_by')=='amundson' and artifact.get('status')=='Open'"\
38 # "artifact.print_summary()"
40 # Find a specific bug and print the full record:
42 # ./sfquery.py bugs-10-02-2003.xml\
43 # "artifact.get('artifact_id') == '596204'"\
44 # "artifact.print_all()"
45 # Find all bugs containing plot and print who submitted them and their status:
47 # ./sfquery.py bugs-10-02-2003.xml\
48 # "contains(artifact,'plot')"\
49 # "sys.stdout.write('%s\n' % artifact.get('submitted_by'));sys.stdout.write('%s\n\n' % artifact.get('status'))"
53 from xml
.sax
import make_parser
, handler
, saxutils
54 from time
import asctime
, gmtime
58 def contains(thing
,pattern
,ignore_case
=1):
59 if type(thing
) == str or type(thing
) == unicode:
61 result
= re
.search(pattern
,thing
,re
.IGNORECASE
)
63 result
= re
.search(pattern
,thing
)
68 elif hasattr(thing
,"contains"):
69 return thing
.contains(pattern
,ignore_case
)
71 print "Warning: contains can not check",thing
,"of type",type(thing
)
77 def add(self
,name
,content
):
78 if self
.fields
.has_key(name
):
79 self
.fields
[name
] += content
81 self
.fields
[name
] = content
83 if self
.fields
.has_key(name
):
84 uni
= self
.fields
[name
]
85 return uni
.encode('iso-8859-1')
88 def contains(self
,pattern
,ignore_case
=1):
89 for key
in self
.fields
.keys():
90 if contains(self
.fields
[key
],pattern
,ignore_case
):
93 def print_field(self
,name
):
94 if self
.fields
.has_key(name
):
96 if string
.find(name
,"date") > -1:
97 print asctime(gmtime(int(self
.fields
[name
])))
98 elif name
== "details":
100 print "-------------------------------------------------"
101 print self
.fields
[name
].encode('iso-8859-1')
102 print "-------------------------------------------------"
104 print self
.fields
[name
]
105 def print_all(self
,prefix
=None):
106 for key
in self
.fields
.keys():
109 self
.print_field(key
)
111 class History(Fielded
):
115 def contains(self
,pattern
,ignore_case
=1):
116 for message
in self
.messages
:
117 if contains(message
,pattern
,ignore_case
):
119 return Fielded
.contains(self
,pattern
,ignore_case
)
120 def print_all(self
,name
,prefix
=None):
121 Fielded
.print_all(self
,prefix
)
126 sub_prefix
= print_prefix
+ " "
127 print print_prefix
,"----------- messages -----------"
128 for message
in self
.messages
:
129 message
.print_all(sub_prefix
)
130 print print_prefix
,"--------------------------------"
132 class Artifact(Fielded
):
137 def print_summary(self
):
138 print self
.get("artifact_id"),":",
139 print self
.get("summary")
140 def contains(self
,pattern
,ignore_case
=1):
141 for message
in self
.messages
:
142 if contains(message
,pattern
,ignore_case
):
144 for history
in self
.histories
:
145 if contains(history
,pattern
,ignore_case
):
147 return Fielded
.contains(self
,pattern
,ignore_case
)
148 def print_all(self
,prefix
=None):
153 print print_prefix
,"==================== artifact ===================="
154 Fielded
.print_all(self
,prefix
)
155 sub_prefix
= print_prefix
+ " "
156 print print_prefix
,"----------- histories -----------"
157 for history
in self
.histories
:
158 history
.print_all(sub_prefix
)
159 print print_prefix
,"----------- messages -----------"
160 for message
in self
.messages
:
161 message
.print_all(sub_prefix
)
162 print print_prefix
,"--------------------------------"
163 print print_prefix
,"================================================="
165 class Artifacts(Fielded
):
170 class ArtifactGrabber(handler
.ContentHandler
):
171 def __init__(self
,warn
=1):
173 self
.artifacts
= None
174 self
.in_named_field
= 0
177 def startElement(self
, name
, attrs
):
178 if name
== "artifacts":
179 self
.current
.append(Artifacts())
180 elif name
== "artifact":
181 self
.current
.append(Artifact())
182 elif name
== "history":
183 self
.current
.append(History())
184 elif name
== "message":
185 self
.current
.append(Fielded())
186 elif name
== "field":
187 if len(self
.current
) > 0:
188 if attrs
.has_key("name"):
189 self
.in_named_field
= 1
190 self
.field_name
= attrs
["name"]
193 print "Warning: ignoring element", name
195 def endElement(self
, name
):
196 if name
== "artifacts":
197 self
.artifacts
= self
.current
.pop()
198 elif name
== "artifact":
199 this
= self
.current
.pop()
200 parent
= self
.current
[len(self
.current
)-1]
201 parent
.artifacts
.append(this
)
202 elif name
== "message":
203 this
= self
.current
.pop()
204 parent
= self
.current
[len(self
.current
)-1]
205 parent
.messages
.append(this
)
206 elif name
== "history":
207 this
= self
.current
.pop()
208 parent
= self
.current
[len(self
.current
)-1]
209 parent
.histories
.append(this
)
210 elif name
== "field":
211 self
.in_named_field
= 0
213 def characters(self
, content
):
214 if self
.in_named_field
:
215 self
.current
[len(self
.current
)-1].add(self
.field_name
,content
)
217 def endDocument(self
):
218 global global_artifacts
219 global_artifacts
= self
.artifacts
.artifacts
221 def get_artifacts(filename
):
222 parser
= make_parser()
223 parser
.setContentHandler(ArtifactGrabber(warn
=0))
224 parser
.parse(filename
)
225 return global_artifacts
227 def simple_query(artifacts
,query
,action
):
228 print "Total number of artifacts =", len(artifacts
)
230 for artifact
in artifacts
:
233 for cmd
in string
.split(action
,';'):
235 print "Found", matches
,"matches."
237 if __name__
== "__main__":
238 if len(sys
.argv
) != 4:
240 sfquery.py <filename> <query> <action>
241 <filename> is an XML dump of a SourceForge bug database.
242 <query> is a python command evaluating to 1 or 0.
243 <action> is a python command or commands to perform on each matched
244 object. Multiple command should be separated by the ';'
249 artifacts
= get_artifacts(sys
.argv
[1])
250 simple_query(artifacts
,sys
.argv
[2],sys
.argv
[3])