File: SourceForgeTracker.py

package info (click to toggle)
pythoncard 0.8.2-2
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 8,452 kB
  • sloc: python: 56,787; makefile: 56; sh: 22
file content (205 lines) | stat: -rw-r--r-- 8,002 bytes parent folder | download | duplicates (3)
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/python

"""
Currently only displays Open topic items. Providing more options
and displaying the full info set from the tracker database is left
as an exercise for the reader. I did the hard part. ;-)

Thanks to Mark Pilgrim for the DOM parsing code.
http://diveintopython.org/kgp_divein.html
Thanks to Mark Pilgrim and Martin Martin v. Loewis for help
with XML and UNICODE issues.
http://aspn.activestate.com/ASPN/Mail/Message/xml-sig/967244
"""
__version__ = "$Revision: 1.23 $"
__date__ = "$Date: 2005/12/13 11:13:21 $"

from PythonCard import configuration, model
import os

from xml.dom import minidom
import urllib
import webbrowser

BUGS = 'Bug Reports'
FEATURE_REQUESTS = 'Feature Requests'
TOPIC_SEPARATOR = "  :  "


def getText(node):
    return "".join([c.data for c in node.childNodes if c.nodeType == c.TEXT_NODE]).encode('ascii', 'ignore')

def doParse(xml):
    xml = xml.replace(chr(19), '')
    xmldoc = minidom.parseString(xml)
    artifacts = xmldoc.getElementsByTagName('artifact')
    trackerDict = {}
    for a in artifacts:
        key = a.attributes["id"].value
        trackerDict[key] = \
            {"summary":getText(a.getElementsByTagName("summary")[0]),
             "detail":getText(a.getElementsByTagName("detail")[0]),
             "status":getText(a.getElementsByTagName("status")[0])}
        try:
            followups = a.getElementsByTagName('item')
            for f in followups:
                trackerDict[key]['detail'] += '\n\n\n' + \
                'Sender: ' + getText(f.getElementsByTagName("sender")[0]) + '\n' + \
                getText(f.getElementsByTagName("text")[0])
        except IndexError:
            pass
    return trackerDict

class Tracker(model.Background):

    def on_initialize(self, event):
        self.configPath = os.path.join(configuration.homedir, 'sourceforgetracker')
        if not os.path.exists(self.configPath):
            os.mkdir(self.configPath)

        self.baseSFUrl = 'http://sourceforge.net/export/sf_tracker_export.php?'
        self.groupIds = {'Boa':1909,
                         'PyChecker':24686, 'PyCrust':31263,
                         'Python': 5470, 'PythonCard':19015,
                         'Scintilla':2439,
                         'wxPython':10718
                         }
        self.categoryIds = {'Boa': {BUGS:101909, FEATURE_REQUESTS:351909},
                            'PyChecker': {BUGS:382217, FEATURE_REQUESTS:382220},
                            'PyCrust': {BUGS:401706, FEATURE_REQUESTS:401709},
                            'Python': {BUGS:105470, FEATURE_REQUESTS:355470},
                            'PythonCard': {BUGS:119015, FEATURE_REQUESTS:369015},
                            'Scintilla': {BUGS:102439, FEATURE_REQUESTS:352439},
                            'wxPython': {BUGS:310718, FEATURE_REQUESTS:360718}
                            }
        self.defaultGroup = 'PythonCard'
        self.defaultCategory = FEATURE_REQUESTS

        temp = self.groupIds.keys()
        temp.sort()
        self.components.choiceGroups.items = temp
        self.components.choiceGroups.stringSelection = self.defaultGroup
        self.components.choiceCategories.stringSelection = self.defaultCategory

        #self.parser = None
        self.trackerDict = {}
        self.displayTopics(self.defaultGroup, self.defaultCategory)

    def status(self, txt):
        self.components.staticStatus.text = txt

    def buildUrl(self, group, category):
        groupId = self.groupIds[group]
        atId = self.categoryIds[group][category]
        return self.baseSFUrl + 'atid=' + str(atId) + '&group_id=' + str(groupId)

    def buildFilename(self, group, category):
        name = group + '_' + category.replace(' ', '') + '.xml'
        return os.path.join(self.configPath, name)

    def loadXML(self, group, category):
        filename = self.buildFilename(group, category)
        try:
            fp = open(filename, 'rb')
            xml = fp.read()
            fp.close()
            return xml
        except IOError:
            return ''

    def displayTopics(self, group, category):
        self.components.topicList.clear()
        filename = self.buildFilename(group, category)
        if not os.path.exists(filename):
            url = self.buildUrl(group, category)
            #print "downloading", filename, url
            self.downloadFile(url, filename)
        xml = self.loadXML(group, category)
        self.status('Parsing XML...')
        self.trackerDict = doParse(xml)

        self.status('Display Topics...')
        topics = []
        for artifact in self.trackerDict:
            # handling other variations is left as an exercise for the reader
            if self.trackerDict[artifact]['status'] == 'Open':
                topics.append(artifact + TOPIC_SEPARATOR + self.trackerDict[artifact]['summary'])
        topics.sort()
        self.components.topicList.clear()
        for t in topics:
            self.components.topicList.append(t)
        self.status('')

    def downloadFile(self, url, filename):
        try:
            #print "url", url
            self.status("Downloading...")
            fp = urllib.urlopen(url)
            xml = fp.read()
            fp.close()
            #print "downloaded", url

            self.status("Writing file...")
            #print filename
            op = open(filename, 'wb')
            # fix SourceForge malformed XML
            op.write('<?xml version="1.0" encoding="iso-8859-1" ?>\n')
            op.write(xml)
            op.close()
            #print "wrote", filename
        except IOError:
            pass
            # show a warning dialog one of these days
        self.status('')

    def on_choiceGroups_select(self, event):
        group = event.target.stringSelection
        category = self.components.choiceCategories.stringSelection
        self.displayTopics(group, category)

    def on_choiceCategories_select(self, event):
        group = self.components.choiceGroups.stringSelection
        category = event.target.stringSelection
        self.displayTopics(group, category)

    def on_topicList_select(self, event):
        artifact, summary = event.target.stringSelection.split(TOPIC_SEPARATOR)
        self.components.topicDetail.text = self.trackerDict[str(artifact)]['detail']

    def doLaunch(self, url):
        # launch a new browser window and autoraise it
        # there appears to be a bug in webbrowser.py because
        # if a window already exists, a new one isn't being created ?!
        webbrowser.open(url, 1, 1)

    def on_topicList_mouseDoubleClick(self, event):
        # http://sourceforge.net/tracker/index.php?func=detail&aid=446264&group_id=19015&atid=119015
        selection = event.target.stringSelection
        if selection != "":
            artifactId, summary = event.target.stringSelection.split(TOPIC_SEPARATOR)
            group = self.components.choiceGroups.stringSelection
            groupId = self.groupIds[group]
            category = self.components.choiceCategories.stringSelection
            categoryId = self.categoryIds[group][category]
            url = 'http://sourceforge.net/tracker/index.php?func=detail&aid=' + \
                  str(artifactId) + '&group_id=' + str(groupId) + '&atid=' + str(categoryId)
            self.doLaunch(url)

    def on_buttonDownload_mouseClick(self, event):
        group = self.components.choiceGroups.stringSelection
        category = self.components.choiceCategories.stringSelection
        filename = self.buildFilename(group, category)
        url = self.buildUrl(group, category)
        #print filename
        #print url
        self.downloadFile(url, filename)
        self.displayTopics(group, category)


if __name__ == '__main__':
    app = model.Application(Tracker)
    # use the following initialization instead
    # if you don't like the colorized layout
    #app = model.Application(Tracker, 'SourceForgeTracker.original.rsrc.py')
    app.MainLoop()