Newer
Older
dmpopidor / db / migrate / 20161122152339_new_plan_template_structure.rb
class NewPlanTemplateStructure < ActiveRecord::Migration
  def up
    # new template tables
    create_table :templates do |t|
      t.string  :title
      t.text    :description
      t.boolean :published
      t.integer :organisation_id
      t.string  :locale
      t.boolean :is_default
      t.timestamps
      # new fields
      t.integer :version
      t.integer :visibility
      t.integer :customization_of
      t.integer :dmptemplate_id     # remove on next migration
    end

    create_table :new_phases do |t|
      t.string   :title
      t.text     :description
      t.integer  :number
      t.integer  :template_id
      t.timestamps
      t.string   :slug
      t.integer  :vid        # remove on next migration
      # new fields
      t.boolean :modifiable
    end

    create_table :new_sections do |t|
      t.string   :title
      t.text     :description
      t.integer  :number
      t.timestamps
      t.boolean  :published
      # new fields
      t.integer :new_phase_id
      t.boolean :modifiable
    end

    create_table :new_questions do |t|
      t.text     :text
      t.text     :default_value
      t.text     :guidance
      t.integer  :number
      t.integer  :new_section_id
      t.timestamps
      t.integer  :question_format_id
      t.boolean  :option_comment_display, default: true
      # new fields
      t.boolean  :modifiable
      t.integer  :question_id         # remove on next migration
    end

    create_join_table :new_questions, :themes do |t|
    end

    create_table :new_answers do |t|
      t.text     :text
      t.integer  :new_plan_id
      t.integer  :user_id
      t.integer  :new_question_id
      t.timestamps
    end

    create_table :question_options do |t|
      t.integer  :new_question_id
      t.integer  :option_id       # remove on next migration
      t.string   :text
      t.integer  :number
      t.boolean  :is_default
      t.timestamps
    end

    create_join_table :new_answers, :question_options do |t|
    end

    create_table :notes do |t|
      t.integer  :user_id
      t.text     :text
      t.boolean  :archived          # do we need this?
      t.integer  :new_answer_id
      t.integer  :archived_by       # do we need this?
      t.timestamps
    end

    create_table :new_suggested_answers do |t|
      t.integer  :new_question_id
      t.integer  :organisation_id
      t.text     :text
      t.boolean  :is_example
      t.timestamps
    end

    # new plans table
    create_table :new_plans do |t|
      t.integer  :project_id
      t.string   :title
      t.integer  :template_id
      t.timestamps
      t.string   :slug
      t.string   :grant_number
      t.string   :identifier
      t.text     :description
      t.string   :principal_investigator
      t.string   :principal_investigator_identifier
      t.string   :data_contact
      t.string   :funder_name
    end

    create_table :roles do |t|
      t.boolean :creator
      t.boolean :editor
      t.boolean :administrator
      t.integer :user_id
      t.integer :new_plan_id
      t.timestamps
    end

    # migrate all of the data from plans into templates (user facing)
    #   first migrate all "pure"(uncustomised) plans
    #     find template for plan
    #     find versions for plan
    #       find phases for template
    #         find sections for version
    #           find questions for section
    #             find question_options for question
    #             find answers for question
    #               find notes from question & link to answer
    #             find guidance_by_question for question
    #             find themes for question
    #     IF EXISTS Template SUCH THAT:
    #     dmptemplate.name = template.name,
    #       phase.title = new_phase.title,
    #         new_phase.version_id in versions.id
    #           SUCCESSFUL MATCH, copy over all data
    #   Then migrate all customised plans
    # migrate most current template into templates (org facing)
    proj_number = 0
    
    if table_exists?('projects') && table_exists?('templates') && table_exists?('answers') &&
              table_exists?('comments') && table_exists?('sections') && table_exists?('new_plan')
      # migrating uncustomised plans
      Template.transaction do
        Project.includes( { dmptemplate: [ { phases: [ { versions: [:sections] } ] } ] }, {plans: [:version ]}, :organisation).find_each(batch_size: 20) do |project|
          puts ""
          puts "beginning number #{proj_number}"
          proj_number +=1
          if project.dmptemplate.nil?               # one of the templates dosent exist
            next
          end
          new_plan = initNewPlan(project)           # copy data from project to NewPlan object
          plans = project.plans                     # select plans for project
          version_ids = []
          versions = []
          plans.each do |plan|                      # select version ids from plans list
            version_ids << plan.version.id
            versions << plan.version
          end
          dmptemplate = project.dmptemplate         # select template for project
          phases = dmptemplate.phases               # select phases for project
          temp_match = false                        # flag for if we found a matching template

          puts "checking for matching templates for #{dmptemplate.title} customised by #{project.organisation.name}" unless project.organisation.nil?
          puts "checking for matching templates for #{dmptemplate.title} uncustomised" unless project.organisation.present?
          possible_templates = project.organisation.nil? ?
            Template.includes(:new_phases).where(dmptemplate_id: dmptemplate.id, organisation_id: dmptemplate.organisation_id) :
            Template.includes(:new_phases).where(dmptemplate_id: dmptemplate.id, organisation_id: project.organisation_id)
          possible_templates.find_each do |t|  # for templates with same id
            # early cut for un-even number of phases
            new_phase_versions = t.new_phases.pluck(:vid)
            if new_phase_versions.sort == version_ids.sort
              temp_match = true                                 # flag that we found match
                                                        # we can point the new_plan to this template and init all data
              new_plan.template_id = t.id
              new_plan.save!
              puts "found a match: #{t.title} version #{t.version}"
              break
            end
          end


          # this section handles for customisations
          unless temp_match     # no matches found, init template & phase & sections & questions & themes & options
            puts "creating new template for #{dmptemplate.title}" unless project.organisation.present?
            puts "creating new template for #{dmptemplate.title} customised by #{project.organisation.name}" unless project.organisation.nil?
            modifiable = project.organisation.nil? || project.organisation_id == dmptemplate.organisation_id
            template = initTemplate(dmptemplate, modifiable, project.organisation_id)      # needs to select next version of temp based on old_temp_id
            # some differences between a customised and un-customised template
            # customised templates need a different organisation_id
            template.organisation_id = project.organisation_id unless project.organisation_id.nil?  # updated to not overwrite with nil
            # customised templates follow different version rules
            template.save!
            # since template was not a match, need to gen/copy all data below the template level
            versions.each do |version|
              new_phase = initNewPhase(version.phase, version, template, modifiable)
              new_phase.save!
              sections = []
              sections += version.sections.where("organisation_id = ? ", dmptemplate.organisation_id).pluck(:id)
              unless project.organisation_id.nil?
                sections += Section.where(organisation_id: project.organisation_id, version_id: version.id).pluck(:id)
              end
              Section.includes(questions: [:themes, :options, :suggested_answers]).where(id: sections).each do |section|
                new_section = initNewSection(section, new_phase, modifiable)
                new_section.save!
                section.questions.each do |question|
                  new_question = initNewQuestion(question, new_section, modifiable)
                  new_question.save!
                  question.themes.each do |theme|
                    new_question.themes << theme
                  end
                  question.options.each do |option|
                    question_option = initQuestionOption(option, new_question)
                    question_option.save!
                  end
                  question.suggested_answers.each do |suggested_answer|
                    new_suggested_answer = initNewSuggestedAnswers(suggested_answer, new_question)
                    new_suggested_answer.save!
                  end
                end
              end
            end
            new_plan.template_id = template.id
            new_plan.save!
          end

          # up to this point, we have either found a matching template and pointed the
          # new_plan obj at it, or we have generated a new:
          # template/phases/sections/questions/question_options/question_themes
          # now need to init answers, notes, answers_options
          #new_plan.template.new_phases.each do |new_phase|
          puts "transfering plan data"
          project.project_groups.each do |group|
            role = initRole(group, new_plan)
            role.save!
          end
          template = Template.includes(new_phases: {new_sections: :new_questions}).find(new_plan.template_id)
          template.new_phases.each do |new_phase|
            old_plan = project.plans.where(version_id: new_phase.vid).first
            puts "old plan id: #{old_plan.id}"
            puts "plan ids :#{plans.pluck(:id)}"
            if old_plan.id == 46
              puts "IT'S NOT WORKING RITE HERE!!!!!!!!!!!!!!!!!!!!!!! IT'S NOT WORKING RITE HERE!!!!!!!!!!!!!!!!!!!!!!! IT'S NOT WORKING RITE HERE!!!!!!!!!!!!!!!!!!!!!!!"
            end
            new_phase.new_sections.each do |new_section|
              new_section.new_questions.each do |new_question|
                # init new answer
                old_ans = Answer.where(question_id: new_question.question_id, plan_id: old_plan.id).order("created_at DESC").first
                # init comments on answer
                new_ans = nil
                comments = Comment.where(question_id: new_question.question_id, plan_id: old_plan.id)
                # unless there is no old answer, and no comments, create an answer
                unless old_ans.nil? && comments.length < 1
                  new_ans = initNewAnswer(old_ans, new_plan, new_question)
                  new_ans.save!
                end
                comments.find_each do |comment|
                  note = initNote(comment, new_ans)
                  note.save!
                end
                if new_ans.present? && new_ans.text.present? && new_ans.text.include?("test2")
                  puts "!!!!!!!!!!!!!!!!DEBUG MODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
                  puts "old answer: #{old_ans.text}"
                  puts "old answer: #{old_ans.id}"
                  puts "new answer: #{new_ans.text}"
                  puts "old plans: #{project.plans.pluck(:id)}"
                  puts "old_plan: #{old_plan.id}"
                  puts "old project: #{project.id}"
                  puts "question: #{new_question.question_id}"
                  puts "old user: #{old_ans.user.email}"
                end
              end
            end
          end
        end
      end

    end
    
    # indexes on join tables at the end
    change_table :new_answers_question_options do |t|
      t.index [:new_answer_id, :question_option_id], name: 'answer_question_option_index'
      t.index [:question_option_id, :new_answer_id], name: 'question_option_answer_index'
    end

    change_table :new_questions_themes do |t|
      t.index [:new_question_id, :theme_id], name: 'question_theme_index'
      t.index [:theme_id, :new_question_id], name: 'theme_question_index'
    end

  end

  def down
    drop_table :templates
    drop_table :new_phases
    drop_table :new_sections
    drop_table :new_questions
    drop_join_table :new_questions, :themes
    drop_table :new_answers
    drop_table :question_options
    drop_join_table :new_answers, :question_options
    drop_table :notes
    drop_table :new_suggested_answers
    drop_table :new_plans
    drop_table :roles
  end
end



def initTemplate(dmptemp, modifiable, organisation_id)
  if table_exists?('templates')
    template                  = Template.new
    template.title            = dmptemp.title
    template.description      = dmptemp.description
    template.published        = dmptemp.published
    template.organisation_id  = organisation_id.present? ? organisation_id : dmptemp.organisation_id
    template.locale           = dmptemp.locale
    template.is_default       = dmptemp.is_default
    template.created_at       = dmptemp.created_at
    template.updated_at       = dmptemp.updated_at
    template.visibility       = 0                   # dummy value for private
    template.customization_of = modifiable ? nil : dmptemp.id
    template.dmptemplate_id   = dmptemp.id
    # if no templates with the same dmptemplate_id and organisation_id exist
    #   0
    # otherwise
    #   take the maximum version from templates with the same dmptemplate_id and organisation_id and add 1
    template.version          = Template.where(dmptemplate_id: dmptemp.id, organisation_id: template.organisation_id).blank? ?
      0 : Template.where(dmptemplate_id: dmptemp.id, organisation_id: template.organisation_id).pluck(:version).max + 1
    puts "NEW TEMPLATE: \n  title: #{template.title} \n  version: #{template.version} \n  others_present? #{Template.where(dmptemplate_id: dmptemp.id).count}"
    return template
    
  else
    return nil
  end
end

def initNewPhase(phase, version, temp, modifiable)
  new_phase                 = NewPhase.new
  new_phase.title           = phase.title
  new_phase.description     = phase.description
  new_phase.number          = phase.number
  new_phase.template_id     = temp.id
  new_phase.created_at      = phase.created_at
  new_phase.updated_at      = phase.updated_at
  new_phase.slug            = phase.slug
  new_phase.vid             = version.id
  new_phase.modifiable      = modifiable
  return new_phase
end

def initNewSection(section, new_phase, modifiable)
  new_section               = NewSection.new
  new_section.title         = section.title
  new_section.description   = section.description
  new_section.number        = section.number
  new_section.published     = section.published
  new_section.new_phase_id  = new_phase.id
  new_section.modifiable    = modifiable
  new_section.created_at    = section.created_at
  new_section.updated_at    = section.updated_at
  return new_section
end

def initNewQuestion(question, new_section, modifiable)
  new_question                        = NewQuestion.new
  new_question.text                   = question.text
  new_question.default_value          = question.default_value
  new_question.guidance               = question.guidance.nil? ? "" : question.guidance
  if table_exists?('guidances')
    Guidance.where(question_id: question.id).each do |guidance|
      new_question.guidance             += guidance.text
    end
  end
  new_question.number                 = question.number
  new_question.new_section_id         = new_section.id
  new_question.question_format_id     = question.question_format_id
  new_question.option_comment_display = question.option_comment_display
  new_question.modifiable             = modifiable
  new_question.question_id            = question.id
  new_question.updated_at             = question.updated_at
  new_question.created_at             = question.created_at
  return new_question
end

def initNewAnswer(answer, new_plan, new_question)
  new_answer                  = NewAnswer.new
  unless answer.nil?
    new_answer.text             = answer.text
    new_answer.new_plan_id      = new_plan.id
    new_answer.new_question_id  = new_question.id
    new_answer.user_id          = answer.user_id
    new_answer.created_at       = answer.created_at
    new_answer.updated_at       = answer.updated_at
    # not sure if these get saved properly as new_answer has no id yet
    if table_exists?('question_options')
      answer.options.each do |option|
        new_answer.question_options << QuestionOption.find_by(option_id: option.id)
      end
    end
  end
  return new_answer
end

def initQuestionOption(option, new_question)
  if table_exists?('question_options')
    question_option                 = QuestionOption.new
    question_option.new_question_id = new_question.id
    question_option.option_id       = option.id
    question_option.text            = option.text
    question_option.number          = option.number
    question_option.is_default      = option.is_default
    question_option.created_at      = option.created_at
    question_option.updated_at      = option.updated_at
    return question_option
  else
    return nil
  end
end

def initNote(comment, new_answer)
  if table_exists?('notes')
    note                  = Note.new
    note.user_id          = comment.user_id
    note.text             = comment.text
    note.archived         = comment.archived
    note.archived_by      = comment.archived_by
    note.new_answer_id    = new_answer.id
    note.created_at       = comment.created_at
    note.updated_at       = comment.updated_at
    return note
  else
    return nil
  end
end

def initNewPlan(project)
  new_plan              = NewPlan.new
  new_plan.project_id   = project.id
  new_plan.title        = project.title
  new_plan.slug         = project.slug
  new_plan.grant_number = project.grant_number
  new_plan.identifier   = project.identifier
  new_plan.description  = project.description
  new_plan.principal_investigator             = project.principal_investigator
  new_plan.principal_investigator_identifier  = project.principal_investigator_identifier
  new_plan.data_contact = project.data_contact
  new_plan.funder_name  = project.funder_name
  new_plan.created_at   = project.created_at
  new_plan.updated_at   = project.updated_at
  return new_plan
end

def initNewSuggestedAnswers(suggested_answer, new_question)
  new_suggested_answer                  = NewSuggestedAnswer.new
  new_suggested_answer.text             = suggested_answer.text
  new_suggested_answer.organisation_id  = suggested_answer.organisation_id
  new_suggested_answer.new_question_id  = new_question.id
  new_suggested_answer.is_example       = suggested_answer.is_example
  new_suggested_answer.created_at       = suggested_answer.created_at
  new_suggested_answer.updated_at       = suggested_answer.updated_at
  return new_suggested_answer
end

def initRole(project_group, new_plan)
  if table_exists?('roles')
    role                = Role.new
    role.creator        = project_group.project_creator
    role.administrator  = project_group.project_administrator
    role.editor         = project_group.project_editor
    role.created_at     = project_group.created_at
    role.updated_at     = project_group.updated_at
    role.user_id        = project_group.user_id
    role.new_plan_id    = new_plan.id
    return role
  else
    return nil
  end
end