comparison examples/trac/contrib/sourceforge2trac.py @ 39:93b4dcbafd7b trunk

Copy Trac to main branch.
author cmlenz
date Mon, 03 Jul 2006 18:53:27 +0000
parents
children
comparison
equal deleted inserted replaced
38:ee669cb9cccc 39:93b4dcbafd7b
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.com/
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()
Copyright (C) 2012-2017 Edgewall Software