Skip to content

Commit

Permalink
Backfill remaining work_histories for choices
Browse files Browse the repository at this point in the history
There are still about 90k application_choices that need their
work_histories backfilled.

This commit attempts to do this. It dups the work histories from the
application_form and associates them with the application_choice.

We do the insert in pure SQL, to avoid the issues we had before. Where a
lot of audits that were not necessary were created for each work history
and all the application_choices, forms and candidates have been touched
due to ActiveRecord callbacks.

No audits or touches will be generated by inserting the records via SQL.
  • Loading branch information
CatalinVoineag committed Sep 20, 2024
1 parent a346eeb commit 04e687e
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module DataMigrations
class BackfillRemainingApplicationChoicesWorkExperiences
TIMESTAMP = 20240911144139
MANUAL_RUN = true

def change
time_now = Time.zone.now

choices_without_work_histories.each_slice(5000).with_index do |ids, index|
next_batch_time = time_now + index.minutes
BackfillWorkHistoriesWorker.perform_at(next_batch_time, ids)
end
end

def choices_without_work_histories
sql = <<-SQL
SELECT "application_choices".id FROM "application_choices" LEFT OUTER JOIN "application_experiences" "work_experiences" ON
"work_experiences"."experienceable_type" = 'ApplicationChoice' AND "work_experiences"."experienceable_id" = "application_choices"."id" AND
"work_experiences"."type" = 'ApplicationWorkExperience' WHERE "application_choices"."status" != 'unsubmitted' AND "work_experiences"."id" IS NULL
union
SELECT "application_choices".id FROM "application_choices" LEFT OUTER JOIN "application_experiences" "volunteering_experiences" ON
"volunteering_experiences"."experienceable_type" = 'ApplicationChoice' AND "volunteering_experiences"."experienceable_id" = "application_choices"."id" AND
"volunteering_experiences"."type" = 'ApplicationVolunteeringExperience' WHERE "application_choices"."status" != 'unsubmitted' AND
"volunteering_experiences"."id" IS NULL
union
SELECT "application_choices".id FROM "application_choices" LEFT OUTER JOIN "application_work_history_breaks" "work_history_breaks" ON
"work_history_breaks"."breakable_type" = 'ApplicationChoice' AND "work_history_breaks"."breakable_id" = "application_choices"."id" WHERE
"application_choices"."status" != 'unsubmitted' AND "work_history_breaks"."id" IS NULL
SQL

ApplicationChoice.find_by_sql(sql).pluck(:id)
end
end
end
56 changes: 56 additions & 0 deletions app/workers/backfill_work_histories_worker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
class BackfillWorkHistoriesWorker
include Sidekiq::Worker

sidekiq_options queue: :low_priority

def perform(choice_ids)
application_experiences = []
work_history_breaks = []

ApplicationChoice.where(id: choice_ids).find_each(batch_size: 200) do |choice|
application_form = choice.application_form

if choice.work_experiences.blank? && application_form.application_work_experiences.any?
application_experiences += valid_attributes(
application_form.application_work_experiences.map(&:dup),
choice,
'experienceable',
)
end

if choice.volunteering_experiences.blank? && application_form.application_volunteering_experiences.any?
application_experiences += valid_attributes(
application_form.application_volunteering_experiences.map(&:dup),
choice,
'experienceable',
)
end

if choice.work_history_breaks.blank? && application_form.application_work_history_breaks.any?
work_history_breaks += valid_attributes(
application_form.application_work_history_breaks.map(&:dup),
choice,
'breakable',
)
end
end

ApplicationExperience.insert_all(application_experiences)
ApplicationWorkHistoryBreak.insert_all(work_history_breaks)
end

private

def valid_attributes(records, choice, polymorphic_column)
records.map do |record|
attributes = record.attributes
attributes["#{polymorphic_column}_type"] = 'ApplicationChoice'
attributes["#{polymorphic_column}_id"] = choice.id
attributes['created_at'] = Time.zone.now
attributes['updated_at'] = Time.zone.now
attributes.delete('id')

attributes
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require 'rails_helper'

RSpec.describe DataMigrations::BackfillRemainingApplicationChoicesWorkExperiences do
pending "add some examples to #{__FILE__}"
end
53 changes: 53 additions & 0 deletions spec/workers/backfill_work_histories_worker_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require 'rails_helper'

RSpec.describe BackfillWorkHistoriesWorker do
describe '#perform' do
before do
TestSuiteTimeMachine.unfreeze!
end

it 'dups the working experiences and histories from application_form to choice', :with_audited do
application_form = create(
:completed_application_form,
volunteering_experiences_count: 1,
full_work_history: true,
)
application_form_2 = create(
:completed_application_form,
volunteering_experiences_count: 1,
full_work_history: true,
)
choice = create(:application_choice, application_form:)
choice_with_data_migrated = create(
:application_choice,
application_form: application_form_2,
)
create(
:application_work_experience,
experienceable: choice_with_data_migrated,
)
create(
:application_volunteering_experience,
experienceable: choice_with_data_migrated,
)
create(
:application_work_history_break,
breakable: choice_with_data_migrated,
)
choice_ids = [choice.id, choice_with_data_migrated.id]

expect {
described_class.new.perform(choice_ids)
}.to change(choice.work_experiences, :count).by(2)
.and change(choice.volunteering_experiences, :count).by(1)
.and change(choice.work_history_breaks, :count).by(1)
.and not_change(choice_with_data_migrated.work_experiences, :count)
.and not_change(choice_with_data_migrated.volunteering_experiences, :count)
.and not_change(choice_with_data_migrated.work_history_breaks, :count)
.and not_change(choice.own_and_associated_audits, :count)
.and not_change(choice.reload, :updated_at)
.and not_change(application_form.reload, :updated_at)
.and not_change(application_form.candidate, :updated_at)
end
end
end

0 comments on commit 04e687e

Please sign in to comment.