Software Freedom Law Center

root/trunk/trac/contrib/sourceforge2trac.py

Revision 46, 11.1 kB (checked in by bkuhn, 11 months ago)

I am keeping a temporary fork of trac as I work on various changes to
it. I'll be coordinating with their mailing list soon.

Line 
1 """
2 Import a Sourceforge project's tracker items into a Trac database.
3
4 Requires:  Development version of Trac 0.7-pre from http://trac.edgewall.org/
5            ElementTree from effbot.org/zone/element.htm
6            Python 2.3 from http://www.python.org/
7            
8 The Sourceforge tracker items can be exported from the 'Backup' page of the
9 project admin section.
10
11 Copyright 2004, Mark Rowe <mrowe@bluewire.net.nz>
12 """
13
14 from elementtree.ElementTree import ElementTree
15 from datetime import datetime
16 import trac.env
17
18 class FieldParser(object):
19     def __init__(self, e):
20         for field in e:
21             if field.get('name').endswith('date'):
22                 setattr(self, field.get('name'), datetime.fromtimestamp(int(field.text)))
23             else:
24                 setattr(self, field.get('name'), field.text)       
25
26 class ArtifactHistoryItem(FieldParser):
27     def __repr__(self):
28         return '<ArtifactHistoryItem field_name=%r old_value=%r entrydate=%r mod_by=%r>' % (
29             self.field_name, self.old_value, self.entrydate, self.mod_by)
30
31 class ArtifactMessage(FieldParser):
32     def __repr__(self):
33         return '<ArtifactMessage adddate=%r user_name=%r body=%r>' % (self.adddate, self.user_name, self.body)
34
35 class Artifact(object):
36     def __init__(self, e):
37         self._history = []
38         self._messages = []
39        
40         for field in e:
41             if field.get('name') == 'artifact_history':
42                 for h in field:
43                     self._history.append(ArtifactHistoryItem(h))
44             elif field.get('name') == 'artifact_messages':
45                 for m in field:
46                     self._messages.append(ArtifactMessage(m))
47             else:
48                 setattr(self, field.get('name'), field.text)
49    
50     def history(self):
51         """Returns the history items in reverse chronological order so that the "new value"
52            can easily be calculated based on the final value of the field, and the old value
53            of the items occuring before it.
54         """
55         history = [(h.entrydate, h) for h in self._history]
56         history.sort()
57         return [h[1] for h in history][::-1]
58    
59     def messages(self):
60         return self._messages[:]
61    
62     def __repr__(self):
63         return '<Artifact summary=%r artifact_type=%r category=%r status=%r>' % (self.summary, self.artifact_type, self.category, self.status)
64
65 class ExportedProjectData(object):
66     def __init__(self, f):
67         self._artifacts = []
68        
69         root = ElementTree().parse(f)   
70        
71         for artifact in root.find('artifacts'):
72             self._artifacts.append(Artifact(artifact))
73    
74     def artifacts(self):
75         """Returns the artifacts in chronological order, so that they will be assigned numbers in sequence."""
76         artifacts = [(a.open_date, a) for a in self._artifacts]
77         artifacts.sort()
78         return [a[1] for a in artifacts]
79    
80     def featureRequests(self):
81         return [a for a in self._artifacts if a.artifact_type == 'Feature Requests']
82    
83     def bugs(self):
84         return [a for a in self._artifacts if a.artifact_type == 'Bugs']
85    
86     def categories(self):
87         """Returns all the category names that are used, in alphabetical order."""
88         c = {}
89         for a in self._artifacts:
90             c[a.category] = 1
91        
92         categories = c.keys()
93         categories.sort()
94         return categories
95    
96     def groups(self):
97         """Returns all the group names that are used, in alphabetical order."""
98         g = {}
99         for a in self._artifacts:
100             g[a.artifact_group_id] = 1
101         del g['None']
102        
103         groups = g.keys()
104         groups.sort()
105         return groups
106    
107     def artifactTypes(self):
108         """Returns all the artifact types that are used, in alphabetical order."""
109         t = {}
110         for a in self._artifacts:
111             t[a.artifact_type] = 1
112         types = t.keys()
113         types.sort()
114         return types
115
116 class TracDatabase(object):
117     def __init__(self, path):
118         self.env = trac.env.Environment(path)
119         self._db = self.env.get_db_cnx()
120         self._db.autocommit = False
121    
122     def db(self):
123         return self._db
124    
125     def hasTickets(self):
126         c = self.db().cursor()
127         c.execute('''SELECT count(*) FROM Ticket''')
128         return int(c.fetchall()[0][0]) > 0
129    
130     def setTypeList(self, s):
131         """Remove all types, set them to `s`"""
132         if self.hasTickets():
133             raise Exception("Will not modify database with existing tickets!")
134        
135         c = self.db().cursor()
136         c.execute("""DELETE FROM enum WHERE kind='ticket_type'""")
137         for i, value in enumerate(s):
138             c.execute("""INSERT INTO enum (kind, name, value) VALUES (%s, %s, %s)""",
139                       "ticket_type",
140                       value,
141                       i)
142         self.db().commit()
143    
144     def setPriorityList(self, s):
145         """Remove all priorities, set them to `s`"""
146         if self.hasTickets():
147             raise Exception("Will not modify database with existing tickets!")
148        
149         c = self.db().cursor()
150         c.execute("""DELETE FROM enum WHERE kind='priority'""")
151         for i, value in enumerate(s):
152             c.execute("""INSERT INTO enum (kind, name, value) VALUES (%s, %s, %s)""",
153                       "priority",
154                       value,
155                       i)
156         self.db().commit()
157
158    
159     def setComponentList(self, l):
160         """Remove all components, set them to `l`"""
161         if self.hasTickets():
162             raise Exception("Will not modify database with existing tickets!")
163        
164         c = self.db().cursor()
165         c.execute("""DELETE FROM component""")
166         for value in l:
167             c.execute("""INSERT INTO component (name) VALUES (%s)""",
168                       value)
169         self.db().commit()
170    
171     def setVersionList(self, v):
172         """Remove all versions, set them to `v`"""
173         if self.hasTickets():
174             raise Exception("Will not modify database with existing tickets!")
175        
176         c = self.db().cursor()
177         c.execute("""DELETE FROM version""")
178         for value in v:
179             c.execute("""INSERT INTO version (name) VALUES (%s)""",
180                       value)
181         self.db().commit()
182        
183     def setMilestoneList(self, m):
184         """Remove all milestones, set them to `m`"""
185         if self.hasTickets():
186             raise Exception("Will not modify database with existing tickets!")
187        
188         c = self.db().cursor()
189         c.execute("""DELETE FROM milestone""")
190         for value in m:
191             c.execute("""INSERT INTO milestone (name) VALUES (%s)""",
192                       value)
193         self.db().commit()
194    
195     def addTicket(self, type, time, changetime, component,
196                   priority, owner, reporter, cc,
197                   version, milestone, status, resolution,
198                   summary, description, keywords):
199         c = self.db().cursor()
200         if status.lower() == 'open':
201             if owner != '':
202                 status = 'assigned'
203             else:
204                 status = 'new'
205
206         c.execute("""INSERT INTO ticket (type, time, changetime, component,
207                                          priority, owner, reporter, cc,
208                                          version, milestone, status, resolution,
209                                          summary, description, keywords)
210                                  VALUES (%s, %s, %s,
211                                          %s, %s, %s, %s, %s,
212                                          %s, %s, %s, %s,
213                                          %s, %s, %s)""",
214                   type, time, changetime, component,
215                   priority, owner, reporter, cc,
216                   version, milestone, status.lower(), resolution,
217                   summary, '{{{\n%s\n}}}' % (description, ), keywords)
218         self.db().commit()
219         return self.db().db.sqlite_last_insert_rowid()
220    
221     def addTicketComment(self, ticket, time, author, value):
222         c = self.db().cursor()
223         c.execute("""INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue)
224                                  VALUES        (%s, %s, %s, %s, %s, %s)""",
225                   ticket, time.strftime('%s'), author, 'comment', '', '{{{\n%s\n}}}' % (value, ))
226         self.db().commit()
227
228     def addTicketChange(self, ticket, time, author, field, oldvalue, newvalue):
229         c = self.db().cursor()
230         c.execute("""INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue)
231                                  VALUES        (%s, %s, %s, %s, %s, %s)""",
232                   ticket, time.strftime('%s'), author, field, oldvalue, newvalue)
233         self.db().commit()
234
235
236 def main():
237     import optparse
238     p = optparse.OptionParser('usage: %prog xml_export.xml /path/to/trac/environment')
239     opt, args = p.parse_args()
240     if len(args) != 2:
241         p.error("Incorrect number of arguments")
242    
243     try:
244         importData(open(args[0]), args[1])
245     except Exception, e:
246         print 'Error:', e
247
248 def importData(f, env):
249     project = ExportedProjectData(f)
250    
251     db = TracDatabase(env)
252     db.setTypeList(project.artifactTypes())
253     db.setComponentList(project.categories())
254     db.setPriorityList(range(1, 11))
255     db.setVersionList(project.groups())
256     db.setMilestoneList([])
257    
258     for a in project.artifacts():
259         i = db.addTicket(type=a.artifact_type,
260                          time=a.open_date,
261                          changetime='',
262                          component=a.category,
263                          priority=a.priority,
264                          owner=a.assigned_to,
265                          reporter=a.submitted_by,
266                          cc='',
267                          version=a.artifact_group_id,
268                          milestone='',
269                          status=a.status,
270                          resolution=a.resolution,
271                          summary=a.summary,
272                          description=a.details,
273                          keywords='')
274         print 'Imported %s as #%d' % (a.artifact_id, i)
275         for msg in a.messages():
276             db.addTicketComment(ticket=i,
277                                 time=msg.adddate,
278                                 author=msg.user_name,
279                                 value=msg.body)
280         if a.messages():
281             print '    imported %d messages for #%d' % (len(a.messages()), i)
282        
283         values = a.__dict__.copy()
284         field_map = {'summary': 'summary'}
285         for h in a.history():
286             if h.field_name == 'close_date' and values.get(h.field_name, '') == '':
287                 f = 'status'
288                 oldvalue = 'assigned'
289                 newvalue = 'closed'
290             else:
291                 f = field_map.get(h.field_name, None)
292                 oldvalue = h.old_value
293                 newvalue = values.get(h.field_name, '')
294                
295             if f:
296                 db.addTicketChange(ticket=i,
297                                    time=h.entrydate,
298                                    author=h.mod_by,
299                                    field=f,
300                                    oldvalue=oldvalue,
301                                    newvalue=newvalue)
302             values[h.field_name] = h.old_value
303
304 if __name__ == '__main__':
305     main()
Note: See TracBrowser for help on using the browser.

SFLC Main Page

[frdm] Support SFLC