Update AUTHORS (using admin/list_authors.pl) for the upcoming release.
[maxima.git] / admin / sfquery.py
blobc407482ee70020b285e93862fd619a1e6be72cfd
1 #!/usr/bin/env python
3 # sfquery.py : python library/program to query Sourceforge bug database
4 # XML files.
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.
27 # Examples:
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'))"
53 import sys
54 from xml.sax import make_parser, handler, saxutils
55 from time import asctime, gmtime
56 import string
57 import re
59 def contains(thing,pattern,ignore_case=1):
60 if type(thing) == str or type(thing) == unicode:
61 if ignore_case:
62 result = re.search(pattern,thing,re.IGNORECASE)
63 else:
64 result = re.search(pattern,thing)
65 if result:
66 return 1
67 else:
68 return 0
69 elif hasattr(thing,"contains"):
70 return thing.contains(pattern,ignore_case)
71 else:
72 print "Warning: contains can not check",thing,"of type",type(thing)
73 return 0
75 class Fielded:
76 def __init__(self):
77 self.fields = {}
78 def add(self,name,content):
79 if self.fields.has_key(name):
80 self.fields[name] += content
81 else:
82 self.fields[name] = content
83 def get(self,name):
84 if self.fields.has_key(name):
85 uni = self.fields[name]
86 return uni.encode('iso-8859-1')
87 else:
88 return None
89 def contains(self,pattern,ignore_case=1):
90 for key in self.fields.keys():
91 if contains(self.fields[key],pattern,ignore_case):
92 return 1
93 return 0
94 def print_field(self,name):
95 if self.fields.has_key(name):
96 print name, ":",
97 if string.find(name,"date") > -1:
98 print asctime(gmtime(int(self.fields[name])))
99 elif name == "details":
100 print
101 print "-------------------------------------------------"
102 print self.fields[name].encode('iso-8859-1')
103 print "-------------------------------------------------"
104 else:
105 print self.fields[name]
106 def print_all(self,prefix=None):
107 for key in self.fields.keys():
108 if prefix:
109 print prefix,
110 self.print_field(key)
112 class History(Fielded):
113 def __init__(self):
114 self.fields = {}
115 self.messages = []
116 def contains(self,pattern,ignore_case=1):
117 for message in self.messages:
118 if contains(message,pattern,ignore_case):
119 return 1
120 return Fielded.contains(self,pattern,ignore_case)
121 def print_all(self,name,prefix=None):
122 Fielded.print_all(self,prefix)
123 if prefix:
124 print_prefix=prefix
125 else:
126 print_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):
134 def __init__(self):
135 self.fields = {}
136 self.messages = []
137 self.histories = []
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):
144 return 1
145 for history in self.histories:
146 if contains(history,pattern,ignore_case):
147 return 1
148 return Fielded.contains(self,pattern,ignore_case)
149 def print_all(self,prefix=None):
150 if prefix:
151 print_prefix=prefix
152 else:
153 print_prefix=""
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):
167 def __init__(self):
168 self.fields={}
169 self.artifacts=[]
171 class ArtifactGrabber(handler.ContentHandler):
172 def __init__(self,warn=1):
173 self.current = []
174 self.artifacts = None
175 self.in_named_field = 0
176 self.warn = warn
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"]
192 else:
193 if self.warn:
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)
230 matches = 0
231 for artifact in artifacts:
232 if eval(query):
233 matches += 1
234 for cmd in string.split(action,';'):
235 eval(cmd)
236 print "Found", matches,"matches."
238 if __name__ == "__main__":
239 if len(sys.argv) != 4:
240 print """Usage:
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 ';'
246 character.
248 sys.exit(1)
249 else:
250 artifacts = get_artifacts(sys.argv[1])
251 simple_query(artifacts,sys.argv[2],sys.argv[3])