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.
198 lines
6.4 KiB
198 lines
6.4 KiB
#-- encoding: UTF-8
|
|
#-- copyright
|
|
# OpenProject is a project management system.
|
|
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License version 3.
|
|
#
|
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
|
# Copyright (C) 2006-2017 Jean-Philippe Lang
|
|
# Copyright (C) 2010-2013 the ChiliProject Team
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# See docs/COPYRIGHT.rdoc for more details.
|
|
#++
|
|
|
|
require_relative 'migration_utils/timelines'
|
|
|
|
class MigrateTimelinesOptions < ActiveRecord::Migration[4.2]
|
|
include Migration::Utils
|
|
|
|
COLUMN = 'options'
|
|
|
|
OPTIONS = {
|
|
# already done in 20131015064141_migrate_timelines_end_date_property_in_options.rb
|
|
# 'end_date' => 'due_date',
|
|
'planning_element_types' => 'type',
|
|
'project_type' => 'type',
|
|
'project_status' => 'status',
|
|
}
|
|
|
|
def up
|
|
say_with_time_silently 'Check for historical comparisons' do
|
|
comparisons = timelines_with_historical_comparisons
|
|
|
|
unless comparisons.empty?
|
|
affected_ids = comparisons.map(&:id)
|
|
|
|
raise 'Error: Cannot migrate timelines options!'\
|
|
"\n\n"\
|
|
"Timelines exist that use historical comparison. This is not\n"\
|
|
"supported in future versions of timelines.\n\n"\
|
|
"The affected timelines ids are: #{affected_ids}\n\n"\
|
|
'You may use the rake task '\
|
|
"'migrations:timelines:remove_timelines_historical_comparison_from_options' "\
|
|
"to prepare the\n"\
|
|
'current schema for this migration.'\
|
|
"\n\n\n"
|
|
end
|
|
end
|
|
|
|
say_with_time_silently 'Update timelines options' do
|
|
update_column_values('timelines',
|
|
[COLUMN],
|
|
update_options(migrate_timelines_options(OPTIONS,
|
|
pe_id_map,
|
|
pe_type_id_map)),
|
|
nil)
|
|
end
|
|
|
|
if contains_none_element?
|
|
puts "\n\n"\
|
|
'ATTENTION:'\
|
|
"\n\n"\
|
|
"The timelines configurations reference the 'none' type. The\n"\
|
|
"'none' type is created in the production seed. Thus, AFTER the\n"\
|
|
"production seed, you need to run rake task\n"\
|
|
"'migrations:timelines:fix_missing_none_type_in_timelines_config'."\
|
|
"\n\n\n\n"
|
|
end
|
|
end
|
|
|
|
def down
|
|
say_with_time_silently 'Restore timelines options' do
|
|
update_column_values('timelines',
|
|
[COLUMN],
|
|
update_options(migrate_timelines_options(OPTIONS.invert,
|
|
pe_id_map.invert,
|
|
pe_type_id_map.invert)),
|
|
nil)
|
|
end
|
|
end
|
|
|
|
def contains_none_element?
|
|
@contains_none_element
|
|
end
|
|
|
|
def contains_none_element=(value)
|
|
@contains_none_element = value
|
|
end
|
|
|
|
private
|
|
|
|
PE_TYPE_KEY = 'planning_element_types'
|
|
PE_TIME_TYPE_KEY = 'planning_element_time_types'
|
|
VERTICAL_PE_TYPES = 'vertical_planning_elements'
|
|
|
|
def migrate_timelines_options(options, pe_id_map, pe_type_id_map)
|
|
calling_class = self
|
|
|
|
Proc.new do |timelines_opts|
|
|
timelines_opts = rename_columns timelines_opts, options
|
|
timelines_opts = migrate_planning_element_types timelines_opts, pe_type_id_map, calling_class
|
|
timelines_opts = migrate_planning_element_time_types timelines_opts, pe_type_id_map
|
|
timelines_opts = migrate_vertical_planning_elements timelines_opts, pe_id_map
|
|
|
|
timelines_opts
|
|
end
|
|
end
|
|
|
|
def migrate_planning_element_types(timelines_opts, pe_type_id_map, calling_class)
|
|
pe_types = []
|
|
|
|
pe_types = timelines_opts[PE_TYPE_KEY].delete_if(&:nil?) if timelines_opts.has_key? PE_TYPE_KEY
|
|
|
|
calling_class.contains_none_element = calling_class.contains_none_element? || pe_types.empty?
|
|
|
|
pe_types = pe_types.empty? ? new_ids_of_former_pes : pe_types.map { |p| pe_type_id_map[p.to_i].to_s }
|
|
|
|
timelines_opts[PE_TYPE_KEY] = pe_types
|
|
|
|
timelines_opts
|
|
end
|
|
|
|
def migrate_planning_element_time_types(timelines_opts, pe_type_id_map)
|
|
return timelines_opts unless timelines_opts.has_key? PE_TIME_TYPE_KEY
|
|
|
|
pe_time_types = timelines_opts[PE_TIME_TYPE_KEY]
|
|
|
|
pe_time_types.map! do |p| pe_type_id_map[p.to_i].to_s end
|
|
|
|
timelines_opts[PE_TIME_TYPE_KEY] = pe_time_types
|
|
|
|
timelines_opts
|
|
end
|
|
|
|
def migrate_vertical_planning_elements(timelines_opts, pe_id_map)
|
|
return timelines_opts unless timelines_opts.has_key? VERTICAL_PE_TYPES
|
|
|
|
vertical_pes = timelines_opts[VERTICAL_PE_TYPES].split(',')
|
|
.map(&:strip)
|
|
|
|
unless vertical_pes.empty?
|
|
mapped_pes = vertical_pes.map { |v| pe_id_map[v.to_i] }
|
|
.compact
|
|
|
|
timelines_opts[VERTICAL_PE_TYPES] = mapped_pes.join(',')
|
|
end
|
|
|
|
timelines_opts
|
|
end
|
|
|
|
def new_ids_of_former_pes
|
|
# don't forget '0' for 'none' type
|
|
@new_ids_of_former_pes ||= pe_types_ids_with_new_ids.each_with_object([0]) do |i, l|
|
|
l << i['new_id']
|
|
end
|
|
end
|
|
|
|
def pe_type_id_map
|
|
@pe_type_id_map ||= pe_types_ids_with_new_ids.each_with_object(-1 => 0) do |r, h|
|
|
h[r['id']] = r['new_id']
|
|
end
|
|
end
|
|
|
|
def pe_types_ids_with_new_ids
|
|
select_all <<-SQL
|
|
SELECT id, new_id
|
|
FROM legacy_planning_element_types
|
|
SQL
|
|
end
|
|
|
|
def pe_id_map
|
|
@pe_id_map ||= pe_ids_with_new_ids.each_with_object({}) do |r, h|
|
|
h[r['id']] = r['new_id']
|
|
end
|
|
end
|
|
|
|
def pe_ids_with_new_ids
|
|
select_all <<-SQL
|
|
SELECT id, new_id
|
|
FROM legacy_planning_elements
|
|
SQL
|
|
end
|
|
end
|
|
|