kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
6.0 KiB
174 lines
6.0 KiB
class MigrateLegacy < ActiveRecord::Migration
|
|
def self.normalize_value(v, t)
|
|
return nil if v.class == NilClass
|
|
|
|
case t
|
|
when :int
|
|
return Integer(v)
|
|
|
|
when :bool
|
|
if [TrueClass, FalseClass].include?(v.class)
|
|
return v
|
|
else
|
|
return ! (['', '0'].include?("#{v}"))
|
|
end
|
|
|
|
else
|
|
return v
|
|
end
|
|
end
|
|
|
|
def self.row(r, t)
|
|
normalized = []
|
|
r.each_with_index{|v, i|
|
|
normalized << MigrateLegacy.normalize_value(v, t[i])
|
|
}
|
|
return normalized
|
|
end
|
|
|
|
def self.up
|
|
begin
|
|
execute "select count(*) from backlogs"
|
|
legacy = true
|
|
rescue
|
|
legacy = false
|
|
end
|
|
|
|
adapter = ActiveRecord::Base.connection.instance_variable_get("@config")[:adapter].downcase
|
|
|
|
ActiveRecord::Base.connection.commit_db_transaction unless adapter.include?('sqlite')
|
|
|
|
if legacy
|
|
Story.reset_column_information
|
|
Issue.reset_column_information
|
|
Task.reset_column_information
|
|
|
|
if Story.trackers.nil? || Story.trackers.size == 0 || Task.tracker.nil?
|
|
raise "Please configure the Backlogs Story and Task trackers before migrating.
|
|
|
|
You do this by starting Redmine and going to \"Administration -> Plugins -> Redmine Scrum Plugin -> Configure\"
|
|
and setting up the Task tracker and one or more Story trackers.
|
|
You might have to go to \"Administration -> Trackers\" first
|
|
and create new trackers for this purpose. After doing this, stop
|
|
redmine and re-run this migration."
|
|
end
|
|
|
|
trackers = {}
|
|
|
|
# find story/task trackers per project
|
|
execute("
|
|
select projects.id as project_id, pt.tracker_id as tracker_id
|
|
from projects
|
|
left join projects_trackers pt on pt.project_id = projects.id").each { |row|
|
|
|
|
project_id, tracker_id = MigrateLegacy.row(row, [:int, :int])
|
|
|
|
trackers[project_id] ||= {}
|
|
trackers[project_id][:story] = tracker_id if Story.trackers.include?(tracker_id)
|
|
trackers[project_id][:task] = tracker_id if Task.tracker == tracker_id
|
|
}
|
|
|
|
# close existing transactions and turn on autocommit
|
|
ActiveRecord::Base.connection.commit_db_transaction unless adapter.include?('sqlite')
|
|
|
|
say_with_time "Migrating Backlogs data..." do
|
|
bottom = 0
|
|
execute("select coalesce(max(position), 0) from items").each { |row|
|
|
bottom = row[0].to_i
|
|
}
|
|
bottom += 1
|
|
|
|
connection = ActiveRecord::Base.connection
|
|
|
|
stories = execute "
|
|
select story.issue_id, story.points, versions.id, issues.project_id
|
|
from items story
|
|
join issues on issues.id = story.issue_id
|
|
left join items parent on parent.id = story.parent_id and story.parent_id <> 0
|
|
left join backlogs sprint on story.backlog_id = sprint.id and sprint.id <> 0
|
|
left join versions on versions.id = sprint.version_id and sprint.version_id <> 0
|
|
where parent.id is null
|
|
order by coalesce(story.position, #{bottom}) desc, story.created_at desc"
|
|
|
|
stories.each { |row|
|
|
id, points, sprint, project = MigrateLegacy.row(row, [:int, :int, :int, :int])
|
|
|
|
say "Updating story #{id}"
|
|
story = Story.find(id)
|
|
|
|
if ! Story.trackers.include?(story.tracker_id)
|
|
raise "Project #{project} does not have a story tracker configured" unless trackers[project][:story]
|
|
story.tracker_id = trackers[project][:story]
|
|
story.save!
|
|
end
|
|
|
|
story.fixed_version_id = sprint
|
|
story.story_points = points
|
|
story.save!
|
|
|
|
# because we're inserting the stories last-first, this
|
|
# position gets shifted down 1 spot each time, yielding a
|
|
# neatly compacted position list
|
|
story.insert_at 1
|
|
}
|
|
|
|
tasks = execute "
|
|
select task.issue_id, versions.id, parent.issue_id, task_issue.project_id
|
|
from items task
|
|
join issues task_issue on task_issue.id = task.issue_id
|
|
join items parent on parent.id = task.parent_id and task.parent_id <> 0
|
|
join issues parent_issue on parent_issue.id = parent.issue_id
|
|
left join backlogs sprint on task.backlog_id = sprint.id and sprint.id <> 0
|
|
left join versions on versions.id = sprint.version_id and sprint.version_id <> 0
|
|
order by coalesce(task.position, #{bottom}), task.created_at"
|
|
|
|
tasks.each { |row|
|
|
id, sprint, parent_id, project = MigrateLegacy.row(row, [:int, :int, :int, :int])
|
|
|
|
say "Updating task #{id}"
|
|
|
|
task = Task.find(id)
|
|
|
|
if ! Task.tracker == task.tracker_id
|
|
raise "Project #{project} does not have a task tracker configured" unless trackers[project][:task]
|
|
task.tracker_id = trackers[project][:task]
|
|
task.save!
|
|
end
|
|
|
|
# because we're inserting the tasks first-last, adding it to
|
|
# the story will yield the correct order
|
|
task.fixed_version_id = sprint
|
|
task.parent_issue_id = parent_id
|
|
task.save!
|
|
}
|
|
|
|
res = execute "select version_id, start_date, is_closed from backlogs"
|
|
res.each { |row|
|
|
version, start_date, is_closed = MigrateLegacy.row(row, [:int, :string, :bool])
|
|
|
|
status = connection.quote(is_closed ? 'closed' : 'open')
|
|
version = connection.quote(version == 0 ? nil : version)
|
|
start_date = connection.quote(start_date)
|
|
|
|
execute "update versions set status = #{status}, sprint_start_date = #{start_date} where id = #{version}"
|
|
}
|
|
end
|
|
|
|
execute %{
|
|
insert into burndown_days (version_id, points_committed, points_accepted, created_at)
|
|
select version_id, scope, done, backlog_chart_data.created_at
|
|
from backlogs
|
|
join backlog_chart_data on backlogs.id = backlog_id
|
|
}
|
|
ActiveRecord::Base.connection.commit_db_transaction unless adapter.include?('sqlite')
|
|
|
|
drop_table :backlogs
|
|
drop_table :items
|
|
end
|
|
|
|
end
|
|
|
|
def self.down
|
|
#pass
|
|
end
|
|
end
|
|
|