3 # sfsum.py: A script for parsing XML data exported from
4 # SourceForge projects.
6 # Right now, this is hard-coded to generate a summary of open bugs.
8 # XML data for SourceForge project is available for download by project
9 # administrators. Because it's intended for backup purposes, you have
10 # to slurp the whole set of data, including info about all of the closed
11 # items, the feature requests, etc., so it can get big.
13 # You can do this by hand (if you're an administrator) with a URL like
14 # this (where 30337 is the group_id for SCons):
16 # http://sourceforge.net/export/xml_export.php?group_id=30337
18 # They also have a Perl script, called xml_export, available as part
19 # of a set of utilities called "adocman" which automate dealing with
20 # SourceForge document management from the command line. "adocman"
23 # https://sourceforge.net/projects/sitedocs/
27 import xml.sax.saxutils
32 'Unassigned' : 'nobody',
33 'Chad Austin' : 'aegis',
34 'Charle Crain' : 'diewarzau',
35 'Steven Knight' : 'stevenknight',
36 'Steve Leblanc' : 'stevenleblanc',
37 'Jeff Petkau' : 'jpet',
38 'Anthony Roach' : 'anthonyroach',
39 'Steven Shaw' : 'steven_shaw',
40 'Terrel Shumway' : 'terrelshumway',
41 'Greg Spencer' : 'greg_spencer',
42 'Christoph Wiedemann' : 'wiedeman',
46 """Just a place to hold attributes that we find in the XML."""
52 """Normalize white space. This will become important if/when
53 we enhance this to search for arbitrary fields."""
54 return string.join(string.split(text), ' ')
56 class ClassifyArtifacts(xml.sax.saxutils.DefaultHandler):
58 Simple SAX subclass to classify the artifacts in SourceForge
61 This reads up the fields in an XML description and turns the field
62 descriptions into attributes of an Artificat object, on the fly.
63 Artifacts are of the following types:
70 We could, if we choose to, add additional types in the future
71 by creating additional trackers.
73 This class loses some info right now because we don't pay attention
74 to the <messages> tag in the output, which contains a list of items
75 that have <field> tags in them. Right now, these just overwrite
76 each other in the Arifact object we create.
78 We also don't pay attention to any attributes of a <field> tag other
79 than the "name" attribute. We'll need to extend this class if we
80 ever want to pay attention to those attributes.
85 def startElement(self, name, attrs):
87 if name == 'artifact':
88 self.artifact = Artifact()
89 elif not self.artifact is None and name == 'field':
90 self.fname = attrs.get('name', None)
92 def characters(self, ch):
93 if not self.artifact is None:
94 self.text = self.text + ch
96 def endElement(self, name):
98 if name == 'artifact':
99 type = self.artifact.artifact_type
101 list = Artifacts[type]
103 Artifacts[type] = list = []
104 list.append(self.artifact)
106 elif not self.artifact is None and name == 'field':
107 setattr(self.artifact, self.fname, self.text)
109 if __name__ == '__main__':
111 parser = xml.sax.make_parser()
112 # Tell the parser we are not interested in XML namespaces.
113 parser.setFeature(xml.sax.handler.feature_namespaces, 0)
115 # Instantiate our handler and tell the parser to use it.
116 parser.setContentHandler(ClassifyArtifacts())
119 parser.parse(sys.argv[1])
121 # Hard-coded search for 'Open' bugs. This should be easily
122 # generalized once we figure out other things for this script to do.
123 bugs = filter(lambda x: x.status == 'Open', Artifacts['Bugs'])
125 print Artifacts.keys()
127 print "%d open bugs" % len(bugs)
129 # Sort them into a separate list for each assignee.
136 Assigned[a] = list = []
139 for a in SFName.keys():
141 b = Assigned[SFName[a]]
146 b.sort(lambda x, y: cmp(x.artifact_id, y.artifact_id))
148 print " %-6s %s" % (bug.artifact_id, bug.summary)