Merge pull request #3880 from betagouv/batch-migrate-pjs

Ajout d'une tâche pour migrer les pièces justificatives de plusieurs démarches
This commit is contained in:
Paul Chavard 2019-05-28 11:29:53 +02:00 committed by GitHub
commit 01e6af47a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 151 additions and 27 deletions

View file

@ -9,9 +9,18 @@ class PieceJustificativeToChampPieceJointeMigrationService
storage_service.ensure_openstack_copy_possible!(PieceJustificativeUploader) storage_service.ensure_openstack_copy_possible!(PieceJustificativeUploader)
end end
def convert_procedure_pjs_to_champ_pjs(procedure) def procedures_with_pjs_in_range(ids_range)
procedures_with_pj = Procedure.unscope(where: :hidden_at).joins(:types_de_piece_justificative).distinct
procedures_with_pj.where(id: ids_range)
end
def number_of_champs_to_migrate(procedure)
(procedure.types_de_piece_justificative.count + 1) * procedure.dossiers.unscope(where: :hidden_at).count
end
def convert_procedure_pjs_to_champ_pjs(procedure, &progress)
types_de_champ_pj = PiecesJustificativesService.types_pj_as_types_de_champ(procedure) types_de_champ_pj = PiecesJustificativesService.types_pj_as_types_de_champ(procedure)
populate_champs_pjs!(procedure, types_de_champ_pj) populate_champs_pjs!(procedure, types_de_champ_pj, &progress)
# Only destroy the old types PJ once everything has been safely migrated to # Only destroy the old types PJ once everything has been safely migrated to
# champs PJs. Destroying the types PJ will cascade and destroy the PJs, # champs PJs. Destroying the types PJ will cascade and destroy the PJs,
@ -24,7 +33,7 @@ class PieceJustificativeToChampPieceJointeMigrationService
@storage_service ||= CarrierwaveActiveStorageMigrationService.new @storage_service ||= CarrierwaveActiveStorageMigrationService.new
end end
def populate_champs_pjs!(procedure, types_de_champ_pj) def populate_champs_pjs!(procedure, types_de_champ_pj, &progress)
procedure.types_de_champ += types_de_champ_pj procedure.types_de_champ += types_de_champ_pj
# Unscope to make sure all dossiers are migrated, even the soft-deleted ones # Unscope to make sure all dossiers are migrated, even the soft-deleted ones
@ -49,6 +58,8 @@ class PieceJustificativeToChampPieceJointeMigrationService
created_at: dossier.created_at created_at: dossier.created_at
) )
end end
yield if block_given?
end end
end end
rescue rescue

View file

@ -1,8 +0,0 @@
namespace :'2019_03_13_migrate_pjs_to_champs' do
task run: :environment do
procedure_id = ENV['PROCEDURE_ID']
service = PieceJustificativeToChampPieceJointeMigrationService.new
service.ensure_correct_storage_configuration!
service.convert_procedure_pjs_to_champ_pjs(Procedure.find(procedure_id))
end
end

View file

@ -0,0 +1,46 @@
require Rails.root.join("lib", "tasks", "task_helper")
namespace :pieces_justificatives do
task migrate_procedure_to_champs: :environment do
procedure_id = ENV['PROCEDURE_ID']
procedure = Procedure.find(procedure_id)
service = PieceJustificativeToChampPieceJointeMigrationService.new
service.ensure_correct_storage_configuration!
progress = ProgressReport.new(service.number_of_champs_to_migrate(procedure))
service.convert_procedure_pjs_to_champ_pjs(procedure) do
progress.inc
end
progress.finish
end
task migrate_procedures_range_to_champs: :environment do
if ENV['RANGE_START'].nil? || ENV['RANGE_END'].nil?
fail "RANGE_START and RANGE_END must be specified"
end
procedures_range = ENV['RANGE_START']..ENV['RANGE_END']
service = PieceJustificativeToChampPieceJointeMigrationService.new
service.ensure_correct_storage_configuration!
procedures_to_migrate = service.procedures_with_pjs_in_range(procedures_range)
total_number_of_champs_to_migrate = procedures_to_migrate
.map { |p| service.number_of_champs_to_migrate(p) }
.sum
progress = ProgressReport.new(total_number_of_champs_to_migrate)
procedures_to_migrate.find_each do |procedure|
rake_puts ''
rake_puts "Migrating procedure #{procedure.id}"
service.convert_procedure_pjs_to_champ_pjs(procedure) do
progress.inc
end
end
progress.finish
end
end

View file

@ -0,0 +1,51 @@
describe 'pieces_justificatives' do
describe 'migrate_procedure_to_champs' do
let(:rake_task) { Rake::Task['pieces_justificatives:migrate_procedure_to_champs'] }
let(:procedure) { create(:procedure, :with_two_type_de_piece_justificative) }
before do
ENV['PROCEDURE_ID'] = procedure.id.to_s
allow_any_instance_of(PieceJustificativeToChampPieceJointeMigrationService).to receive(:ensure_correct_storage_configuration!)
rake_task.invoke
end
after { rake_task.reenable }
it 'migrates the procedure' do
expect(procedure.reload.types_de_piece_justificative).to be_empty
end
end
describe 'migrate_procedures_range_to_champs' do
let(:rake_task) { Rake::Task['pieces_justificatives:migrate_procedures_range_to_champs'] }
let(:procedure_in_range_1) { create(:procedure, :with_two_type_de_piece_justificative) }
let(:procedure_in_range_2) { create(:procedure, :with_two_type_de_piece_justificative) }
let(:procedure_out_of_range) { create(:procedure, :with_two_type_de_piece_justificative) }
before do
procedure_in_range_1
procedure_in_range_2
procedure_out_of_range
ENV['RANGE_START'] = procedure_in_range_1.id.to_s
ENV['RANGE_END'] = procedure_in_range_2.id.to_s
allow_any_instance_of(PieceJustificativeToChampPieceJointeMigrationService).to receive(:ensure_correct_storage_configuration!)
rake_task.invoke
end
after { rake_task.reenable }
it 'migrates procedures in the ids range' do
expect(procedure_in_range_1.reload.types_de_piece_justificative).to be_empty
expect(procedure_in_range_2.reload.types_de_piece_justificative).to be_empty
end
it 'doesnt migrate procedures not in the range' do
expect(procedure_out_of_range.reload.types_de_piece_justificative).to be_present
end
end
end

View file

@ -9,16 +9,17 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
let(:procedure) { create(:procedure, types_de_piece_justificative: types_pj) } let(:procedure) { create(:procedure, types_de_piece_justificative: types_pj) }
let(:types_pj) { [create(:type_de_piece_justificative)] } let(:types_pj) { [create(:type_de_piece_justificative)] }
let!(:dossier) do let!(:dossier) { make_dossier }
create(
:dossier,
procedure: procedure,
pieces_justificatives: pjs
)
end
let(:pjs) { [] } let(:pjs) { [] }
def make_dossier(hidden: false)
create(:dossier,
procedure: procedure,
pieces_justificatives: pjs,
hidden_at: hidden ? Time.zone.now : nil)
end
def make_pjs def make_pjs
types_pj.map do |tpj| types_pj.map do |tpj|
create(:piece_justificative, :contrat, type_de_piece_justificative: tpj) create(:piece_justificative, :contrat, type_de_piece_justificative: tpj)
@ -31,6 +32,22 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
expect(storage_service).to receive(:make_attachment) expect(storage_service).to receive(:make_attachment)
end end
describe '.number_of_champs_to_migrate' do
let!(:other_dossier) { make_dossier }
it 'reports the numbers of champs to be migrated' do
expect(service.number_of_champs_to_migrate(procedure)).to eq(4)
end
context 'when the procedure has hidden dossiers' do
let!(:hidden_dossier) { make_dossier(hidden: true) }
it 'reports the numbers of champs including those of hidden dossiers' do
expect(service.number_of_champs_to_migrate(procedure)).to eq(6)
end
end
end
context 'when conversion succeeds' do context 'when conversion succeeds' do
context 'for the procedure' do context 'for the procedure' do
it 'types de champ are created for the "pièces jointes" header and for each PJ' do it 'types de champ are created for the "pièces jointes" header and for each PJ' do
@ -114,19 +131,26 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
.to change { dossier.pieces_justificatives.count } .to change { dossier.pieces_justificatives.count }
.to(0) .to(0)
end end
context 'when the procedure has several dossiers' do
let!(:other_dossier) { make_dossier }
it 'sends progress callback for each migrated champ' do
number_of_champs_to_migrate = service.number_of_champs_to_migrate(procedure)
progress_count = 0
service.convert_procedure_pjs_to_champ_pjs(procedure) do
progress_count += 1
end
expect(progress_count).to eq(number_of_champs_to_migrate)
end
end
end end
context 'when the dossier is soft-deleted it still gets converted' do context 'when the dossier is soft-deleted it still gets converted' do
let(:pjs) { make_pjs } let(:pjs) { make_pjs }
let!(:dossier) { make_dossier(hidden: true) }
let!(:dossier) do
create(
:dossier,
procedure: procedure,
pieces_justificatives: pjs,
hidden_at: Time.zone.now
)
end
before { expect_storage_service_to_convert_object } before { expect_storage_service_to_convert_object }