Guest User

Untitled

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