Mercurial > genshi > mirror
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() |