Mercurial > hg > soundsoftware-site
comparison lib/tasks/.svn/text-base/migrate_from_mantis.rake.svn-base @ 0:513646585e45
* Import Redmine trunk SVN rev 3859
author | Chris Cannam |
---|---|
date | Fri, 23 Jul 2010 15:52:44 +0100 |
parents | |
children | 94944d00e43c |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:513646585e45 |
---|---|
1 # redMine - project management software | |
2 # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 # | |
4 # This program is free software; you can redistribute it and/or | |
5 # modify it under the terms of the GNU General Public License | |
6 # as published by the Free Software Foundation; either version 2 | |
7 # of the License, or (at your option) any later version. | |
8 # | |
9 # This program is distributed in the hope that it will be useful, | |
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 # GNU General Public License for more details. | |
13 # | |
14 # You should have received a copy of the GNU General Public License | |
15 # along with this program; if not, write to the Free Software | |
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 | |
18 desc 'Mantis migration script' | |
19 | |
20 require 'active_record' | |
21 require 'iconv' | |
22 require 'pp' | |
23 | |
24 namespace :redmine do | |
25 task :migrate_from_mantis => :environment do | |
26 | |
27 module MantisMigrate | |
28 | |
29 DEFAULT_STATUS = IssueStatus.default | |
30 assigned_status = IssueStatus.find_by_position(2) | |
31 resolved_status = IssueStatus.find_by_position(3) | |
32 feedback_status = IssueStatus.find_by_position(4) | |
33 closed_status = IssueStatus.find :first, :conditions => { :is_closed => true } | |
34 STATUS_MAPPING = {10 => DEFAULT_STATUS, # new | |
35 20 => feedback_status, # feedback | |
36 30 => DEFAULT_STATUS, # acknowledged | |
37 40 => DEFAULT_STATUS, # confirmed | |
38 50 => assigned_status, # assigned | |
39 80 => resolved_status, # resolved | |
40 90 => closed_status # closed | |
41 } | |
42 | |
43 priorities = IssuePriority.all | |
44 DEFAULT_PRIORITY = priorities[2] | |
45 PRIORITY_MAPPING = {10 => priorities[1], # none | |
46 20 => priorities[1], # low | |
47 30 => priorities[2], # normal | |
48 40 => priorities[3], # high | |
49 50 => priorities[4], # urgent | |
50 60 => priorities[5] # immediate | |
51 } | |
52 | |
53 TRACKER_BUG = Tracker.find_by_position(1) | |
54 TRACKER_FEATURE = Tracker.find_by_position(2) | |
55 | |
56 roles = Role.find(:all, :conditions => {:builtin => 0}, :order => 'position ASC') | |
57 manager_role = roles[0] | |
58 developer_role = roles[1] | |
59 DEFAULT_ROLE = roles.last | |
60 ROLE_MAPPING = {10 => DEFAULT_ROLE, # viewer | |
61 25 => DEFAULT_ROLE, # reporter | |
62 40 => DEFAULT_ROLE, # updater | |
63 55 => developer_role, # developer | |
64 70 => manager_role, # manager | |
65 90 => manager_role # administrator | |
66 } | |
67 | |
68 CUSTOM_FIELD_TYPE_MAPPING = {0 => 'string', # String | |
69 1 => 'int', # Numeric | |
70 2 => 'int', # Float | |
71 3 => 'list', # Enumeration | |
72 4 => 'string', # Email | |
73 5 => 'bool', # Checkbox | |
74 6 => 'list', # List | |
75 7 => 'list', # Multiselection list | |
76 8 => 'date', # Date | |
77 } | |
78 | |
79 RELATION_TYPE_MAPPING = {1 => IssueRelation::TYPE_RELATES, # related to | |
80 2 => IssueRelation::TYPE_RELATES, # parent of | |
81 3 => IssueRelation::TYPE_RELATES, # child of | |
82 0 => IssueRelation::TYPE_DUPLICATES, # duplicate of | |
83 4 => IssueRelation::TYPE_DUPLICATES # has duplicate | |
84 } | |
85 | |
86 class MantisUser < ActiveRecord::Base | |
87 set_table_name :mantis_user_table | |
88 | |
89 def firstname | |
90 @firstname = realname.blank? ? username : realname.split.first[0..29] | |
91 @firstname.gsub!(/[^\w\s\'\-]/i, '') | |
92 @firstname | |
93 end | |
94 | |
95 def lastname | |
96 @lastname = realname.blank? ? '-' : realname.split[1..-1].join(' ')[0..29] | |
97 @lastname.gsub!(/[^\w\s\'\-]/i, '') | |
98 @lastname = '-' if @lastname.blank? | |
99 @lastname | |
100 end | |
101 | |
102 def email | |
103 if read_attribute(:email).match(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i) && | |
104 !User.find_by_mail(read_attribute(:email)) | |
105 @email = read_attribute(:email) | |
106 else | |
107 @email = "#{username}@foo.bar" | |
108 end | |
109 end | |
110 | |
111 def username | |
112 read_attribute(:username)[0..29].gsub(/[^a-zA-Z0-9_\-@\.]/, '-') | |
113 end | |
114 end | |
115 | |
116 class MantisProject < ActiveRecord::Base | |
117 set_table_name :mantis_project_table | |
118 has_many :versions, :class_name => "MantisVersion", :foreign_key => :project_id | |
119 has_many :categories, :class_name => "MantisCategory", :foreign_key => :project_id | |
120 has_many :news, :class_name => "MantisNews", :foreign_key => :project_id | |
121 has_many :members, :class_name => "MantisProjectUser", :foreign_key => :project_id | |
122 | |
123 def name | |
124 read_attribute(:name)[0..29] | |
125 end | |
126 | |
127 def identifier | |
128 read_attribute(:name).underscore[0..19].gsub(/[^a-z0-9\-]/, '-') | |
129 end | |
130 end | |
131 | |
132 class MantisVersion < ActiveRecord::Base | |
133 set_table_name :mantis_project_version_table | |
134 | |
135 def version | |
136 read_attribute(:version)[0..29] | |
137 end | |
138 | |
139 def description | |
140 read_attribute(:description)[0..254] | |
141 end | |
142 end | |
143 | |
144 class MantisCategory < ActiveRecord::Base | |
145 set_table_name :mantis_project_category_table | |
146 end | |
147 | |
148 class MantisProjectUser < ActiveRecord::Base | |
149 set_table_name :mantis_project_user_list_table | |
150 end | |
151 | |
152 class MantisBug < ActiveRecord::Base | |
153 set_table_name :mantis_bug_table | |
154 belongs_to :bug_text, :class_name => "MantisBugText", :foreign_key => :bug_text_id | |
155 has_many :bug_notes, :class_name => "MantisBugNote", :foreign_key => :bug_id | |
156 has_many :bug_files, :class_name => "MantisBugFile", :foreign_key => :bug_id | |
157 has_many :bug_monitors, :class_name => "MantisBugMonitor", :foreign_key => :bug_id | |
158 end | |
159 | |
160 class MantisBugText < ActiveRecord::Base | |
161 set_table_name :mantis_bug_text_table | |
162 | |
163 # Adds Mantis steps_to_reproduce and additional_information fields | |
164 # to description if any | |
165 def full_description | |
166 full_description = description | |
167 full_description += "\n\n*Steps to reproduce:*\n\n#{steps_to_reproduce}" unless steps_to_reproduce.blank? | |
168 full_description += "\n\n*Additional information:*\n\n#{additional_information}" unless additional_information.blank? | |
169 full_description | |
170 end | |
171 end | |
172 | |
173 class MantisBugNote < ActiveRecord::Base | |
174 set_table_name :mantis_bugnote_table | |
175 belongs_to :bug, :class_name => "MantisBug", :foreign_key => :bug_id | |
176 belongs_to :bug_note_text, :class_name => "MantisBugNoteText", :foreign_key => :bugnote_text_id | |
177 end | |
178 | |
179 class MantisBugNoteText < ActiveRecord::Base | |
180 set_table_name :mantis_bugnote_text_table | |
181 end | |
182 | |
183 class MantisBugFile < ActiveRecord::Base | |
184 set_table_name :mantis_bug_file_table | |
185 | |
186 def size | |
187 filesize | |
188 end | |
189 | |
190 def original_filename | |
191 MantisMigrate.encode(filename) | |
192 end | |
193 | |
194 def content_type | |
195 file_type | |
196 end | |
197 | |
198 def read(*args) | |
199 if @read_finished | |
200 nil | |
201 else | |
202 @read_finished = true | |
203 content | |
204 end | |
205 end | |
206 end | |
207 | |
208 class MantisBugRelationship < ActiveRecord::Base | |
209 set_table_name :mantis_bug_relationship_table | |
210 end | |
211 | |
212 class MantisBugMonitor < ActiveRecord::Base | |
213 set_table_name :mantis_bug_monitor_table | |
214 end | |
215 | |
216 class MantisNews < ActiveRecord::Base | |
217 set_table_name :mantis_news_table | |
218 end | |
219 | |
220 class MantisCustomField < ActiveRecord::Base | |
221 set_table_name :mantis_custom_field_table | |
222 set_inheritance_column :none | |
223 has_many :values, :class_name => "MantisCustomFieldString", :foreign_key => :field_id | |
224 has_many :projects, :class_name => "MantisCustomFieldProject", :foreign_key => :field_id | |
225 | |
226 def format | |
227 read_attribute :type | |
228 end | |
229 | |
230 def name | |
231 read_attribute(:name)[0..29].gsub(/[^\w\s\'\-]/, '-') | |
232 end | |
233 end | |
234 | |
235 class MantisCustomFieldProject < ActiveRecord::Base | |
236 set_table_name :mantis_custom_field_project_table | |
237 end | |
238 | |
239 class MantisCustomFieldString < ActiveRecord::Base | |
240 set_table_name :mantis_custom_field_string_table | |
241 end | |
242 | |
243 | |
244 def self.migrate | |
245 | |
246 # Users | |
247 print "Migrating users" | |
248 User.delete_all "login <> 'admin'" | |
249 users_map = {} | |
250 users_migrated = 0 | |
251 MantisUser.find(:all).each do |user| | |
252 u = User.new :firstname => encode(user.firstname), | |
253 :lastname => encode(user.lastname), | |
254 :mail => user.email, | |
255 :last_login_on => user.last_visit | |
256 u.login = user.username | |
257 u.password = 'mantis' | |
258 u.status = User::STATUS_LOCKED if user.enabled != 1 | |
259 u.admin = true if user.access_level == 90 | |
260 next unless u.save! | |
261 users_migrated += 1 | |
262 users_map[user.id] = u.id | |
263 print '.' | |
264 end | |
265 puts | |
266 | |
267 # Projects | |
268 print "Migrating projects" | |
269 Project.destroy_all | |
270 projects_map = {} | |
271 versions_map = {} | |
272 categories_map = {} | |
273 MantisProject.find(:all).each do |project| | |
274 p = Project.new :name => encode(project.name), | |
275 :description => encode(project.description) | |
276 p.identifier = project.identifier | |
277 next unless p.save | |
278 projects_map[project.id] = p.id | |
279 p.enabled_module_names = ['issue_tracking', 'news', 'wiki'] | |
280 p.trackers << TRACKER_BUG | |
281 p.trackers << TRACKER_FEATURE | |
282 print '.' | |
283 | |
284 # Project members | |
285 project.members.each do |member| | |
286 m = Member.new :user => User.find_by_id(users_map[member.user_id]), | |
287 :roles => [ROLE_MAPPING[member.access_level] || DEFAULT_ROLE] | |
288 m.project = p | |
289 m.save | |
290 end | |
291 | |
292 # Project versions | |
293 project.versions.each do |version| | |
294 v = Version.new :name => encode(version.version), | |
295 :description => encode(version.description), | |
296 :effective_date => version.date_order.to_date | |
297 v.project = p | |
298 v.save | |
299 versions_map[version.id] = v.id | |
300 end | |
301 | |
302 # Project categories | |
303 project.categories.each do |category| | |
304 g = IssueCategory.new :name => category.category[0,30] | |
305 g.project = p | |
306 g.save | |
307 categories_map[category.category] = g.id | |
308 end | |
309 end | |
310 puts | |
311 | |
312 # Bugs | |
313 print "Migrating bugs" | |
314 Issue.destroy_all | |
315 issues_map = {} | |
316 keep_bug_ids = (Issue.count == 0) | |
317 MantisBug.find_each(:batch_size => 200) do |bug| | |
318 next unless projects_map[bug.project_id] && users_map[bug.reporter_id] | |
319 i = Issue.new :project_id => projects_map[bug.project_id], | |
320 :subject => encode(bug.summary), | |
321 :description => encode(bug.bug_text.full_description), | |
322 :priority => PRIORITY_MAPPING[bug.priority] || DEFAULT_PRIORITY, | |
323 :created_on => bug.date_submitted, | |
324 :updated_on => bug.last_updated | |
325 i.author = User.find_by_id(users_map[bug.reporter_id]) | |
326 i.category = IssueCategory.find_by_project_id_and_name(i.project_id, bug.category[0,30]) unless bug.category.blank? | |
327 i.fixed_version = Version.find_by_project_id_and_name(i.project_id, bug.fixed_in_version) unless bug.fixed_in_version.blank? | |
328 i.status = STATUS_MAPPING[bug.status] || DEFAULT_STATUS | |
329 i.tracker = (bug.severity == 10 ? TRACKER_FEATURE : TRACKER_BUG) | |
330 i.id = bug.id if keep_bug_ids | |
331 next unless i.save | |
332 issues_map[bug.id] = i.id | |
333 print '.' | |
334 STDOUT.flush | |
335 | |
336 # Assignee | |
337 # Redmine checks that the assignee is a project member | |
338 if (bug.handler_id && users_map[bug.handler_id]) | |
339 i.assigned_to = User.find_by_id(users_map[bug.handler_id]) | |
340 i.save_with_validation(false) | |
341 end | |
342 | |
343 # Bug notes | |
344 bug.bug_notes.each do |note| | |
345 next unless users_map[note.reporter_id] | |
346 n = Journal.new :notes => encode(note.bug_note_text.note), | |
347 :created_on => note.date_submitted | |
348 n.user = User.find_by_id(users_map[note.reporter_id]) | |
349 n.journalized = i | |
350 n.save | |
351 end | |
352 | |
353 # Bug files | |
354 bug.bug_files.each do |file| | |
355 a = Attachment.new :created_on => file.date_added | |
356 a.file = file | |
357 a.author = User.find :first | |
358 a.container = i | |
359 a.save | |
360 end | |
361 | |
362 # Bug monitors | |
363 bug.bug_monitors.each do |monitor| | |
364 next unless users_map[monitor.user_id] | |
365 i.add_watcher(User.find_by_id(users_map[monitor.user_id])) | |
366 end | |
367 end | |
368 | |
369 # update issue id sequence if needed (postgresql) | |
370 Issue.connection.reset_pk_sequence!(Issue.table_name) if Issue.connection.respond_to?('reset_pk_sequence!') | |
371 puts | |
372 | |
373 # Bug relationships | |
374 print "Migrating bug relations" | |
375 MantisBugRelationship.find(:all).each do |relation| | |
376 next unless issues_map[relation.source_bug_id] && issues_map[relation.destination_bug_id] | |
377 r = IssueRelation.new :relation_type => RELATION_TYPE_MAPPING[relation.relationship_type] | |
378 r.issue_from = Issue.find_by_id(issues_map[relation.source_bug_id]) | |
379 r.issue_to = Issue.find_by_id(issues_map[relation.destination_bug_id]) | |
380 pp r unless r.save | |
381 print '.' | |
382 STDOUT.flush | |
383 end | |
384 puts | |
385 | |
386 # News | |
387 print "Migrating news" | |
388 News.destroy_all | |
389 MantisNews.find(:all, :conditions => 'project_id > 0').each do |news| | |
390 next unless projects_map[news.project_id] | |
391 n = News.new :project_id => projects_map[news.project_id], | |
392 :title => encode(news.headline[0..59]), | |
393 :description => encode(news.body), | |
394 :created_on => news.date_posted | |
395 n.author = User.find_by_id(users_map[news.poster_id]) | |
396 n.save | |
397 print '.' | |
398 STDOUT.flush | |
399 end | |
400 puts | |
401 | |
402 # Custom fields | |
403 print "Migrating custom fields" | |
404 IssueCustomField.destroy_all | |
405 MantisCustomField.find(:all).each do |field| | |
406 f = IssueCustomField.new :name => field.name[0..29], | |
407 :field_format => CUSTOM_FIELD_TYPE_MAPPING[field.format], | |
408 :min_length => field.length_min, | |
409 :max_length => field.length_max, | |
410 :regexp => field.valid_regexp, | |
411 :possible_values => field.possible_values.split('|'), | |
412 :is_required => field.require_report? | |
413 next unless f.save | |
414 print '.' | |
415 STDOUT.flush | |
416 # Trackers association | |
417 f.trackers = Tracker.find :all | |
418 | |
419 # Projects association | |
420 field.projects.each do |project| | |
421 f.projects << Project.find_by_id(projects_map[project.project_id]) if projects_map[project.project_id] | |
422 end | |
423 | |
424 # Values | |
425 field.values.each do |value| | |
426 v = CustomValue.new :custom_field_id => f.id, | |
427 :value => value.value | |
428 v.customized = Issue.find_by_id(issues_map[value.bug_id]) if issues_map[value.bug_id] | |
429 v.save | |
430 end unless f.new_record? | |
431 end | |
432 puts | |
433 | |
434 puts | |
435 puts "Users: #{users_migrated}/#{MantisUser.count}" | |
436 puts "Projects: #{Project.count}/#{MantisProject.count}" | |
437 puts "Memberships: #{Member.count}/#{MantisProjectUser.count}" | |
438 puts "Versions: #{Version.count}/#{MantisVersion.count}" | |
439 puts "Categories: #{IssueCategory.count}/#{MantisCategory.count}" | |
440 puts "Bugs: #{Issue.count}/#{MantisBug.count}" | |
441 puts "Bug notes: #{Journal.count}/#{MantisBugNote.count}" | |
442 puts "Bug files: #{Attachment.count}/#{MantisBugFile.count}" | |
443 puts "Bug relations: #{IssueRelation.count}/#{MantisBugRelationship.count}" | |
444 puts "Bug monitors: #{Watcher.count}/#{MantisBugMonitor.count}" | |
445 puts "News: #{News.count}/#{MantisNews.count}" | |
446 puts "Custom fields: #{IssueCustomField.count}/#{MantisCustomField.count}" | |
447 end | |
448 | |
449 def self.encoding(charset) | |
450 @ic = Iconv.new('UTF-8', charset) | |
451 rescue Iconv::InvalidEncoding | |
452 return false | |
453 end | |
454 | |
455 def self.establish_connection(params) | |
456 constants.each do |const| | |
457 klass = const_get(const) | |
458 next unless klass.respond_to? 'establish_connection' | |
459 klass.establish_connection params | |
460 end | |
461 end | |
462 | |
463 def self.encode(text) | |
464 @ic.iconv text | |
465 rescue | |
466 text | |
467 end | |
468 end | |
469 | |
470 puts | |
471 if Redmine::DefaultData::Loader.no_data? | |
472 puts "Redmine configuration need to be loaded before importing data." | |
473 puts "Please, run this first:" | |
474 puts | |
475 puts " rake redmine:load_default_data RAILS_ENV=\"#{ENV['RAILS_ENV']}\"" | |
476 exit | |
477 end | |
478 | |
479 puts "WARNING: Your Redmine data will be deleted during this process." | |
480 print "Are you sure you want to continue ? [y/N] " | |
481 STDOUT.flush | |
482 break unless STDIN.gets.match(/^y$/i) | |
483 | |
484 # Default Mantis database settings | |
485 db_params = {:adapter => 'mysql', | |
486 :database => 'bugtracker', | |
487 :host => 'localhost', | |
488 :username => 'root', | |
489 :password => '' } | |
490 | |
491 puts | |
492 puts "Please enter settings for your Mantis database" | |
493 [:adapter, :host, :database, :username, :password].each do |param| | |
494 print "#{param} [#{db_params[param]}]: " | |
495 value = STDIN.gets.chomp! | |
496 db_params[param] = value unless value.blank? | |
497 end | |
498 | |
499 while true | |
500 print "encoding [UTF-8]: " | |
501 STDOUT.flush | |
502 encoding = STDIN.gets.chomp! | |
503 encoding = 'UTF-8' if encoding.blank? | |
504 break if MantisMigrate.encoding encoding | |
505 puts "Invalid encoding!" | |
506 end | |
507 puts | |
508 | |
509 # Make sure bugs can refer bugs in other projects | |
510 Setting.cross_project_issue_relations = 1 if Setting.respond_to? 'cross_project_issue_relations' | |
511 | |
512 # Turn off email notifications | |
513 Setting.notified_events = [] | |
514 | |
515 MantisMigrate.establish_connection db_params | |
516 MantisMigrate.migrate | |
517 end | |
518 end |