summaryrefslogtreecommitdiffstats
path: root/bin/sfsum
blob: 142793aff0d56af43a7d24755e2e272b5e016f31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python
#
# sfsum.py:  A script for parsing XML data exported from
#            SourceForge projects.
#
# Right now, this is hard-coded to generate a summary of open bugs.
#
# XML data for SourceForge project is available for download by project
# administrators.  Because it's intended for backup purposes, you have
# to slurp the whole set of data, including info about all of the closed
# items, the feature requests, etc., so it can get big.
#
# You can do this by hand (if you're an administrator) with a URL like
# this (where 30337 is the group_id for SCons):
#
#       http://sourceforge.net/export/xml_export.php?group_id=30337
#
# They also have a Perl script, called xml_export, available as part
# of a set of utilities called "adocman" which automate dealing with
# SourceForge document management from the command line.  "adocman"
# is available at:
#
#       https://sourceforge.net/projects/sitedocs/
#
from __future__ import print_function

import xml.sax
import xml.sax.saxutils
import sys

SFName = {
    'Unassigned'        	: 'nobody',
    'Chad Austin'       	: 'aegis',
    'Charle Crain'      	: 'diewarzau',
    'Steven Knight'     	: 'stevenknight',
    'Steve Leblanc'     	: 'stevenleblanc',
    'Jeff Petkau'       	: 'jpet',
    'Anthony Roach'     	: 'anthonyroach',
    'Steven Shaw'       	: 'steven_shaw',
    'Terrel Shumway'    	: 'terrelshumway',
    'Greg Spencer'              : 'greg_spencer',
    'Christoph Wiedemann'       : 'wiedeman',
}

class Artifact(object):
    """Just a place to hold attributes that we find in the XML."""
    pass

Artifacts = {}

def nws(text):
    """Normalize white space.  This will become important if/when
    we enhance this to search for arbitrary fields."""
    return ' '.join(text.split())

class ClassifyArtifacts(xml.sax.saxutils.DefaultHandler):
    """
    Simple SAX subclass to classify the artifacts in SourceForge
    XML output.

    This reads up the fields in an XML description and turns the field
    descriptions into attributes of an Artificat object, on the fly.
    Artifacts are of the following types:

        Bugs
        Feature Requests
        Patches
        Support Requests

    We could, if we choose to, add additional types in the future
    by creating additional trackers.

    This class loses some info right now because we don't pay attention
    to the <messages> tag in the output, which contains a list of items
    that have <field> tags in them.  Right now, these just overwrite
    each other in the Arifact object we create.

    We also don't pay attention to any attributes of a <field> tag other
    than the "name" attribute.  We'll need to extend this class if we
    ever want to pay attention to those attributes.
    """
    def __init__(self):
        self.artifact = None

    def startElement(self, name, attrs):
        self.text = ""
        if name == 'artifact':
            self.artifact = Artifact()
        elif not self.artifact is None and name == 'field':
            self.fname = attrs.get('name', None)

    def characters(self, ch):
        if not self.artifact is None:
            self.text = self.text + ch

    def endElement(self, name):
        global Artifacts
        if name == 'artifact':
            type = self.artifact.artifact_type
            try:
                list = Artifacts[type]
            except KeyError:
                Artifacts[type] = list = []
            list.append(self.artifact)
            self.artifact = None
        elif not self.artifact is None and name == 'field':
            setattr(self.artifact, self.fname, self.text)

if __name__ == '__main__':
    # Create a parser.
    parser = xml.sax.make_parser()
    # Tell the parser we are not interested in XML namespaces.
    parser.setFeature(xml.sax.handler.feature_namespaces, 0)

    # Instantiate our handler and tell the parser to use it.
    parser.setContentHandler(ClassifyArtifacts())

    # Parse the input.
    parser.parse(sys.argv[1])

    # Hard-coded search for 'Open' bugs.  This should be easily
    # generalized once we figure out other things for this script to do.
    bugs = [x for x in Artifacts['Bugs'] if x.status == 'Open']

    print(list(Artifacts.keys()))

    print("%d open bugs" % len(bugs))

    # Sort them into a separate list for each assignee.
    Assigned = {}
    for bug in bugs:
        a = bug.assigned_to
        try:
            list = Assigned[a]
        except KeyError:
            Assigned[a] = list = []
        list.append(bug)

    for a in SFName.keys():
        try:
            b = Assigned[SFName[a]]
        except KeyError:
            pass
        else:
            print("    %s" % a)
            b.sort(key=lambda x, y: (x.artifact_id, y.artifact_id))
            for bug in b:
                print("        %-6s  %s" % (bug.artifact_id, bug.summary))